原文:https://www.pediy.com/kssd/pediy09/pediy09-254.htm
第一阶段第二题我的笨方法+处理的文件(看雪金山2007逆向分析挑战赛)
by aker
16:29 2007-8-26
使用工具:winhex,stud_pe,w32asm,od
如果对pe文件格式一点都不知道的,可以先看看www.pediy.com的资料区的pe格式,我也是现学现卖的:P
另外对windows消息循环机制要有一定的了解。
开始下载后很快就合并成功并出现窗口了,就放下了,感觉应该很容易出来的。今天在论坛上发现提交的人较少,想是不是很难啊,而且我也上不了网了,所以自己动手做了下,发现确实挺烦得,但是我估计我的方法非常笨拙,也有可能是平时这方面作的少,就当是抛砖引玉吧;)希望看到大牛的手笔,特别是shoooo,ccfer等好快的牛,呵呵呵。我找导入表是就这样用眼睛看得,不知道是不是可以写程序判断之类。入口点是合并后用w32asm看得。另外如果文件要重新对齐和重建引入表,估计就更加麻烦了。大家有好方法都介绍下拉。
附件是我合并好并修改好的文件。
1.文件重建
先从其他文件剥下来一个pe文件头,命名为pehead.dat。方法随便,我用的是winhex从一个文件前面剥下一个4k的文件头。方法是在文件头ALT+1,在要选尾部ALT+2,然后再编辑里面另存为phead.dat.
然后按照下面的顺序。
文件大小
同样用winhex工具菜单的合并,合并文件,得到build.exe。代码:rav - name size
0000h - phead 1000h
1000h - text 6000h
7000h - rdata 1000h
8000h - data 3000h
打开stud_PE,修改每个区块的位置和大小。这个不要说了吧。
在text中找入口点,用w32asm打开刚刚重建的文件,发现有GetCommandLine的函数的起点1527h,在stud_pe中填入。
在rdata中找到import table,看看,刚好应该是这个段的起始位置应该7000h,所以就不用重建了。
import table,位置为7618h,大小为30,修改IMAGE_DIRECTORY_ENTRY_IMPORT ,基本完成重建。
点击看一下,说是非法可执行文件。郁闷,再看看,哈忘了文件大小,文件大小变了,修改为b000h,okay,保存。测试,出来了那个pediy的界面。
另外这个地方发现校验和好像不需要改,好像和资料上说的不一样,谁指点我一下。
还有问题就是如果我不知道到底哪个文件放导入表该怎么办呢?就是怎么判断哪个区段的文件是何种文件。
小小总结,文件添加区段后要修改区段值,文件大小,区段属性等
修改入口点的话自己要记得跳回来。
2.增加菜单
看到窗口类注册的时候:一大段代码,其中关键的有这几个:
发现注册的窗口类已经有了一个默认的菜单名字叫pediy.com,这样添加菜单就很简单了。代码:004011F5 |. BE 94804000 mov esi,bf1.00408094 ; ASCII "pediy.com"
........
00401248 |. 8D45 F4 lea eax,dword ptr ss:[ebp-C]
0040124B |. 8945 D0 mov dword ptr ss:[ebp-30],eax
0040124E |. 8D45 B0 lea eax,dword ptr ss:[ebp-50]
00401251 |. BF A0804000 mov edi,bf1.004080A0 ; ASCII "pediy.com"
00401256 |. 50 push eax ; /pWndClass
00401257 |. 897D D4 mov dword ptr ss:[ebp-2C],edi ; |
0040125A |. FF15 0C714000 call dword ptr ds:[<&USER32.RegisterClassA>] ; \RegisterClassA
手工写一个菜单资源,rc编译为.res文件,我不知道rsrc目录的格式是什么样子的,我就直接把这个和一个其他的代码一起编译了,然后剥出来,名为rsrc.dat,因为对齐是4k,所以文件大小有4k。
在stud_pe中添加一个新节rsrc,大小为1000h,填充为刚刚的文件,然后在stud_pe中手工修改数据目录中的resource table,指向B000h,这是对于我的文件而言的。文件大小我填写的90h。代码:pediy.com MENU
{
POPUP "&Help"
{
MENUITEM "&About", 1
}
}
好了,这样重新打开文件就发现菜单在上面了。现在的问题就是如何让程序响应我们的点击呢?
3.响应消息
在论坛混的大概都知道window窗体程序是消息驱动的。我们点击菜单,鼠标滑动等都会产生对应的windows消息,消息先存放到系统队列,然后由系统派送到对应的产生消息的窗体。(个人理解:P,不对请一定要告诉我)。写windows程序的时候很多消息都是系统自己处理的,我们只处理我们需要的消息,其他都有系统缺省处理。我们要处理的消息由wndproc指派。
好了,知道这个,我们就可以知道我们点击菜单上的about的时候,肯定会有对应的消息发送到窗体了,只不过窗体可能没有处理机制而已。菜单消息的大类是WM_COMMAND消息,然后小类可以是我们自己制定的消息号,我们在上面定义菜单的时候,把about的消息标志定义为1。我们现在要做的就是处理WM_COMMAND,也要处理里面的一号消息。
看看窗体消息处理的汇编代码,在下面好长一段,首先看看wndproc的原形:代码:MENUITEM "&About", 1
第二个参数为msg消息号,后面两个为对应消息的参数,第一个为窗体句柄。代码:LRESULT CALLBACK MainWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
所以代码中的[ebp+c]就是消息号。
这个代码可能是优化过吧,比较的方法和正常编写出来的不一样,比如正常的比较都是cmp eax,111之类的为对wm_command的处理,但是这个里面很奇怪,比较是用的减,然后判断标志位是否为0这样的,就是dec或者sub,然后je。所以不容易看得清楚。我手工加了几个消息注释,WM_DESTROY(2h),WM_SIZE(5h),WM_PAINT(15h).可以看到,没有WM_COMMAND,但是我们现在需要处理菜单呀,怎么办呢?
我考虑的是自己添加消息,但是上面的空间很满,前后都没有空闲地方了。代码:004012D5 /. 55 push ebp
004012D6 |. 8BEC mov ebp,esp
004012D8 |. 83EC 40 sub esp,40
004012DB |. 8B45 0C mov eax,dword ptr ss:[ebp+C]
004012DE |. 48 dec eax ; Switch (cases 2..F)
004012DF |. 48 dec eax
004012E0 |. 74 68 je short b1.0040134A ; WM_DESTROY
004012E2 |. 83E8 03 sub eax,3
004012E5 |. 74 4D je short b1.00401334 ; WM_SIZE
004012E7 |. 83E8 0A sub eax,0A
004012EA |. 74 14 je short b1.00401300 ; WM_PAINT
004012EC |. FF75 14 push dword ptr ss:[ebp+14] ; /lParam; Default case of switch 004012DE
004012EF |. FF75 10 push dword ptr ss:[ebp+10] ; |wParam
004012F2 |. FF75 0C push dword ptr ss:[ebp+C] ; |Message
004012F5 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
004012F8 |. FF15 F4704000 call dword ptr ds:[<&USER32.DefWindowProcA>] ; \DefWindowProcA
004012FE |. EB 54 jmp short b1.00401354
00401300 |> 8D45 C0 lea eax,dword ptr ss:[ebp-40] ; Case F of switch 004012DE
00401303 |. 50 push eax ; /pPaintstruct
00401304 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
00401307 |. FF15 F8704000 call dword ptr ds:[<&USER32.BeginPaint>] ; \BeginPaint
0040130D |. FF35 B0A94000 push dword ptr ds:[40A9B0]
00401313 |. FF35 B4A94000 push dword ptr ds:[40A9B4]
00401319 |. 50 push eax
0040131A |. FF75 08 push dword ptr ss:[ebp+8]
0040131D |. E8 36000000 call b1.00401358
00401322 |. 83C4 10 add esp,10
00401325 |. 8D45 C0 lea eax,dword ptr ss:[ebp-40]
00401328 |. 50 push eax ; /pPaintstruct
00401329 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
0040132C |. FF15 FC704000 call dword ptr ds:[<&USER32.EndPaint>] ; \EndPaint
00401332 |. EB 1E jmp short b1.00401352
00401334 |> 8B45 14 mov eax,dword ptr ss:[ebp+14] ; Case 5 of switch 004012DE
00401337 |. 0FB7C8 movzx ecx,ax
0040133A |. C1E8 10 shr eax,10
0040133D |. 890D B4A94000 mov dword ptr ds:[40A9B4],ecx
00401343 |. A3 B0A94000 mov dword ptr ds:[40A9B0],eax
00401348 |. EB 08 jmp short b1.00401352
0040134A |> 6A 00 push 0 ; /ExitCode = 0; Case 2 of switch 004012DE
0040134C |. FF15 00714000 call dword ptr ds:[<&USER32.PostQuitMessage>>; \PostQuitMessage
00401352 |> 33C0 xor eax,eax
00401354 |> C9 leave
00401355 \. C2 1000 retn 10
怎么办呢,真急人啊;)
我考虑的是在下面代码处跳出去,跳到一个空闲的地方,处理了WM_COMMAND,然后再回来,为什么选择这个地方呢?
因为这个地方消息号刚传进来,自己控制的话比较清楚。反正总要在DefWindowProcA前处理掉自己的消息才是;)
可以看到,我就是简单的远跳出去了,把原来的减和一个近程跳转覆盖掉了,但是我们在后面必须恢复该操作。代码:004012DE |. 48 dec eax ; Switch (cases 2..F)
004012DF |. 48 dec eax
004012E0 |. 74 68 je short b1.0040134A ; WM_DESTROY
004012E2 |. 83E8 03 sub eax,3
004012E5 |. 74 4D je short b1.00401334 ; WM_SIZE
004012E7 |. 83E8 0A sub eax,0A
004012EA |. 74 14 je short b1.00401300 ; WM_PAINT
//////////////////////////
// 我修改的代码如下
004012DE . /E9 91580000 jmp build.00406B74
004012E3 |90 nop
004012E4 |90 nop
004012E5 . /74 4D je short build.00401334 ///////此处就一样了
我找的空闲空间是整个代码段之后的空地,地址是00406B74。下面看看代码跳出去后恢复原来操作和跳回来代码。
看到,我们跳到地头后,首先比较是不是111h,就是看是不是WM_COMMAND,看winuser.h。
如果不是jnz short build.00406BBC就跳出去处理原来的操作了。build.00406BBC处可以看到就是原来的操作,dec eax。。。。
不过该处需要自己手工把跳回去的地址整理好,有点麻烦,不过我的笨方法就这样了;)
好了,上面才讲到如何跳转出去,再跳转回来。代码:00406B74 > \3D 11010000 cmp eax,111 ; Switch (cases 2..111)
00406B79 . 75 41 jnz short build.00406BBC
00406B7B . 8B45 10 mov eax,dword ptr ss:[ebp+10] ; Case 111 (WM_COMMAND) of switch 00406B74
00406B7E . 66:83F8 01 cmp ax,1
00406B82 . 75 41 jnz short build.00406BC5
//.........省一个关键代码...........过会说
00406BBC > 48 dec eax
00406BBD . 48 dec eax
00406BBE . 74 0A je short build.00406BCA
00406BC0 . 83E8 03 sub eax,3
00406BC3 . 74 0A je short build.00406BCF
00406BC5 >^ E9 1DA7FFFF jmp build.004012E7 ; Default case of switch 00406B74
00406BCA >^ E9 7BA7FFFF jmp build.0040134A ; Case 2 (WM_DESTROY) of switch 00406B74
00406BCF >^ E9 60A7FFFF jmp build.00401334 ; Case 5 (WM_SIZE) of switch 00406B74
下面就是要考虑怎么样跳一个消息框出来了。你肯定想,不就直接push参数,call messagebox摆,对了,你是对的。但是有两个问题要考虑,第一个就是数据的问题。压栈的时候你的数据该怎么搞,因为要符合要求才是啊,我就是手工在代码段后面添了
两段小数据,当然,需要加字符串的结束符0和回车换行符。代码:pediy
看雪论坛.珠海金山2007逆向分析挑战赛
http://www.pediy.com
call MessageBox的时候,你知道具体地址不?因为导入表中没有这个呢。
我采用的是LoadLibraryA,GetProcAddress做的,看代码吧,可能还清楚些。
下面是自己手工写的代码。
代码:00406B74 > \3D 11010000 cmp eax,111 ; Switch (cases 2..111)
00406B79 . 75 41 jnz short build.00406BBC
00406B7B . 8B45 10 mov eax,dword ptr ss:[ebp+10] ; Case 111 (WM_COMMAND) of switch 00406B74
00406B7E . 66:83F8 01 cmp ax,1
00406B82 . 75 41 jnz short build.00406BC5
///////////////// 下面是找函数的关键代码 /////////////////
00406B84 . 68 B8754000 push build.004075B8 ; /FileName = "user32.dll"
00406B89 . FF15 6C704000 call dword ptr ds:[<&KERNEL32.LoadLibraryA>] ; \LoadLibraryA
00406B8F . 8BF8 mov edi,eax
00406B91 . 8B35 A0704000 mov esi,dword ptr ds:[<&KERNEL32.GetProcAddr>; kernel32.GetProcAddress
00406B97 . 68 AC754000 push build.004075AC ; /ProcNameOrOrdinal = "MessageBoxA"
00406B9C . 57 push edi ; |hModule
00406B9D . FFD6 call esi ; \GetProcAddress
00406B9F . 8BF8 mov edi,eax
00406BA1 . 68 40000000 push 40
00406BA6 . 68 E06B4000 push build.00406BE0 ; ASCII "pediy"
00406BAB . 68 E76B4000 push build.00406BE7
00406BB0 . 68 00000000 push 0
00406BB5 . FFD7 call edi
///////////////// 上面是找函数的关键代码 /////////////////
00406BB7 .^ E9 96A7FFFF jmp build.00401352
00406BBC > 48 dec eax
00406BBD . 48 dec eax
00406BBE . 74 0A je short build.00406BCA
00406BC0 . 83E8 03 sub eax,3
00406BC3 . 74 0A je short build.00406BCF
00406BC5 >^ E9 1DA7FFFF jmp build.004012E7 ; Default case of switch 00406B74
00406BCA >^ E9 7BA7FFFF jmp build.0040134A ; Case 2 (WM_DESTROY) of switch 00406B74
00406BCF >^ E9 60A7FFFF jmp build.00401334 ; Case 5 (WM_SIZE) of switch 00406B74
总结:以前看过一点点pe的知识,也很少写windows窗体程序,真是什么什么到用时方恨少啊,发现自己根本就是不懂,呵呵呵呵。
另外估计我的方法是特别笨的方法,希望抛砖引玉,大家说说到底有什么速成的方法咯。
说老实话,我觉得这道题目可能考基础知识点多点,考耐心,比如合并文件自己可以用工具,但是要修改里面的数据的时候,感觉很麻烦,上一道题目考思维能力,解题能力。
觉得这里改比较好:
将原窗口消息处理函数里这段代码的 call ds:DefWindowProcA
.text:004012EC push [ebp+arg_C]
.text:004012EF push [ebp+arg_8]
.text:004012F2 push [ebp+arg_4]
.text:004012F5 push [ebp+arg_0]
.text:004012F8 call ds:DefWindowProcA
.text:004012FE jmp short locret_401354
改成call MyWndProc,函数代码如下:
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if ( message == WM_COMMAND && wParam == 0x68)
{
MessageBox(NULL,"看雪论坛.珠海金山2007逆向分析挑战赛\r\nhttp://www.pediy.com","pediy",0x40);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
还能多出一个空余字节 :)
第一阶段第二题我的笨方法+处理的文件(看雪金山2007逆向分析挑战赛)
by aker
16:29 2007-8-26
使用工具:winhex,stud_pe,w32asm,od
如果对pe文件格式一点都不知道的,可以先看看www.pediy.com的资料区的pe格式,我也是现学现卖的:P
另外对windows消息循环机制要有一定的了解。
开始下载后很快就合并成功并出现窗口了,就放下了,感觉应该很容易出来的。今天在论坛上发现提交的人较少,想是不是很难啊,而且我也上不了网了,所以自己动手做了下,发现确实挺烦得,但是我估计我的方法非常笨拙,也有可能是平时这方面作的少,就当是抛砖引玉吧;)希望看到大牛的手笔,特别是shoooo,ccfer等好快的牛,呵呵呵。我找导入表是就这样用眼睛看得,不知道是不是可以写程序判断之类。入口点是合并后用w32asm看得。另外如果文件要重新对齐和重建引入表,估计就更加麻烦了。大家有好方法都介绍下拉。
附件是我合并好并修改好的文件。
1.文件重建
先从其他文件剥下来一个pe文件头,命名为pehead.dat。方法随便,我用的是winhex从一个文件前面剥下一个4k的文件头。方法是在文件头ALT+1,在要选尾部ALT+2,然后再编辑里面另存为phead.dat.
然后按照下面的顺序。
文件大小
同样用winhex工具菜单的合并,合并文件,得到build.exe。代码:rav - name size
0000h - phead 1000h
1000h - text 6000h
7000h - rdata 1000h
8000h - data 3000h
打开stud_PE,修改每个区块的位置和大小。这个不要说了吧。
在text中找入口点,用w32asm打开刚刚重建的文件,发现有GetCommandLine的函数的起点1527h,在stud_pe中填入。
在rdata中找到import table,看看,刚好应该是这个段的起始位置应该7000h,所以就不用重建了。
import table,位置为7618h,大小为30,修改IMAGE_DIRECTORY_ENTRY_IMPORT ,基本完成重建。
点击看一下,说是非法可执行文件。郁闷,再看看,哈忘了文件大小,文件大小变了,修改为b000h,okay,保存。测试,出来了那个pediy的界面。
另外这个地方发现校验和好像不需要改,好像和资料上说的不一样,谁指点我一下。
还有问题就是如果我不知道到底哪个文件放导入表该怎么办呢?就是怎么判断哪个区段的文件是何种文件。
小小总结,文件添加区段后要修改区段值,文件大小,区段属性等
修改入口点的话自己要记得跳回来。
2.增加菜单
看到窗口类注册的时候:一大段代码,其中关键的有这几个:
发现注册的窗口类已经有了一个默认的菜单名字叫pediy.com,这样添加菜单就很简单了。代码:004011F5 |. BE 94804000 mov esi,bf1.00408094 ; ASCII "pediy.com"
........
00401248 |. 8D45 F4 lea eax,dword ptr ss:[ebp-C]
0040124B |. 8945 D0 mov dword ptr ss:[ebp-30],eax
0040124E |. 8D45 B0 lea eax,dword ptr ss:[ebp-50]
00401251 |. BF A0804000 mov edi,bf1.004080A0 ; ASCII "pediy.com"
00401256 |. 50 push eax ; /pWndClass
00401257 |. 897D D4 mov dword ptr ss:[ebp-2C],edi ; |
0040125A |. FF15 0C714000 call dword ptr ds:[<&USER32.RegisterClassA>] ; \RegisterClassA
手工写一个菜单资源,rc编译为.res文件,我不知道rsrc目录的格式是什么样子的,我就直接把这个和一个其他的代码一起编译了,然后剥出来,名为rsrc.dat,因为对齐是4k,所以文件大小有4k。
在stud_pe中添加一个新节rsrc,大小为1000h,填充为刚刚的文件,然后在stud_pe中手工修改数据目录中的resource table,指向B000h,这是对于我的文件而言的。文件大小我填写的90h。代码:pediy.com MENU
{
POPUP "&Help"
{
MENUITEM "&About", 1
}
}
好了,这样重新打开文件就发现菜单在上面了。现在的问题就是如何让程序响应我们的点击呢?
3.响应消息
在论坛混的大概都知道window窗体程序是消息驱动的。我们点击菜单,鼠标滑动等都会产生对应的windows消息,消息先存放到系统队列,然后由系统派送到对应的产生消息的窗体。(个人理解:P,不对请一定要告诉我)。写windows程序的时候很多消息都是系统自己处理的,我们只处理我们需要的消息,其他都有系统缺省处理。我们要处理的消息由wndproc指派。
好了,知道这个,我们就可以知道我们点击菜单上的about的时候,肯定会有对应的消息发送到窗体了,只不过窗体可能没有处理机制而已。菜单消息的大类是WM_COMMAND消息,然后小类可以是我们自己制定的消息号,我们在上面定义菜单的时候,把about的消息标志定义为1。我们现在要做的就是处理WM_COMMAND,也要处理里面的一号消息。
看看窗体消息处理的汇编代码,在下面好长一段,首先看看wndproc的原形:代码:MENUITEM "&About", 1
第二个参数为msg消息号,后面两个为对应消息的参数,第一个为窗体句柄。代码:LRESULT CALLBACK MainWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
所以代码中的[ebp+c]就是消息号。
这个代码可能是优化过吧,比较的方法和正常编写出来的不一样,比如正常的比较都是cmp eax,111之类的为对wm_command的处理,但是这个里面很奇怪,比较是用的减,然后判断标志位是否为0这样的,就是dec或者sub,然后je。所以不容易看得清楚。我手工加了几个消息注释,WM_DESTROY(2h),WM_SIZE(5h),WM_PAINT(15h).可以看到,没有WM_COMMAND,但是我们现在需要处理菜单呀,怎么办呢?
我考虑的是自己添加消息,但是上面的空间很满,前后都没有空闲地方了。代码:004012D5 /. 55 push ebp
004012D6 |. 8BEC mov ebp,esp
004012D8 |. 83EC 40 sub esp,40
004012DB |. 8B45 0C mov eax,dword ptr ss:[ebp+C]
004012DE |. 48 dec eax ; Switch (cases 2..F)
004012DF |. 48 dec eax
004012E0 |. 74 68 je short b1.0040134A ; WM_DESTROY
004012E2 |. 83E8 03 sub eax,3
004012E5 |. 74 4D je short b1.00401334 ; WM_SIZE
004012E7 |. 83E8 0A sub eax,0A
004012EA |. 74 14 je short b1.00401300 ; WM_PAINT
004012EC |. FF75 14 push dword ptr ss:[ebp+14] ; /lParam; Default case of switch 004012DE
004012EF |. FF75 10 push dword ptr ss:[ebp+10] ; |wParam
004012F2 |. FF75 0C push dword ptr ss:[ebp+C] ; |Message
004012F5 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
004012F8 |. FF15 F4704000 call dword ptr ds:[<&USER32.DefWindowProcA>] ; \DefWindowProcA
004012FE |. EB 54 jmp short b1.00401354
00401300 |> 8D45 C0 lea eax,dword ptr ss:[ebp-40] ; Case F of switch 004012DE
00401303 |. 50 push eax ; /pPaintstruct
00401304 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
00401307 |. FF15 F8704000 call dword ptr ds:[<&USER32.BeginPaint>] ; \BeginPaint
0040130D |. FF35 B0A94000 push dword ptr ds:[40A9B0]
00401313 |. FF35 B4A94000 push dword ptr ds:[40A9B4]
00401319 |. 50 push eax
0040131A |. FF75 08 push dword ptr ss:[ebp+8]
0040131D |. E8 36000000 call b1.00401358
00401322 |. 83C4 10 add esp,10
00401325 |. 8D45 C0 lea eax,dword ptr ss:[ebp-40]
00401328 |. 50 push eax ; /pPaintstruct
00401329 |. FF75 08 push dword ptr ss:[ebp+8] ; |hWnd
0040132C |. FF15 FC704000 call dword ptr ds:[<&USER32.EndPaint>] ; \EndPaint
00401332 |. EB 1E jmp short b1.00401352
00401334 |> 8B45 14 mov eax,dword ptr ss:[ebp+14] ; Case 5 of switch 004012DE
00401337 |. 0FB7C8 movzx ecx,ax
0040133A |. C1E8 10 shr eax,10
0040133D |. 890D B4A94000 mov dword ptr ds:[40A9B4],ecx
00401343 |. A3 B0A94000 mov dword ptr ds:[40A9B0],eax
00401348 |. EB 08 jmp short b1.00401352
0040134A |> 6A 00 push 0 ; /ExitCode = 0; Case 2 of switch 004012DE
0040134C |. FF15 00714000 call dword ptr ds:[<&USER32.PostQuitMessage>>; \PostQuitMessage
00401352 |> 33C0 xor eax,eax
00401354 |> C9 leave
00401355 \. C2 1000 retn 10
怎么办呢,真急人啊;)
我考虑的是在下面代码处跳出去,跳到一个空闲的地方,处理了WM_COMMAND,然后再回来,为什么选择这个地方呢?
因为这个地方消息号刚传进来,自己控制的话比较清楚。反正总要在DefWindowProcA前处理掉自己的消息才是;)
可以看到,我就是简单的远跳出去了,把原来的减和一个近程跳转覆盖掉了,但是我们在后面必须恢复该操作。代码:004012DE |. 48 dec eax ; Switch (cases 2..F)
004012DF |. 48 dec eax
004012E0 |. 74 68 je short b1.0040134A ; WM_DESTROY
004012E2 |. 83E8 03 sub eax,3
004012E5 |. 74 4D je short b1.00401334 ; WM_SIZE
004012E7 |. 83E8 0A sub eax,0A
004012EA |. 74 14 je short b1.00401300 ; WM_PAINT
//////////////////////////
// 我修改的代码如下
004012DE . /E9 91580000 jmp build.00406B74
004012E3 |90 nop
004012E4 |90 nop
004012E5 . /74 4D je short build.00401334 ///////此处就一样了
我找的空闲空间是整个代码段之后的空地,地址是00406B74。下面看看代码跳出去后恢复原来操作和跳回来代码。
看到,我们跳到地头后,首先比较是不是111h,就是看是不是WM_COMMAND,看winuser.h。
如果不是jnz short build.00406BBC就跳出去处理原来的操作了。build.00406BBC处可以看到就是原来的操作,dec eax。。。。
不过该处需要自己手工把跳回去的地址整理好,有点麻烦,不过我的笨方法就这样了;)
好了,上面才讲到如何跳转出去,再跳转回来。代码:00406B74 > \3D 11010000 cmp eax,111 ; Switch (cases 2..111)
00406B79 . 75 41 jnz short build.00406BBC
00406B7B . 8B45 10 mov eax,dword ptr ss:[ebp+10] ; Case 111 (WM_COMMAND) of switch 00406B74
00406B7E . 66:83F8 01 cmp ax,1
00406B82 . 75 41 jnz short build.00406BC5
//.........省一个关键代码...........过会说
00406BBC > 48 dec eax
00406BBD . 48 dec eax
00406BBE . 74 0A je short build.00406BCA
00406BC0 . 83E8 03 sub eax,3
00406BC3 . 74 0A je short build.00406BCF
00406BC5 >^ E9 1DA7FFFF jmp build.004012E7 ; Default case of switch 00406B74
00406BCA >^ E9 7BA7FFFF jmp build.0040134A ; Case 2 (WM_DESTROY) of switch 00406B74
00406BCF >^ E9 60A7FFFF jmp build.00401334 ; Case 5 (WM_SIZE) of switch 00406B74
下面就是要考虑怎么样跳一个消息框出来了。你肯定想,不就直接push参数,call messagebox摆,对了,你是对的。但是有两个问题要考虑,第一个就是数据的问题。压栈的时候你的数据该怎么搞,因为要符合要求才是啊,我就是手工在代码段后面添了
两段小数据,当然,需要加字符串的结束符0和回车换行符。代码:pediy
看雪论坛.珠海金山2007逆向分析挑战赛
http://www.pediy.com
call MessageBox的时候,你知道具体地址不?因为导入表中没有这个呢。
我采用的是LoadLibraryA,GetProcAddress做的,看代码吧,可能还清楚些。
下面是自己手工写的代码。
代码:00406B74 > \3D 11010000 cmp eax,111 ; Switch (cases 2..111)
00406B79 . 75 41 jnz short build.00406BBC
00406B7B . 8B45 10 mov eax,dword ptr ss:[ebp+10] ; Case 111 (WM_COMMAND) of switch 00406B74
00406B7E . 66:83F8 01 cmp ax,1
00406B82 . 75 41 jnz short build.00406BC5
///////////////// 下面是找函数的关键代码 /////////////////
00406B84 . 68 B8754000 push build.004075B8 ; /FileName = "user32.dll"
00406B89 . FF15 6C704000 call dword ptr ds:[<&KERNEL32.LoadLibraryA>] ; \LoadLibraryA
00406B8F . 8BF8 mov edi,eax
00406B91 . 8B35 A0704000 mov esi,dword ptr ds:[<&KERNEL32.GetProcAddr>; kernel32.GetProcAddress
00406B97 . 68 AC754000 push build.004075AC ; /ProcNameOrOrdinal = "MessageBoxA"
00406B9C . 57 push edi ; |hModule
00406B9D . FFD6 call esi ; \GetProcAddress
00406B9F . 8BF8 mov edi,eax
00406BA1 . 68 40000000 push 40
00406BA6 . 68 E06B4000 push build.00406BE0 ; ASCII "pediy"
00406BAB . 68 E76B4000 push build.00406BE7
00406BB0 . 68 00000000 push 0
00406BB5 . FFD7 call edi
///////////////// 上面是找函数的关键代码 /////////////////
00406BB7 .^ E9 96A7FFFF jmp build.00401352
00406BBC > 48 dec eax
00406BBD . 48 dec eax
00406BBE . 74 0A je short build.00406BCA
00406BC0 . 83E8 03 sub eax,3
00406BC3 . 74 0A je short build.00406BCF
00406BC5 >^ E9 1DA7FFFF jmp build.004012E7 ; Default case of switch 00406B74
00406BCA >^ E9 7BA7FFFF jmp build.0040134A ; Case 2 (WM_DESTROY) of switch 00406B74
00406BCF >^ E9 60A7FFFF jmp build.00401334 ; Case 5 (WM_SIZE) of switch 00406B74
总结:以前看过一点点pe的知识,也很少写windows窗体程序,真是什么什么到用时方恨少啊,发现自己根本就是不懂,呵呵呵呵。
另外估计我的方法是特别笨的方法,希望抛砖引玉,大家说说到底有什么速成的方法咯。
说老实话,我觉得这道题目可能考基础知识点多点,考耐心,比如合并文件自己可以用工具,但是要修改里面的数据的时候,感觉很麻烦,上一道题目考思维能力,解题能力。
一直知道自己对pe结构了解不足,做这个题就表现的很彻底,拼接exe的时间用了好几个小时,然后想把菜单加到资源里也没有成功
,可喜的是最后还是折腾出来了。下面是简要的分析过程:
从导入表入手,很明显存放在_rdata里面,在偏移0x061C的地方找到
00000618h: B4 76 00 00 00 00 00 00 00 00 00 00 AC 77 00 00 ; ............
对应的是KERNEL32.DLL,因此这个文件的位置偏移应该是0x7000。
根据各个文件的大小,可以知道其顺序是:
Header:0x1000
_text :0x6000
_rdata:0x7000
_data :0x8000
将他们连接起来,然后随便copy一个exe的头进去,修改如下:
Number of Sections = 3
Size of Code Section = 0x6000
Entry Point RVA = 0x1000(临时)
Section Alignment = 0x1000
File Alignment = 0x1000
Import Table Address = 0x7618(关键)
Import Table Size = 0x50
保存以后用OD载入,查找函数GetVersion(多半是VC的程序,就猜它),得到入口点,修改
Entry Point RVA = 0x1527(修正)
exe拼接好了,然后开始diy。在创建窗口以后跳到自己的空间
00401282 . FF15 10714000 CALL NEAR DWORD PTR [<&USER32.CreateW>; \CreateWindowExA
00401288 .- E9 73770000 JMP 00408A00
先用高级语言写了个样子:
hMenu := CreateMenu;
hPopup := CreateMenu;
AppendMenu(hPopup, MF_POPUP, 555, '&About');
AppendMenu(hMenu, MF_POPUP, hPopup, '&Help');
SetMenu(Handle, hMenu);
然后在OD里面直接修改如下:
00408A00 60 PUSHAD
00408A01 8BF8 MOV EDI, EAX
00408A03 68 F5894000 PUSH 004089F5 ; ASCII "user32.dll"
00408A08 FF15 6C704000 CALL NEAR DWORD PTR [<&KERNEL32.LoadL>; kernel32.LoadLibraryA
00408A0E A3 BC894000 MOV DWORD PTR [4089BC], EAX
00408A13 68 EA894000 PUSH 004089EA ; ASCII "CreateMenu"
00408A18 FF35 BC894000 PUSH DWORD PTR [4089BC]
00408A1E FF15 A0704000 CALL NEAR DWORD PTR [<&KERNEL32.GetPr>; kernel32.GetProcAddress
00408A24 A3 B0894000 MOV DWORD PTR [4089B0], EAX
00408A29 68 DE894000 PUSH 004089DE ; ASCII "AppendMenuA"
00408A2E FF35 BC894000 PUSH DWORD PTR [4089BC]
00408A34 FF15 A0704000 CALL NEAR DWORD PTR [<&KERNEL32.GetPr>; kernel32.GetProcAddress
00408A3A A3 B4894000 MOV DWORD PTR [4089B4], EAX
00408A3F 68 D6894000 PUSH 004089D6 ; ASCII "SetMenu"
00408A44 FF35 BC894000 PUSH DWORD PTR [4089BC]
00408A4A FF15 A0704000 CALL NEAR DWORD PTR [<&KERNEL32.GetPr>; kernel32.GetProcAddress
00408A50 A3 B8894000 MOV DWORD PTR [4089B8], EAX
00408A55 FF15 B0894000 CALL NEAR DWORD PTR [4089B0]
00408A5B 8BD8 MOV EBX, EAX
00408A5D FF15 B0894000 CALL NEAR DWORD PTR [4089B0]
00408A63 8BF0 MOV ESI, EAX
00408A65 68 CF894000 PUSH 004089CF ; ASCII "&About"
00408A6A 68 55050000 PUSH 555 ; 菜单的ID
00408A6F 6A 10 PUSH 10
00408A71 56 PUSH ESI
00408A72 FF15 B4894000 CALL NEAR DWORD PTR [4089B4]
00408A78 68 C8894000 PUSH 004089C8 ; ASCII "&Help"
00408A7D 56 PUSH ESI
00408A7E 6A 10 PUSH 10
00408A80 53 PUSH EBX
00408A81 FF15 B4894000 CALL NEAR DWORD PTR [4089B4]
00408A87 53 PUSH EBX
00408A88 57 PUSH EDI
00408A89 FF15 B8894000 CALL NEAR DWORD PTR [4089B8]
00408A8F 61 POPAD
00408A90 FF75 14 PUSH DWORD PTR [EBP+14]
00408A93 8BF8 MOV EDI, EAX ; 恢复破坏的代码
00408A95 - E9 F387FFFF JMP 0040128D ; 跳回原来空间
处理菜单的事件在消息循环里修改:
004012DB . 8B45 0C MOV EAX, DWORD PTR [EBP+C]
004012DE . 48 DEC EAX
004012DF . 48 DEC EAX
004012E0 . 74 68 JE SHORT 0040134A
004012E2 .- E9 19780000 JMP 00408B00
跳到自己的空间里,判断一下是不是WM_COMMAND消息,
00408B00 2D 0F010000 SUB EAX, 10F
00408B05 74 11 JE SHORT 00408B18
00408B07 05 0C010000 ADD EAX, 10C
00408B0C - 0F84 2288FFFF JE 00401334
00408B12 - E9 D087FFFF JMP 004012E7
00408B17 90 NOP
然后判断一下是不是菜单555触发的,如果是就弹出对话框,
00408B18 817D 10 55050000 CMP DWORD PTR [EBP+10], 555
00408B1F 75 18 JNZ SHORT 00408B39
00408B21 90 NOP
00408B22 68 40200100 PUSH 12040
00408B27 68 C08A4000 PUSH 00408AC0 ; ASCII "pediy"
00408B2C 68 C68A4000 PUSH 00408AC6
00408B31 E8 8ECEFFFF CALL 004059C4
00408B36 83C4 0C ADD ESP, 0C
00408B39 - E9 1488FFFF JMP 00401352
注意的是exe本身就提供了MessageBoxA的调用,可以直接用函数4059C4,参数是:
消息内容: string;
窗口标题: string;
窗口ICON: Integer;
调用完后配平堆栈,返回消息循环处,收工。
这题比上题难多了,要不是有看雪精华、exescope、lordpe、winhex、ue、计算器、delphi、vc、msdn、baidu、google等的大力协助,几乎无法完成。
觉得这里改比较好:
将原窗口消息处理函数里这段代码的 call ds:DefWindowProcA
.text:004012EC push [ebp+arg_C]
.text:004012EF push [ebp+arg_8]
.text:004012F2 push [ebp+arg_4]
.text:004012F5 push [ebp+arg_0]
.text:004012F8 call ds:DefWindowProcA
.text:004012FE jmp short locret_401354
改成call MyWndProc,函数代码如下:
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if ( message == WM_COMMAND && wParam == 0x68)
{
MessageBox(NULL,"看雪论坛.珠海金山2007逆向分析挑战赛\r\nhttp://www.pediy.com","pediy",0x40);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
还能多出一个空余字节 :)
偶也来凑下热闹,相同的东东不贴了,只贴上偶Patch的代码吧:
找一空地写入下面的代码:
00406B80 817C24 08 11010000 cmp dword ptr [esp+8], 111 ;WM_COMMAND
00406B88 ^ 0F85 47A7FFFF jnz 004012D5
00406B8E 66:837C24 0C 68 cmp word ptr [esp+C], 68 ;About命令ID,资源文件自定义时的值
00406B94 ^ 0F85 3BA7FFFF jnz 004012D5
00406B9A 60 pushad
00406B9B 6A 40 push 40
00406B9D 68 8C804000 push 0040808C ; ASCII "pediy"
00406BA2 68 C06B4000 push 00406BC0
00406BA7 E8 18EEFFFF call 004059C4 ;延时载入的MessageBox ,应该就是VC静态连接的AfxMessageBox吧
00406BAC 83C4 0C add esp, 0C
00406BAF 61 popad
00406BB0 33C0 xor eax, eax
00406BB2 C2 1000 retn 10
00406BC0 BF B4 D1 A9 C2 DB CC B3 2E D6 E9 BA A3 BD F0 .看雪论坛.珠海金
00406BCF C9 BD 32 30 30 37 C4 E6 CF F2 B7 D6 CE F6 CC F4 山2007逆向分析挑
00406BDF D5 BD C8 FC 0D 0A 68 74 74 70 3A 2F 2F 77 77 77 战赛..http://www
00406BEF 2E 70 65 64 69 79 2E 63 6F 6D 00 .pediy.com.
并改 RegisterClass 窗口消息处理入口地址:
0040121A |. C745 B4 D5124>mov dword ptr [ebp-4C], 004012D5 ;原消息处理入口地址
为:
0040121A C745 B4 806B4000 mov dword ptr [ebp-4C], 00406B80
DefWindowProc + AfxMessageBox + wc.lpszMenuName = 王道
偶也偶的贴出来献丑一下,写的很烂
代码:00406BB6 > \FF15 98B04000 CALL DWORD PTR DS:[<&user32.CreateMenu>]
00406BBC . 8BF0 MOV ESI,EAX
00406BBE . 68 AF6B4000 PUSH bithaha.00406BAF
00406BC3 . 68 00010000 PUSH 100
00406BC8 . 6A 00 PUSH 0
00406BCA . 50 PUSH EAX
00406BCB . FF15 9CB04000 CALL DWORD PTR DS:[<&user32.AppendMenuA>] 00406BD1 . FF15 98B04000 CALL DWORD PTR DS:[<&user32.CreateMenu>]
00406BD7 . 68 A96B4000 PUSH bithaha.00406BA9
00406BDC . 56 PUSH ESI
00406BDD . 8BF0 MOV ESI,EAX
00406BDF . 6A 10 PUSH 10
00406BE1 . 50 PUSH EAX
00406BE2 . FF15 9CB04000 CALL DWORD PTR DS:[<&user32.AppendMenuA>] 00406BE8 . 6A 00 PUSH 0
00406BEA . 53 PUSH EBX
00406BEB . 56 PUSH ESI
00406BEC . B8 00000080 MOV EAX,80000000
00406BF1 . 33F6 XOR ESI,ESI
00406BF3 .^ E9 79A6FFFF JMP bithaha.00401271//返回到createwindow前
00406BF8 > 3D 11010000 CMP EAX,111
00406BFD . 75 2D JNZ SHORT bithaha.00406C2C
00406BFF . 90 NOP
00406C00 . 90 NOP
00406C01 . 90 NOP
00406C02 . 90 NOP
00406C03 . 56 PUSH ESI
00406C04 . 8B75 10 MOV ESI,DWORD PTR SS:[EBP+10]
00406C07 . 81FE 00010000 CMP ESI,100
00406C0D . 5E POP ESI
00406C0E . 75 1C JNZ SHORT bithaha.00406C2C
00406C10 . 90 NOP
00406C11 . 90 NOP
00406C12 . 90 NOP
00406C13 . 6A 00 PUSH 0
00406C15 . 68 976B4000 PUSH bithaha.00406B97
00406C1A . 68 746B4000 PUSH bithaha.00406B74
00406C1F . 6A 00 PUSH 0
00406C21 . FF15 A0B04000 CALL DWORD PTR DS:[<&user32.MessageBoxA>]
00406C27 .^ E9 28A7FFFF JMP bithaha.00401354
00406C2C > 48 DEC EAX
00406C2D . 48 DEC EAX
00406C2E .^ 0F84 16A7FFFF JE bithaha.0040134A
00406C34 . 83E8 03 SUB EAX,3
00406C37 .^ E9 A9A6FFFF JMP bithaha.004012E5//返回到窗口回调函数
我的处理Pe文件代码,唉入口点通过Peid的特征码找的,可是就是那个菜单没做出来,差啊。。。
unit PEHandler;
interface
uses
Classes, Forms, sysutils, windows;
type
{PE导出表声明}
PImageExportDirectory = ^TImageExportDirectory;
TImageExportDirectory = packed record
Characteristics: DWORD;
TimeDateStamp: DWORD;
MajorVersion: WORD;
MinorVersion: WORD;
Name: DWORD;
Base: DWORD;
NumberOfFunctions: DWORD;
NumberOfNames: DWORD;
AddressOfFunctions: DWORD;
AddressOfNames: DWORD;
AddressOfNameOrdinals: DWORD;
end;
PImportByName=^TImportByName;
TImportByName =Packed record
ProcedureHint: word;
ProcedureName: array[0..1]of char;
end;
PImageImportDescriptor = ^TImageImportDescriptor;
TImageImportDescriptor = packed record
OriginalFirstThunk: DWord;
TimeDateStamp : DWord;
ForwarderChain : DWord;
DLLName : DWord;
FirstThunk : DWord;
end;
PImageThunkData=^TImageThunkData;
TImageThunkData = record
case integer of
1:( ForwarderString : DWord; );
2:( Function_ : DWord; );
3:( Ordinal : DWord; );
4:( AddressOfData : DWord; );
end;
PImageBaseRelocation=^TImageBaseRelocation;
TImageBaseRelocation=Packed Record
VirtualAddress: Dword;
SizeOfBlock: Dword;
TypeOffset: array[0..1] of Word;
end;
TPESection = record //自定义
ObjectName: string;
Address: PChar;
PhysicalSize: Integer;
// VirtualSize: Integer;
// Characteristics: Cardinal;
PointerToRawData: Integer;
end;
TNameOrID = (niName, niID);
TPEImport = record
NameOrID: TNameOrID;
Name: string;
ID: Integer;
// PAddress: PChar; {指向导入表函数用于执行此函数}
end;
TPEImports = record {记录一个Dll文件所调用的函数个数}
DLLName: string;
Entries: array of TPEImport; {函数数据}
end;
TPEExport = record
Name: string;
RelativeID: Integer;
ID: Integer;
Address: DWORD;{相对地址}
end;
TPEHandler = class(TThread)
private
{ Private declarations }
FPE, FTmp: TMemoryStream;
FSectionFiles: array[0..3] of string;
FSectionSizes: array[0..3] of integer;
FImportCount: integer;
FImportVA: integer;
FIAT: integer;
FIATSize: integer;
procedure HandlerImport;
function GetEntryPoint: integer;
procedure GetPEHead;
Procedure FillSection;
procedure ChangPEHead;
procedure ShowInfo(Info: string);
protected
procedure Execute; override;
end;
implementation
uses uMain;
{ TPEHandler }
procedure TPEHandler.ChangPEHead;
var
ImageDosHeader: TImageDosHeader;
ImageNtHeaders: TImageNtHeaders;
ImageSectionHeader: TImageSectionHeader;
Sections: array of TImageSectionHeader;
i, RVA, EP: integer;
begin
FPe.Seek(0, soBeginning);
FPE.ReadBuffer(ImageDosHeader, sizeof(ImageDosHeader));
FPE.Position := ImageDosHeader._lfanew;
FPE.ReadBuffer(ImageNtHeaders,sizeof(TImageNtHeaders));
//////////////////////////////////////////////////////////
//修改如下:
//File Header
ImageNtHeaders.FileHeader.NumberOfSections := 4;
//OptionalHeader
ImageNtHeaders.OptionalHeader.SizeOfCode := FSectionSizes[0];
ImageNtHeaders.OptionalHeader.SizeOfImage := FPE.Size;
//ImageNtHeaders.OptionalHeader.SizeOfImage := FPE.Size - $1000;
EP := GetEntryPoint;
ImageNtHeaders.OptionalHeader.AddressOfEntryPoint := EP;
ShowInfo('PE entry Point: 0x' + IntToHex(EP, 8));
//修改输入表
HandlerImport;
for i := 0 to 15 do
begin
ImageNtHeaders.OptionalHeader.DataDirectory[i].VirtualAddress := 0;
ImageNtHeaders.OptionalHeader.DataDirectory[i].Size := 0;
end;
//输入表
ImageNtHeaders.OptionalHeader.DataDirectory[1].VirtualAddress := FImportVA;
ImageNtHeaders.OptionalHeader.DataDirectory[1].Size := (FImportCount + 1) *
sizeof(TImageImportDescriptor);
//输入地址表
ImageNtHeaders.OptionalHeader.DataDirectory[12].VirtualAddress := FIAT;
ImageNtHeaders.OptionalHeader.DataDirectory[12].Size := $124;
//////////////////////////////////////////////////////////////////////////////
//写 改好的信息
FPE.Position := ImageDosHeader._lfanew;
FPE.WriteBuffer(ImageNtHeaders,sizeof(TImageNtHeaders));
SetLength(Sections, 4);
RVA := $1000;
for i := 0 to 3 do
begin
Move(PChar(FSectionFiles[i])^,Sections[i].Name, length(FSectionFiles[i]));
Sections[i].Misc.VirtualSize := FSectionSizes[i];
Sections[i].VirtualAddress := RVA;
RVA := RVA + FSectionSizes[i];
Sections[i].SizeOfRawData := FSectionSizes[i];
Sections[i].PointerToRawData := Sections[i].VirtualAddress;
Sections[i].PointerToRelocations := 0;
Sections[i].PointerToLinenumbers := 0;
Sections[i].NumberOfRelocations := 0;
Sections[i].NumberOfLinenumbers := 0;
case i of
0: Sections[i].Characteristics := $C0000060;
1: Sections[i].Characteristics := $C0000040;
2: Sections[i].Characteristics := $C00000C0;
3:
begin
Sections[i].Characteristics := $C00000C0;
//Sections[i].VirtualAddress := $18000;
end;
end;
FPE.WriteBuffer(Sections[i],sizeof(TImageSectionHeader));
end;
Fillchar(ImageSectionHeader, sizeof(TImageSectionHeader), 0);
FPE.WriteBuffer(ImageSectionHeader,sizeof(TImageSectionHeader));
ShowInfo('change Pe Head');
end;
procedure TPEHandler.Execute;
begin
FPE := TMemoryStream.Create;
try
FSectionFiles[0] := '_text';
FSectionFiles[1] := '_rdata';
FSectionFiles[2] := '_data';
FSectionFiles[3] := '_rsrc';
FTmp := TMemoryStream.Create;
try
GetPEHead;
FillSection;
ChangPEHead;
FPE.SaveToFile('New.exe');
finally
FTmp.Free;
end;
finally
FPE.Free;
end;
end;
procedure TPEHandler.FillSection;
var
rmod, i, wcount: integer;
begin
for i := 0 to 3 do
begin
FTmp.LoadFromFile(FSectionFiles[i]);
rMod := FTmp.Size mod $1000;
if rMod <> 0 then
begin
FTmp.Size := FTmp.Size + $1000 - rMod;
end;
FSectionSizes[i] := FTmp.Size;
FPe.Seek(0, soEnd);
wcount := FPe.Write(PChar(FTmp.Memory)^, FTmp.Size);
ShowInfo('Fill Section ' + FSectionFiles[i] + ', size: 0x'
+ IntToHex(WCount, 8));
end;
end;
function TPEHandler.GetEntryPoint: integer;
const
VCEP: array[0..5] of byte = ($55, $8B, $EC, $6A, $FF, $68);
var
rByte: byte;
i: integer;
bFind: Boolean;
begin
Result := 0;
FPe.Position := $1000;
while FPe.Position < $1000 + FSectionSizes[0] do
begin
bFind := true;
for i := 0 to 5 do
begin
FPe.ReadBuffer(rByte, 1);
if rByte <> VCEP[i] then
begin
FPe.Position := FPe.Position - i;
bFind := false;
Break;
end;
end;
if bfind then
begin
Result := FPe.Position - 6;
exit;
end;
end;
end;
procedure TPEHandler.GetPEHead;
begin
FTmp.LoadFromFile('1.exe');
FPe.Size := $1000;
FPe.Seek(0, soBeginning);
FTmp.Size := $1000;
FPe.LoadFromStream(FTmp);
ShowInfo('Get Pe Head from: ''1.exe''');
end;
procedure TPEHandler.HandlerImport;
const
LastRVA: integer = $0654;
var
iRVA, SectionVA, LastVA, DllVA: integer;
ImageImportDescriptor: TImageImportDescriptor;
DLLName: array[1..32] of char;
DllCount: integer;
begin
SectionVA := FSectionSizes[0] + $1000;
LastVA := SectionVA + LastRVA;
FImportCount := 0;
FIAT := $0FFFFFFF;
while True do
begin
FPE.Position := LastVA - (FImportCount + 1) * sizeof(TImageImportDescriptor);
FPe.ReadBuffer(ImageImportDescriptor, sizeof(TImageImportDescriptor));
DllVA := ImageImportDescriptor.DLLName;
if (Integer(DllVA) > sectionVA + FSectionSizes[1]) or
(Integer(DllVA) < sectionVA) then Break;
if FIAT > ImageImportDescriptor.FirstThunk then
FIAT := ImageImportDescriptor.FirstThunk;
ShowInfo('IAT: 0x' + IntToHex(ImageImportDescriptor.FirstThunk, 8));
FPE.Position := DLLVA;
FPE.ReadBuffer(DLLName, 32);
ShowInfo('Import Dll Name:' + DllName);
Inc(FImportCount);
end;
ShowInfo('Import Count:' + IntToStr(FImportCount));
FImportVA := SectionVA + LastRVA - FImportCount * sizeof(TImageImportDescriptor);
ShowInfo('Import VA: 0x' + IntToHex(FImportVA, 8));
end;
procedure TPEHandler.ShowInfo(Info: string);
begin
frmMain.lbInfo.Items.Add(Info);
Application.ProcessMessages;
end;
end.
这道题用Delphi我想可以这样做:
写一个DLL,在pediy的窗口创建后loadlibary进去。
在程序里创建TMainMenu类,然后添加Help和About,在用SetMenu(MainMenu1.Handle);来把菜单挂上去。
然后做一个消息钩子把窗口的消息钩住,拦截WM_COMMAND消息,再在Hook程序内MessageBox就可以了。
当时我是用直接写汇编来做的:
分别在CreateWindowEx和DispatchMessage那里作跳转,然后对应的两段代码如下:
代码:00406C00 > \FF15 10714000 CALL DWORD PTR DS:[<&USER32.CreateWindow>; \CreateWindowExA
00406C06 . 60 PUSHAD
00406C07 . A3 F0B04000 MOV DWORD PTR DS:[40B0F0],EAX
00406C0C . 68 B8754000 PUSH pediy02.004075B8 ; /FileName = "user32.dll"
00406C11 . FF15 6C704000 CALL DWORD PTR DS:[<&KERNEL32.LoadLibrar>; \LoadLibraryA
00406C17 . 8BF8 MOV EDI,EAX
00406C19 . 8B35 A0704000 MOV ESI,DWORD PTR DS:[<&KERNEL32.GetProc>; kernel32.GetProcAddress
00406C1F . 68 10B04000 PUSH pediy02.0040B010 ; /ProcNameOrOrdinal = "CreateMenu"
00406C24 . 57 PUSH EDI ; |hModule
00406C25 . FFD6 CALL ESI ; \GetProcAddress
00406C27 . A3 80B04000 MOV DWORD PTR DS:[40B080],EAX
00406C2C . 68 20B04000 PUSH pediy02.0040B020 ; /ProcNameOrOrdinal = "InsertMenuItemA"
00406C31 . 57 PUSH EDI ; |hModule
00406C32 . FFD6 CALL ESI ; \GetProcAddress
00406C34 . A3 84B04000 MOV DWORD PTR DS:[40B084],EAX
00406C39 . 68 50B04000 PUSH pediy02.0040B050 ; /ProcNameOrOrdinal = "SetMenu"
00406C3E . 57 PUSH EDI ; |hModule
00406C3F . FFD6 CALL ESI ; \GetProcAddress
00406C41 . A3 88B04000 MOV DWORD PTR DS:[40B088],EAX
00406C46 . 68 60B04000 PUSH pediy02.0040B060 ; /ProcNameOrOrdinal = "MessageBoxA"
00406C4B . 57 PUSH EDI ; |hModule
00406C4C . FFD6 CALL ESI ; \GetProcAddress
00406C4E . A3 8CB04000 MOV DWORD PTR DS:[40B08C],EAX
00406C90 . FF15 80B04000 CALL DWORD PTR DS:[40B080] ; USER32.CreateMenu
00406C96 . A3 90B04000 MOV DWORD PTR DS:[40B090],EAX
00406C9B . 68 00B14000 PUSH pediy02.0040B100
00406CA0 . 68 FF000000 PUSH 0FF
00406CA5 . 6A 00 PUSH 0
00406CA7 . 50 PUSH EAX
00406CA8 . FF15 84B04000 CALL DWORD PTR DS:[40B084] ; USER32.InsertMenuItemA
00406CAE . A1 90B04000 MOV EAX,DWORD PTR DS:[40B090]
00406CB3 . A3 44B14000 MOV DWORD PTR DS:[40B144],EAX
00406CB8 . FF15 80B04000 CALL DWORD PTR DS:[40B080] ; USER32.CreateMenu
00406CBE . A3 94B04000 MOV DWORD PTR DS:[40B094],EAX
00406CC3 . 68 30B14000 PUSH pediy02.0040B130
00406CC8 . 68 FF000000 PUSH 0FF
00406CCD . 6A 00 PUSH 0
00406CCF . 50 PUSH EAX
00406CD0 . FF15 84B04000 CALL DWORD PTR DS:[40B084] ; USER32.InsertMenuItemA
00406CD6 . FF35 94B04000 PUSH DWORD PTR DS:[40B094]
00406CDC . FF35 F0B04000 PUSH DWORD PTR DS:[40B0F0]
00406CE2 . FF15 88B04000 CALL DWORD PTR DS:[40B088] ; USER32.SetMenu
00406CE8 . 61 POPAD
00406CE9 .^ E9 9AA5FFFF JMP pediy02.00401288
代码:00406D00 > \8178 04 11010000 CMP DWORD PTR DS:[EAX+4],111
00406D07 . 75 77 JNZ SHORT pediy02.00406D80
00406D09 . 6A 40 PUSH 40
00406D0B . 68 8C804000 PUSH pediy02.0040808C ; ASCII "pediy"
00406D10 . 68 00B24000 PUSH pediy02.0040B200
00406D15 . FF35 F0B04000 PUSH DWORD PTR DS:[40B0F0]
00406D1B . FF15 8CB04000 CALL DWORD PTR DS:[40B08C] ; USER32.MessageBoxA
00406D80 > \FF15 24714000 CALL DWORD PTR DS:[<&USER32.DispatchMess>; \DispatchMessageA
00406D86 .^ E9 37A5FFFF JMP pediy02.004012C2