如何在没有控制台窗口的情况下查看 printf 调用的输出?

逆向工程 艾达 dll注入
2021-06-23 20:20:19

我正在使用 IDA 在一个旧的视频游戏中闲逛,并注意到有很多对该printf函数的调用

我可以在另一个函数,见dword_5CE914是包括各种启动参数(例如位掩码debugnofullscreen等)。您可能已经猜到了,0x4000000是表示debug存在开关的值尽管启用了此功能,但没有可见的调试输出。

作为软件编程的新手,我假设在游戏开发过程中,printf调用会输出到调试器/控制台窗口,而这些窗口已被禁用以进行生产。

我的问题是:既然没有调试器窗口,我该如何查看输出?

我目前的思考过程是:

  1. 创建一个包含将任意文本输出到 .txt 文件的函数的自定义 DLL
  2. 将 DLL 注入游戏地址空间
  3. 挂钩printf并将参数传递到我的函数中以保存输出

这听起来像是一种合理的方法,还是有人可以推荐更好的方法?

3个回答

您概述的方法似乎是合理的;如果我正在处理这个问题,我可能会做类似的事情。如果printf是通过函数指针调用的——比如说,它是从另一个 .DLL 导入的——你可以简单地用你的 DLL 中的 IAT 条目覆盖它的 IAT 条目。否则,我会printf直接挂钩,并将参数传递到vfprintf我的 DLL 中。

如果您将程序转换为控制台程序(例如使用 EDITBIN),您应该能够从控制台窗口运行它并查看它打印的所有内容。

好的,我想知道如何完整地包含 printf() 但在没有控制台的情况下运行
似乎如果使用 WinMain() 并使用 /subsystem:windows 编译可以实现
这里是一个示例代码

#include <stdio.h>
#include <windows.h>
int pringlob = 0;
int a = 9;
int b = 10;
int c = 33;
int WINAPI WinMain(_In_ HINSTANCE,_In_opt_ HINSTANCE,_In_ LPSTR, _In_ int)
{
    if ((pringlob & 0x400000) != 0)
    {
        printf("this is mytest\n");
        if ((pringlob & 0x400000) != 0)
        {
            printf("this is mytest 1 %d\n", a);
            if ((pringlob & 0x400000) != 0)
            {
                printf("this is mytest 2 %d\n", b);
                if ((pringlob & 0x400000) != 0)
                {
                    printf("this is mytest 3 %d\n", c);
                }
            }
        }
    }
    MessageBoxA(NULL,"NO CONSOLE TEST","NO CONSOLE USE ATTACHCONSOLE",MB_OK);
}

在 vs 2017 社区中编译这个

cl /Zi /W4 /analyze /Od /nologo /EHsc foo.cpp /link /release /subsystem:windows  user32.lib  

这在没有控制台的情况下运行,但保持 printf 完好无损,它打印到由 AttachConsole() 在二进制文件的 pid 上创建的控制台

在 x64dbg 中打开 foo.exe 运行到
WinMainCrtStartup 或 @$exentry aka PEHeader->AddressOFEntryPoint
这样所有 dll init 都完成了
open file->attach 并注意
堆栈中 foo.exe 的 pid使用 push qword
和 push pid
push return address这是 WinmainCRTStartup
使用 Ctrl+g 转到 kernelbase.AllocConsole
跳过该函数并返回到 WinMainCRTStartup
使用 pop qword in stack 弹出 pid

将全局设置为 0xffffffff
f9 以运行 exe
您将在新创建的控制台中获得所有 printfs,
请参阅附加的屏幕截图
在此处输入图片说明

编辑 :

igorsk 在他的回答中建议的是添加相同测试的更好选择

:\>ls -lg
total 2
-rw-r--r-- 1 197121  91 Apr  3 02:01 complink.bat
-rw-r--r-- 1 197121 595 Apr  2 23:45 fook.cpp

:\>complink.bat

:\>cl /Zi /W4 /Od /EHsc /nologo /analyze fook.cpp /link /release /subsystem:windows user32.lib
fook.cpp

:\>dumpbin /headers fook.exe | grep -i subsystem
            6.00 subsystem version
               2 subsystem (Windows GUI)

:\>editbin /subsystem:console fook.exe
Microsoft (R) COFF/PE Editor Version 14.16.27045.0
Copyright (C) Microsoft Corporation.  All rights reserved.


:\>dumpbin /headers fook.exe | grep -i subsystem
            6.00 subsystem version
               3 subsystem (Windows CUI)

:\>cdb -c "ed fook!pringlob 0xffffffff;g;q" fook.exe | awk "/Reading/,/quit/"
0:000> cdb: Reading initial command 'ed fook!pringlob 0xffffffff;g;q'
xxxxxxxxxx
this is mytest
this is mytest 1 9
this is mytest 2 10
this is mytest 3 33
quit: