是否可以在 Ollydbg 中捕获进程的标准输出?

逆向工程 ollydbg
2021-07-09 10:26:52

我正在调试由我编写的虚拟应用程序启动的进程外 COM 服务器(一个 EXE)。我在创建实例时附加调试器,并让虚拟应用程序调用服务器。

在调试过程中,我可以看到它通过调用 sprintf 进行了大量日志记录。我想捕获这个输出。有没有办法用 OllyDbg 2.01 做到这一点?

3个回答

sprintf是通过取代一个CRT函数sprintf_s既采取destination一个format stringvarargs 没有的参数可以从范围one to several取决于格式字符串。

这两个函数的代码通常都嵌入在二进制文件(静态)中,需要定位,并且需要在记录将被 sprintf'馈送到目标缓冲区的输出之前完成准备步骤;

Ollydbg 1.10

下面的输出显示了两个 sprintf 版本的开始(注意 src 可用于 vc 编译的 exe 循环注释列以使 src 可见)

sprintf 
00401147 >/$  8BFF          MOV     EDI, EDI         ; { sprintf.c:99. 

00401120 >/$  55            PUSH    EBP                              
; __DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_1_ARGLIST(int, sprintf_s, vsprintf_s,
 _Deref_post_z_ char, _Dest, _In_z_ _Printf_format_string_ const char *, 
_Format) stdio.h:323. 

选择函数的开始点右键分析 -> 假设参数 -> 选择 Sformat(ptr,format,.....) apply

输出 post 假设参数操作循环回到来自 src 的评论

00401147 >/$  8BFF          MOV     EDI, EDI     ;  Decoded as <Sformat>

00401120 >/$  55            PUSH    EBP          ;  Decoded as <Sformat>

现在您只需要一个不间断的条件断点来记录所有 sprintf 输出

select the start of function and click shift+f4 

为两个版本设置单选按钮如下

pause radio button                     never 
log expression radio button            never 
log function arguments radio button    always 

现在只需按 f9,您就会将所有 sprintf 参数记录到日志窗口或您指定的文件(在日志窗口中右键单击以设置文件日志记录)

Breakpoints
Address                            Module              Active 
00401120 sprintlo.sprintf_s<128>   sprintlo            Log "logging sprintf_s_arguments"
00401147 sprintlo.sprintf          sprintlo            Log "logging sprint arguments" 

这是一个示例输出

Log data
CALL to Assumed Sformat from sprintlo.0040104F
  ptr = 0013FEE0
  format = "%s    %03d %s %p"
  <%s> = "sprint logging number"
  <%03d> = 0
  <%s> = "logme"
  <%p> = sprintlo.00401105
CALL to Assumed Sformat from sprintlo.004010BF
  ptr = 0013FEE0
  format = "%s    %03d %s %p"
  <%s> = "sprint logging number"
  <%03d> = 0
  <%s> = "logme"
  <%p> = sprintlo.00401111
  ==================================================================================
CALL to Assumed Sformat from sprintlo.0040104F
  ptr = 0013FEE0
  format = "%s    %03d %s %p"
  <%s> = "sprint logging number"
  <%03d> = 4
  <%s> = "logme"
  <%p> = sprintlo.00401105
CALL to Assumed Sformat from sprintlo.004010BF
  ptr = 0013FEE0
  format = "%s    %03d %s %p"
  <%s> = "sprint logging number"
  <%03d> = 4
  <%s> = "logme"
  <%p> = sprintlo.00401111
Process terminated, exit code 0

用于演示的代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <intrin.h>
void insecuresprintf(int in) {
    char dummybuff[0x80];
    memset(dummybuff,0,sizeof(dummybuff));
    void* pvAddressOfReturnAddress = _AddressOfReturnAddress();    
    sprintf(
        dummybuff,
        "%s    %03d %s %p\0\0","sprint logging number" ,
        in,"logme",*((void**) pvAddressOfReturnAddress)
        );
    return;
}
void securesprintf(int in) {
    char dummybuff[0x80];
    memset(dummybuff,0,sizeof(dummybuff));
    void* pvAddressOfReturnAddress = _AddressOfReturnAddress();    
    sprintf_s(
        dummybuff,
        "%s    %03d %s %p\0\0","sprint logging number" ,
        in,"logme",*((void**) pvAddressOfReturnAddress)
        );
    return ;
}
int main (void) { 
    for (int i =0; i<5; i++)    {
        insecuresprintf(i);
        securesprintf(i);
    }
    return 0;
}

ollydbg 2.01

而不是分析假设参数你必须直接 shift + f4 并指定函数类型那里使用格式 2A (arg1,format,.....) 但函数中有一个错误,因为它导致结果字符串被截断

INT3 breakpoints
Address   Module    Status       Disassembly                              Comment
00401120 >sprintlog Cond         PUSH    EBP                              INT sprintlog.sprintf_s<128>(_Dest,_Format)
00401147 >sprintlog Cond         MOV     EDI, EDI                         INT sprintlog.sprintf(string,format)

ollydbg 2.01 的输出(注意 %03d 显示了正确的值,但 %p %s 输出不可见)

Log data
00401147  Call to sprintlog.sprintf from sprintlog.0040104F
        0013FEE0  Arg1 = 13FEE0
        004131AC  Format = "%s    %03d %s %p"
        00413194  <%s> =
        00000000  <%03d> =
        0041318C  <%s> =
        00401105  <%p> =
00401120  Call to sprintlog.sprintf_s<128> from sprintlog.004010BF
        0013FEE0  Arg1 = 13FEE0
        004131E0  Format = "%s    %03d %s %p"
        004131C8  <%s> =
        00000000  <%03d> =
        004131C0  <%s> =
        00401111  <%p> =
========================================================================================
00401147  Call to sprintlog.sprintf from sprintlog.0040104F
        0013FEE0  Arg1 = 13FEE0
        004131AC  Format = "%s    %03d %s %p"
        00413194  <%s> =
        00000004  <%03d> =
        0041318C  <%s> =
        00401105  <%p> =
00401120  Call to sprintlog.sprintf_s<128> from sprintlog.004010BF
        0013FEE0  Arg1 = 13FEE0
        004131E0  Format = "%s    %03d %s %p"
        004131C8  <%s> =
        00000004  <%03d> =
        004131C0  <%s> =
        00401111  <%p> =
      Process terminated, exit code 0

顺便说一句,windbg 非常简单

0:000> x *!sprintf*
7c925bc4 ntdll!sprintf = <no type information>
00401147 sprintlog!sprintf (void)
004011e7 sprintlog!sprintf_s (void)
00401120 sprintlog!sprintf_s<128> (char (*)[128], char *)
0:000> bp 00401120 "r $t1 = poi(@esp+4) ; gu; .printf \"%ma\\n\", @$t1 ; gc"
0:000> bp 00401147 "r $t1 = poi(@esp+4) ; gu; .printf \"%ma\\n\", @$t1 ; gc"
0:000> .bpcmds
bp0 0x00401120  "r $t1 = poi(@esp+4) ; gu; .printf \"%ma\\n\", @$t1 ; gc";
bp1 0x00401147  "r $t1 = poi(@esp+4) ; gu; .printf \"%ma\\n\", @$t1 ; gc";
0:000> g
ModLoad: 5cb70000 5cb96000   C:\WINDOWS\system32\ShimEng.dll
sprint logging number    000 logme 00401105
sprint logging number    000 logme 00401111
sprint logging number    001 logme 00401105
sprint logging number    001 logme 00401111
sprint logging number    002 logme 00401105
sprint logging number    002 logme 00401111
sprint logging number    003 logme 00401105
sprint logging number    003 logme 00401111
sprint logging number    004 logme 00401105
sprint logging number    004 logme 00401111

我可以想到这些选项:

1) 编辑 .exe 标志以将其标记为控制台进程。例如

EDITBIN /SUBSYSTEM:CONSOLE comserver.exe

2) 使用调试器AllocConsole()在运行时调用

3) 在日志函数上放置一个断点,并在断点的操作中记录字符串内容(不确定在 OllyDbg 中是否可行)。

sprintf 不会“记录”到标准输出。它打印到一个变量。

无论如何:您可以使用 OllyDbgScript 在 sprintf 上设置 BP,然后使用BPGOTOcommand 执行一些操作,例如转储格式化的变量并再次运行。