160个CrackMe之108 mfc程序 寻找按钮事件,代码还原(下)

·分析该CrackMe按钮事件

这个check就是个按钮,现在用visual studio提供的工具看看这个按钮id

·选择自己的Spy++(不会用的话百度查一下)

找到check按钮id1

·那该程序的检查按钮事件就为

AFX_MSGMAP_ENTRY 按钮;按钮。nMessage = WM_COMMAND ;  按钮。nCode = BN_CLICKED ;  按钮。nID = 1 ;  按钮。nLastID = 1 ;  按钮。nSig = AfxSigCmd_v ;  按钮。pfn =未知; 
 
等价替换后 
btn.nMessage=0x0111; btn.nCode=0; btn.nID=1; btn.nLastID=1; btn.nSig=AfxSigCmd_v; btn.pfn=CMFCApplication3Dlg::未知; 
 
btn的内存呈现形式为 
nMessage,nCode,nID,nLastID,nSig,pfn 
可以不用管nSig,pfn的值,只用搜索前四项就够了,等价替换为 
0x0111,  0,   1,1,nSig,pfn       等价替换为 
11 01 00 00, 00 00 00 00, 01 00 00 00, 01 00 00 00 ,nSig,pfn 等价替换为 
11 01 00 00 00 00 00 00 01 00 00 00 01 00 00 00  
找到按钮事件地址 795E7980

·地址795E7980的汇编代码看着有点怪异的是因为这里是虚函数调用(vcall) 执行到795E798D jmp eax f7跟进去后来到真正的按钮事件地址

·现在来讲讲vcall,写了个小例子

#include  #include  class myclass 
{ public: 
 	virtual void func1() 
 	{ 
 
 	} 
 	virtual void func2() 
 	{ 
 
 	} 
};  int  main ( )  
{  
 	printf ( "MYACLS::myvirfunc1()地址=%p\n" ,  & myclass :: func1 ) ;  //打印的是vcall函数  	printf ( "MYACLS::myvirfunc2()地址=%p\n" ,  & myclass :: func2 ) ; 
  	myclass * pmyobj =  new  myclass ( ) ; 
 
 	返回 1 ;  
} 

·执行的结果

 

#### myvirfunc1 myvirfunc2函数真正的地址为

现在00FB108400FB1088代码组合

是不是和地址795E7980代码很相似

·vcall 的作用

调整这个指针到真正的虚函数中

·现在看看代码还原

ida打开该程序跳转00401512

text:00401518                 mov     [ebp+var_20], ecx 
.text:0040151B                 mov     ax, word_40315C 
.text:00401521                 mov     word ptr [ebp+String], ax 
.text:00401525                 xor     ecx, ecx 
.text:00401527                 mov     [ebp+var_A], ecx 
.text:0040152A                 mov     [ebp+var_6], ecx 
.text:0040152D                 mov     edx, dword_403020 
.text:00401533                 mov     dword ptr [ebp+String2], edx 
.text:00401536                 mov     eax, dword_403024 .text:0040153B                 mov     [ebp+var_18], eax 
.text:0040153E                 mov     cx, word_403028 
.text:00401545                 mov     [ebp+var_14], cx 
 
var_20看前后使用是 用来this指针的不用管 
 
var_A和var_6都被初始化为0,上下文都没有使用,说明是数组 
String 只初始化了2个字节,加上var_A和var_6,String是10个字节 
 
String2 和var_18,var_14地址相连,var_18,var_14上下文都没有使用,说明是数组 var_14只初始化了2个字节,加上String2和var_18,String2是10个字节 dword_403020 值为4472423C,dword_403024值为426F532D,word_403028值为003E换成asci码为 
 
 
 
代码为 
 	CHAR String[10]={0};//因为汇编代码中都数组中的值被初始化为0了,所以写成String[10]={0} 
 	CHAR String2[10]=""; 
.text:00401549                 push    0Ah             ; int 
.text:0040154B                 lea     edx, [ebp+String]
.text:0040154E                 push    edx             ; char * .text:0040154F                 push    3E8h            ; int 
.text:00401554                 mov     ecx, [ebp+var_20] ; this 
.text:00401557                 call    ?GetDlgItemTextA@CWnd@@QBEHHPADH@Z ; CWnd::GetDlgItemTex
.text:0040155C                 lea     eax, [ebp+String]
.text:0040155F                 push    eax             ; lpString 
.text:00401560                 call    ds:lstrlenA 
.text:00401566                 mov     [ebp+var_10], eax 
.text:00401569                 cmp     [ebp+var_10], 1 
.text:0040156D                 jnb     short loc_401585 
 
