前言
最近在学习MFC程序,以前破解MF程序时感觉不好针按钮对下断点,所以特意熟悉与分析了MFC的消息映射机制
注: 这只是一个很随意的笔记,用来分析的程序只是一个CM,所以有些观点不全面或不正确,忘大家指出.
参考书籍
王艳平 <<windows程序设计>> 00401633 . 8B7424 18 MOV ESI,DWORD PTR SS:[ESP+18]
00401637 > 6A 00 PUSH 0
00401639 . 68 34304000 PUSH Rith_Cra.00403034 ; congratulations!
0040163E . 68 20304000 PUSH Rith_Cra.00403020 ; well done cracker!
00401643 . 8BCE MOV ECX,ESI
00401645 . E8 54020000 CALL <JMP.&MFC42.#4224_MessageBoxA@CWnd@@QAEHPBD0I@Z>
用提示字符串找到这个按钮的算法部分 也就是 MFC程序的消息映射函数处;
一个按钮点击事件的过程如下:
CWinThread::PumpMessage -> CWnd::PretranslateMessage -> CWnd::WWalkPreTranslateMessate -> CD1Dlg::PreTranslateMessage -> CDialog::PreTranslateMessage -> CWnd::PreTranslateInput -> CWnd::IsDialogMessageA -> USER32内核 -> AfxWndProcBase -> AfxWndProc -> AfxCallWndProc -> CWnd::WindowProc -> CWnd::OnWndMsg -> CWnd::OnCommand -> CDialog::OnCmdMsg -> CCmdTarget::OnCmdMsg -> _AfxDispatchCmdMsg -> CD1Dlg::OnButton1()
由于我主要跟踪的是MFC的消息映射,所以该流程中USER32内核前的部分可以不用关心.
在刚才打开算法部分的的开头下断,点击按钮,断下来,ALT+K打开调用堆栈,在use32后面第一个MFC函数体上点显示过程,下断.
F9运行断下来,看
73DC8468 FF75 14 PUSH DWORD PTR SS:[EBP+14]
73DC846B 8365 FC 00 AND DWORD PTR SS:[EBP-4],0
73DC846F FF75 10 PUSH DWORD PTR SS:[EBP+10]
73DC8472 FF75 0C PUSH DWORD PTR SS:[EBP+C]
73DC8475 FF75 08 PUSH DWORD PTR SS:[EBP+8]
73DC8478 E8 A595F6FF CALL MFC42.#1578_?AfxWndProc@@YGJPAUHWND__@@IIJ@Z
73DC847D 8B4D F0 MOV ECX,DWORD PTR SS:[EBP-10]
73DC8480 8B55 EC MOV EDX,DWORD PTR SS:[EBP-14]
73DC8483 8951 04 MOV DWORD PTR DS:[ECX+4],EDX
73DC8486 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
73DC8489 64:890D 00000000 MOV DWORD PTR FS:[0],ECX
73DC8490 C9 LEAVE
73DC8491 C2 1000 RETN 10
看到CALL MFC42.#1578_?AfxWndProc了吧,这个函数是 MFC提供的通用消息处理函数,MFC的消息是通过这个函数分发到每个窗口的cWnd对象,然后在cWnd对象定义的消息处理函数中处理各种消息.
然后在73DC8478 E8 A595F6FF CALL MFC42.#1578_?AfxWndProc@@YGJPAUHWND__@@IIJ@Z上下断,F7单步进入, 73D31A36 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8]
73D31A39 56 PUSH ESI
73D31A3A E8 C3F7FFFF CALL MFC42.#2867_?FromHandlePermanent@CWnd@@SGPAV1@PAUHWND>
73D31A3F 85C0 TEST EAX,EAX
73D31A41 74 17 JE SHORT MFC42.73D31A5A
73D31A43 3970 20 CMP DWORD PTR DS:[EAX+20],ESI
73D31A46 75 12 JNZ SHORT MFC42.73D31A5A
73D31A48 FF75 14 PUSH DWORD PTR SS:[EBP+14]
73D31A4B FF75 10 PUSH DWORD PTR SS:[EBP+10]
73D31A4E FF75 0C PUSH DWORD PTR SS:[EBP+C]
73D31A51 56 PUSH ESI
73D31A52 50 PUSH EAX
73D31A53 E8 1C000000 CALL MFC42.#1109_?AfxCallWndProc@@YGJPAVCWnd@@PAUHWND__@@I>
这里面有两个函数,FromHandlePermanent@CWnd这个函数根据传入窗口的hWND找到起对应的CWnd对象,然后把(*pCwnd,hWnd,uMsg,wPram,lParam)传入到下个AfxCallWndProc函数中.
单步进入AfxCallWndProc函数中,
73D31A83 57 PUSH EDI
73D31A84 8965 F0 MOV DWORD PTR SS:[EBP-10],ESP
73D31A87 68 DB88DC73 PUSH MFC42.#2202_?CreateObject@?$CThreadLocal@V_AFX_THREAD>
73D31A8C B9 0CE6E073 MOV ECX,MFC42.73E0E60C
73D31A91 E8 DF670900 CALL MFC42.#3030_?GetData@CThreadLocalObject@@QAEPAVCNoTra>
73D31A96 8BD8 MOV EBX,EAX
73D31A98 8D43 34 LEA EAX,DWORD PTR DS:[EBX+34]
73D31A9B 8BF0 MOV ESI,EAX
73D31A9D 6A 07 PUSH 7
73D31A9F 59 POP ECX
73D31AA0 8D7D C0 LEA EDI,DWORD PTR SS:[EBP-40]
73D31AA3 F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
73D31AA5 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
73D31AA8 8B75 10 MOV ESI,DWORD PTR SS:[EBP+10]
73D31AAB 8B7D 08 MOV EDI,DWORD PTR SS:[EBP+8]
73D31AAE 8908 MOV DWORD PTR DS:[EAX],ECX
73D31AB0 8B45 14 MOV EAX,DWORD PTR SS:[EBP+14]
73D31AB3 8943 3C MOV DWORD PTR DS:[EBX+3C],EAX
73D31AB6 8B45 18 MOV EAX,DWORD PTR SS:[EBP+18]
73D31AB9 8943 40 MOV DWORD PTR DS:[EBX+40],EAX
73D31ABC 33C0 XOR EAX,EAX
73D31ABE 83FE 02 CMP ESI,2
73D31AC1 895D EC MOV DWORD PTR SS:[EBP-14],EBX
73D31AC4 8973 38 MOV DWORD PTR DS:[EBX+38],ESI
73D31AC7 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
73D31ACA 75 0E JNZ SHORT MFC42.73D31ADA
73D31ACC 3947 38 CMP DWORD PTR DS:[EDI+38],EAX
73D31ACF 74 09 JE SHORT MFC42.73D31ADA
73D31AD1 8B4F 38 MOV ECX,DWORD PTR DS:[EDI+38]
73D31AD4 8B11 MOV EDX,DWORD PTR DS:[ECX]
73D31AD6 50 PUSH EAX
73D31AD7 FF52 64 CALL DWORD PTR DS:[EDX+64]
73D31ADA 8365 0C 00 AND DWORD PTR SS:[EBP+C],0
73D31ADE 81FE 10010000 CMP ESI,110 // ; 判断程序是否初始化
73D31AE4 75 0E JNZ SHORT MFC42.73D31AF4
73D31AE6 8D45 0C LEA EAX,DWORD PTR SS:[EBP+C]
73D31AE9 50 PUSH EAX
73D31AEA 8D45 DC LEA EAX,DWORD PTR SS:[EBP-24]
73D31AED 50 PUSH EAX
73D31AEE 57 PUSH EDI
73D31AEF E8 51880100 CALL MFC42.73D4A345
73D31AF4 FF75 18 PUSH DWORD PTR SS:[EBP+18]
73D31AF7 8B07 MOV EAX,DWORD PTR DS:[EDI]
73D31AF9 FF75 14 PUSH DWORD PTR SS:[EBP+14]
73D31AFC 8BCF MOV ECX,EDI
73D31AFE 56 PUSH ESI
73D31AFF FF90 A0000000 CALL DWORD PTR DS:[EAX+A0]
这里的
GetData@CThreadLocalObject@@QAEPAVCNoTrackObject@@P6GPAV2@XZ@Z是为了得到一个
AFX_MODULE_STATE结构,并且把当前的消息填充到AFX_MODULE_STATE结构中.
单步到73D31AFF FF90 A0000000 CALL DWORD PTR DS:[EAX+A0] ; <JMP.&MFC42.#6374_?WindowProc@CWnd@@MAEJIIJ@Z>
这里,F7进入: 73D31B8D 8BCE MOV ECX,ESI
73D31B8F FF75 0C PUSH DWORD PTR SS:[EBP+C]
73D31B92 FF75 08 PUSH DWORD PTR SS:[EBP+8]
73D31B95 FF90 A4000000 CALL DWORD PTR DS:[EAX+A4] ; <JMP.&MFC42.#5163_?OnWndMsg@CWnd@@MAEHIIJPAJ@Z>
73D31B9B 85C0 TEST EAX,EAX
73D31B9D 75 16 JNZ SHORT MFC42.73D31BB5
73D31B9F FF75 10 PUSH DWORD PTR SS:[EBP+10]
73D31BA2 8B06 MOV EAX,DWORD PTR DS:[ESI]
73D31BA4 FF75 0C PUSH DWORD PTR SS:[EBP+C]
73D31BA7 8BCE MOV ECX,ESI
73D31BA9 FF75 08 PUSH DWORD PTR SS:[EBP+8]
73D31BAC FF90 A8000000 CALL DWORD PTR DS:[EAX+A8]
73D31BB2 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
73D31BB5 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] 这里面同样有两个函数调用:
<JMP.&MFC42.#5163_?OnWndMsg@CWnd@@MAEHIIJPAJ@Z>
这个函数是 MFC处理消息的核心函数,在这个函数里面MFC确定是否对传入的消息进行处理:
如果OnWndMsg@CWnd没有处理这个消息,将由下面这个函数处理CALL DWORD PTR DS:[EAX+A8](这里的[EAX+A8]实际上是 DefWindowProcA@CWnd,windows的默认消息函数);
进入OnWndMsg@CWnd@@MAEHIIJPAJ@Z,
73D31BD7 81FB 11010000 CMP EBX,111
73D31BDD 56 PUSH ESI
73D31BDE 57 PUSH EDI
73D31BDF 8BF9 MOV EDI,ECX
73D31BE1 75 1B JNZ SHORT MFC42.73D31BFE
看到这里的EBX值没,程序在比较ebx中所放的消息是否是WM_COMMAND,如果不是剩下的就交给其他的消息函数处理,我们的探索之旅就结束了,我们当然不能让这种事情发生.
73D31BD7 81FB 11010000 CMP EBX,111
在这上面下条件断点 ebx==111,取消其他断点.F9运行,单击check it 按钮.断下来:
73D31BD7 > 81FB 11010000 CMP EBX,111
73D31BDD 56 PUSH ESI
73D31BDE 57 PUSH EDI
73D31BDF 8BF9 MOV EDI,ECX
73D31BE1 75 1B JNZ SHORT MFC42.73D31BFE
73D31BE3 FF75 10 PUSH DWORD PTR SS:[EBP+10]
73D31BE6 8B07 MOV EAX,DWORD PTR DS:[EDI]
73D31BE8 FF75 0C PUSH DWORD PTR SS:[EBP+C]
73D31BEB FF90 80000000 CALL DWORD PTR DS:[EAX+80] ; <JMP.&MFC42.#4441_?OnCommand@CWnd@@MAEHIJ@Z>
73D31BF1 85C0 TEST EAX,EAX
73D31BF3 0F84 CA000000 JE MFC42.73D31CC3
这里 <JMP.&MFC42.#4441_?OnCommand@CWnd@@MAEHIJ@Z>就是对按钮事件进行响应的位置,F7步入:
73D3323B FF75 08 PUSH DWORD PTR SS:[EBP+8]
73D3323E 8BCE MOV ECX,ESI
73D33240 57 PUSH EDI
73D33241 FF50 14 CALL DWORD PTR DS:[EAX+14] ; <JMP.&MFC42.#4425_?OnCmdMsg@CDialog@@UAEHIHPAXPAUAFX_CMDHANDLERINFO@@@Z>
73D33244 5F POP EDI
73D33245 5E POP ESI
73D33246 5B POP EBX
73D33247 C9 LEAVE 单步到:
73D33241 FF50 14 CALL DWORD PTR DS:[EAX+14] ; <JMP.&MFC42.#4425_?OnCmdMsg@CDialog@@UAEHIHPAXPAUAFX_CMDHANDLERINFO@@@Z>
上继续步入:
73D9DEA4 8BF1 MOV ESI,ECX
73D9DEA6 57 PUSH EDI
73D9DEA7 53 PUSH EBX
73D9DEA8 E8 0844F9FF CALL MFC42.#4424_?OnCmdMsg@CCmdTarget@@UAEHIHPAXPAUAFX_CMDHANDLERINFO@> 继续步入73D9DEA8 E8 0844F9FF CALL MFC42.#4424_?OnCmdMsg@CCmdTarget@@UAEHIHPAXPAUAFX_CMDHANDLERINFO@>
73D32385 FF75 08 PUSH DWORD PTR SS:[EBP+8]
73D32388 FF75 0C PUSH DWORD PTR SS:[EBP+C]
73D3238B 53 PUSH EBX
73D3238C FF76 04 PUSH DWORD PTR DS:[ESI+4]
73D3238F E8 DAFEFFFF CALL MFC42.#1145_?AfxFindMessageEntry@@YGPBUAFX_MSGMAP_ENTRY@@PBU1@III>
73D32394 85C0 TEST EAX,EAX
73D32396 75 0F JNZ SHORT MFC42.73D323A7
73D32398 FF16 CALL DWORD PTR DS:[ESI]
73D3239A 8BF0 MOV ESI,EAX
73D3239C 85F6 TEST ESI,ESI
73D3239E ^ 75 E5 JNZ SHORT MFC42.73D32385
73D323A0 5E POP ESI
73D323A1 5B POP EBX
73D323A2 5F POP EDI
73D323A3 5D POP EBP
73D323A4 C2 1000 RETN 10 这里面AfxFindMessageEntry就是MFC中实现消息映射的位置,MFC的CWnd类中存放有
BEGIN_MESSAGE_MAP(CMsgDlg, CDialog)
//{{AFX_MSG_MAP(CMsgDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
ON_BN_CLICKED(IDC_BUTTON2, OnButton2)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
STRUCT AFX_MSG_ENTRY
{
UNIT nMessage; //窗口消息
UNIT nCode; //控制代码
UNIT nID; //控件ID
UNIT nLastID
UNIT nSig //消息处理函数的类型
AFX_PMSG pfn //消息处理函数的地址
}
如上所示的静态数组,称为消息映射表.MFC用一个STRUCT AFX_MSG_ENTRY结构来描述映射表中的每个表项.
在这里通过一个GetMessageMap函数获得消息映射表的基地址,然后在AfxFindMessageEntry函数中,调用一个循环,比较传入的消息和映射表中表项的是否匹配来找到消息对应的处理函数,然后把这个数组中的pfn(消息处理函数地址)记录下来.
继续单步:
73D323BA E8 7F000000 CALL MFC42.73D3243E
73D323BF ^ EB DF JMP SHORT MFC42.73D323A0
CALL MFC42.73D3243E步入:
73D3243E 8BFF MOV EDI,EDI
73D32440 55 PUSH EBP
73D32441 8BEC MOV EBP,ESP
73D32443 8B45 20 MOV EAX,DWORD PTR SS:[EBP+20]
73D32446 53 PUSH EBX
73D32447 33DB XOR EBX,EBX
73D32449 43 INC EBX
73D3244A 85C0 TEST EAX,EAX
73D3244C 74 12 JE SHORT MFC42.73D32460
73D3244E 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
73D32451 8908 MOV DWORD PTR DS:[EAX],ECX
73D32453 8B4D 14 MOV ECX,DWORD PTR SS:[EBP+14]
73D32456 8948 04 MOV DWORD PTR DS:[EAX+4],ECX
73D32459 8BC3 MOV EAX,EBX
73D3245B E9 E4000000 JMP MFC42.73D32544
73D32460 8B45 1C MOV EAX,DWORD PTR SS:[EBP+1C]
73D32463 83F8 28 CMP EAX,28
73D32466 56 PUSH ESI
73D32467 77 71 JA SHORT MFC42.73D324DA
73D32469 74 5C JE SHORT MFC42.73D324C7
73D3246B 48 DEC EAX
73D3246C 48 DEC EAX
73D3246D 74 53 JE SHORT MFC42.73D324C2
73D3246F 83E8 0A SUB EAX,0A
73D32472 74 46 JE SHORT MFC42.73D324BA
73D32474 48 DEC EAX
73D32475 74 3E JE SHORT MFC42.73D324B5
73D32477 83E8 16 SUB EAX,16
73D3247A 74 2E JE SHORT MFC42.73D324AA
73D3247C 83E8 03 SUB EAX,3
73D3247F 74 16 JE SHORT MFC42.73D32497
73D32481 48 DEC EAX
73D32482 75 69 JNZ SHORT MFC42.73D324ED
73D32484 8B45 18 MOV EAX,DWORD PTR SS:[EBP+18]
73D32487 FF30 PUSH DWORD PTR DS:[EAX]
73D32489 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
73D3248C FF70 04 PUSH DWORD PTR DS:[EAX+4]
73D3248F FF55 14 CALL DWORD PTR SS:[EBP+14]
73D32492 E9 A8000000 JMP MFC42.73D3253F
单步到第1个[ebp+14],经常破解的应该知道[ebp+14]这是个参数,你要是跟踪下来就会知道ebp+14的值就是AfxFindMessageEntry函数中,保存的消息处理函数的地址.
哦,原来call [EBP+14]是调用我们的消息处理函数,继续进入你就发现这就是程序的算法部分.
在这里我们先,停下来总结下.MFC通过一连串的函数走到了AfxFindMessageEntry,然后记录下
消息处理函数的地址,在通过call [EBP+14]来调用消息处理函数.
那我们是否可以通过call [EBP+14]来定位程序的算法部分.删除所有断点,,直接F9,然后ALT+E,双击MFC42.DLL,CTRL+F,输入CALL [EBP+14],在第一个找到的地址处F2下断点。
步入,果然就到了算法部分.(这里是 看 书呆彭 的贴才知道,大牛就是厉害,可惜他没讲原理).
那这中方法是否适用于其他的MFC版本呢?
在MFC42d中,也可以通过这种方式秒破.不过特征值变成了 call [ebp-4].
最后我总结下MFC的消息路由:
AfxWindProc
|
afxCallWndProc
|
WindowProc
|
OnWndMsg(这里面判断消息类型,然后进行分发)
如果OnWndMsg没有处理DefWindowProcA@CWnd进行默认处理 这次的分析到这里就结束了,算法很简单,我就不详细说明了.
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: