160個CrackMe之108 mfc程式 尋找按鈕事件,程式碼還原(下)

極安御信發表於2022-03-10

·分析該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 <Windows.h> #include <stdio.h> 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碼為  <BrD-SoB>      程式碼為    CHAR String[10]={0};//因為彙編程式碼中都陣列中的值被初始化為0了,所以寫成String[10]={0}    CHAR String2[10]="<BrD-SoB>";  .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+5Bj  .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+83j  .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+71j  .text:004015C1                                         ; sub_401512+99j  .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]="<BrD-SoB>";   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程式以打包到附件了


相關文章