push的是10,调用的函数是intGetDlgItemText(intnID**,LPTSTRlpStr,intnMaxCount),说明数组 大就是10, var_10=lstrlenA函数的返回值, 后比较的后的跳转指令是jnb 说明var_10是无符号的 
 
代码为 
GetDlgItemTextA(1000, String, 10); unsigned int var_10; var_10 = lstrlenA(String); if ( var_10<1)
.text:0040156F                 push    40h ; '@'       ; unsigned int 
.text:00401571                 push    offset aCrackme ; "CrackMe" 
.text:00401576                 push    offset aEnterRegistrat ; "Enter Registration Number" 
.text:0040157B                 mov     ecx, [ebp+var_20] ; this 
.text:0040157E                 call    ?MessageBoxA@CWnd@@QAEHPBD0I@Z ; CWnd::MessageBoxA(char 
.text:00401583                 jmp     short loc_4015C1 
.text:00401585 ; --------------------------------------------------------------------------- .text:00401585 
.text:00401585 loc_401585:                             ; CODE XREF: sub_401512+5B↑j 
.text:00401585                 lea     ecx, [ebp+String2] 
.text:00401588                 push    ecx             ; lpString2 .text:00401589                 lea     edx, [ebp+String] 
.text:0040158C                 push    edx             ; lpString1 
.text:0040158D                 call    ds:lstrcmpA 
.text:00401593                 test    eax, eax 
.text:00401595                 jnz     short loc_4015AD 
.text:00401597                 push    40h ; '@'       ; unsigned int 
.text:00401599                 push    offset aCrackme_0 ; "CrackMe" 
.text:0040159E                 push    offset aCorrectWayToGo ; "Correct way to go!!" 
.text:004015A3                 mov     ecx, [ebp+var_20] ; this 
.text:004015A6                 call    ?MessageBoxA@CWnd@@QAEHPBD0I@Z ; CWnd::MessageBoxA(char 
.text:004015AB                 jmp     short loc_4015C1 
.text:004015AD ; --------------------------------------------------------------------------- .text:004015AD 
.text:004015AD loc_4015AD:                             ; CODE XREF: sub_401512+83↑j 
.text:004015AD                 push    40h ; '@'       ; unsigned int 
.text:004015AF                 push    offset aCrackme_1 ; "CrackMe" 
.text:004015B4                 push    offset aIncorrectTryAg ; "Incorrect try again!!" 
.text:004015B9                 mov     ecx, [ebp+var_20] ; this 
.text:004015BC                 call    ?MessageBoxA@CWnd@@QAEHPBD0I@Z ; CWnd::MessageBoxA(char .text:004015C1 
.text:004015C1 loc_4015C1:                             ; CODE XREF: sub_401512+71↑j 
.text:004015C1                                         ; sub_401512+99↑j 
.text:004015C1                 mov     esp, ebp 
.text:004015C3                 pop     ebp 
.text:004015C4                 retn 
.text:004015C4 sub_401512      endp 
 
汇编结构来看感觉是 if, elseif, else结构代码为 
 	 	MessageBoxA("Enter Registration Number", "CrackMe", 0x40u);  	else if ( lstrcmpA(String, String2) ) 
 	 	MessageBoxA("Incorrect try again!!", "CrackMe", 0x40u); 
 	else 
 	 	MessageBoxA("Correct way to go!!", "CrackMe", 0x40u); 
 
 
 	CHAR String2[10]="";  	unsigned int var_10; 
 	CHAR String[10]={0};//因为汇编代码中都数组中的值被初始化为0了,所以写成String[10]={0} 
 
 	GetDlgItemTextA(1000, String, 10);  	var_10 = lstrlenA(String);  	if ( var_10<1) 
 	 	MessageBoxA("Enter Registration Number", "CrackMe", 0x40u);  	else if ( lstrcmpA(String, String2) ) 
 	 	MessageBoxA("Incorrect try again!!", "CrackMe", 0x40u); 
 	else 
 	 	MessageBoxA("Correct way to go!!", "CrackMe", 0x40u); 

·因为该程序是vc6写的,我自己也用vc6写了一份,因为该crackme引用的dll是 MFC42.DLL 明说不是Debug编译的,Debug编译的是用的MFC42D.DLL,所以

选择用Release编译,第二因为该crackme是用ebp寻找的局部变量,说明没有开 o2优化编译,我个人就选择的无优化编译,最后生成的二进制和原版还有略有差异

·最后输入秘钥 实现破解

·完成自己代码还原的mfc程序以打包到附件了