文章

滴水-day40-ESP寻址-定位回调函数

ESP寻址

函数的调用总会伴随着传递参数,A函数调用B函数给B函数传递了两个INT参数,那么在CALL B函数的时候,B函数内部一定会有堆栈提升的操作给这个函数使用,通常情况下都是使用的PUSH EBP的方式进行堆栈的提升,但还有一种方式比PUSH EBP更方便快捷,就是ESP提升堆栈。

我们都知道CALL 指令其实就相当于一个PUSH MOV 的结合体,当CALL指令执行的时候会往堆栈中压入一个数据,该数据是当前CALL指令的下一条指令的地址,俗称函数返回地址。

例如说A函数调用B函数传递两个参数,且AB函数都是_stdcall调用约定,且CALL下一条指令地址为:0x12345678,那么就会有以下操作

PUSH 2 PUSH 1 CALL B

此时堆栈如下

0x12345680 0x12345678

0x1234567C 0x00000001

0x12345678 0x00000002

0x12345680就是目前的栈顶,当CALL进入函数的时候堆栈就是这个样子,那么在函数内部肯定会有一个提升堆栈的操作,这里使用ESP。

SUB ESP 0x40

那么此时堆栈如下

0x12345640 0x00000000

……………………………………

0x12345680 0x12345678

0x1234567C 0x00000001

0x12345678 0x00000002

此时新的栈顶就是0x12345640,而原来的ESP成了这个函数的栈底。

此时通过ESP+40可以取到函数的返回地址,ESP+44可以取到第一个参数,ESP+48可以取到第二个参数。

当然ESP寻址也是有缺点的,就是在函数内部如果还有PUSH操作的话那么堆栈就会提升,在想通过ESP取值的时候也要跟着提升,例如在函数内部PUSH EAX,此时栈顶就是0x1234563C

那么想取到函数的返回地址就是ESP+0x44.

回调函数

练习

WIN32入口函数识别

1.WIN32入口函数有4个参数,且该函数是_stdcall调用约定,那么在外层肯定会有4个传参的操作。

0078119A > /E9 211A0000     jmp DiShui_A.wWinMainCRTStartupmodeure_nopopacheledptr
00782BC3    E8 68FCFFFF     call DiShui_A.__scrt_common_mainrt_uninitialize_criticaldefaultnmentnt
00782838    E8 13000000     call DiShui_A.__scrt_common_main_sehermination_complete_defaultnmentnt
007829A2    E8 79010000                    call DiShui_A.invoke_mainain_policy::set_app_typeledptr

00782B39    8B4D FC                        mov ecx,dword ptr ss:[ebp-0x4]
00782B3C    51                             push ecx
00782B3D    8B55 F8                        mov edx,dword ptr ss:[ebp-0x8]
00782B40    52                             push edx
00782B41    6A 00                          push 0x0
00782B43    68 00007700                    push DiShui_A.00770000
00782B48    E8 8AE5FFFF                    call DiShui_A.007810D7

EBP-4获取到局部变量的数据存放到ecx中。
ECX压入栈中,观察寄存器ECX发现其值是0xA,就是WinMain最后一个参数的值。
EBP-8获取到局部变量的数据存放到EDX中。
EDX压入栈中,这个是WinMain函数的第三个参数,是命令行参数的地址。
PUSH 0,是WinMain函数的第二个函数,此参数始终为NULL。
PUSH 0x770000,此程序的ImageBase,如果在数据窗口跟随会发现,就是这个程序在内存中的状态。

image.png

结合以上分析,call DiShui_A.007810D7就是要找的WinMain函数了

007810D7   /E9 640E0000                    jmp DiShui_A.wWinMainode_Defaultin_usee_modetfail_fptr

窗口回调函数定位

00592161    8B45 08             mov eax,dword ptr ss:[ebp+0x8]                              ; DiShui_A.00580000
00592164    50                  push eax                                                    ; DiShui_A.00580000
00592165    E8 17F0FFFF         call DiShui_A.00591181

ebp+0x8,取WinMain第一个参数存放到eax中,push eax后call了一个地址,在我的程序中我是把窗口注册放到了一个函数中,所以我要传递一个此程序的句柄。

00591181就是我地自定义的函数

00591181   /E9 BA090000         jmp DiShui_A.MyRegisterClassyfailurere_os_handled_fptr

…………………………………………
00591BF8    8D45 C8             lea eax,dword ptr ss:[ebp-0x38]
00591BFB    50                  push eax
00591BFC    FF15 FCC05900       call dword ptr ds:[<&USER32.RegisterClassExW>]              ; user32.RegisterClassExW

当给WNDCLASSEXW结构体的成员赋值完毕之后,会调用RegisterClassExW函数进行窗口的注册。

此函数需要一个WNDCLASSEXW结构体参数。eax是该结构体的首地址

那么既然知道了WNDCLASSEXW的首地址,就可以直接定位到窗口回调函数了

WNDCLASSEXW结构的第三个参数就是处理窗口消息的函数地址。

直接在堆栈中取到eax+0x8地址保存的数据就是WndProc函数的地址了

0059110E   /E9 8D0B0000         jmp DiShui_A.WndProcllPathFromFilePathitialize_criticaldefa>

麻了,在DeBug版本中我找不到WndProc具体的回调函数在哪里

消息具体事件函数定位

但是在Release中可以轻松定位到具体消息的事件函数

00E910A7  |.  50            push    eax                                                             ; /pWndClassEx = 00BEF94C
00E910A8  |.  FF15 6430E900 call    near dword ptr ds:[<&USER32.RegisterClassExW>]                  ; \\RegisterClassEx

堆栈跟踪eax的数据,取到第三个参数就是WndProc函数的地址

00BEF954   00E911A0  DiShui_A.WndProcfastfailstartup_lockializexception_default

跟踪这个地址

00E911B9 >|.  8B75 0C       mov     esi, dword ptr ss:[ebp+0xC]

在这里下一个断点运行程序会发现程序不断的在暂停,这是应为ebp+0xc取的是WndProc函数的第二个参数,该参数是消息类型,所以会不断的停在这里。

右击断点设置条件:[ebp+c] == WM_LBUTTONDOWN

当ebp+c的值等于WM_LBUTTONDOWN也就是201的时候再暂停,这个是鼠标左键的消息。

ReverseTraining_1.exe

三个字符是AFg

00401120   .  8B4424 30     mov     eax, dword ptr ss:[esp+0x30]                                ;  Case 100 (WM_KEYDOWN) of switch 004010F7
00401124   .  83F8 41       cmp     eax, 0x41                                                   ;  Switch (cases 41..67)
00401127   .  74 49         je      short ReverseT.00401172
00401129   .  83F8 46       cmp     eax, 0x46
0040112C   .  74 33         je      short ReverseT.00401161
0040112E   .  83F8 67       cmp     eax, 0x67
00401131   .  74 1D         je      short ReverseT.00401150

License:  CC BY 4.0