下載題目,從圖標初步判斷是一個MFC程序,其它也沒什么好看的,直接拖入虛擬機雙擊運行看下顯示效果。
雙擊后程序顯示一個輸入框,提示輸入passwod,並驗證。
首先什么都不輸入,直接驗證,程序彈窗輸出提示字符串:“請輸入pass!”。
而隨意輸入pass,則彈窗輸出提示字符串:“錯了! 加油!”,並且程序退出。
從以上信息我們猜測:
1、如果pass正確,程序應該會和失敗時一樣彈窗,並且提示相關字符串,我們可以從搜索錯誤或者正確時提示的字符串入手,也可以從彈窗API入手。
2、既然程序需要用戶輸入,那我們就從輸入控件,或者獲取控件字符串的API入手。
3、假如程序是MFC程序,並且用戶輸入后需要點擊按鈕驗證,我們可以從OD搜索MFC按鈕事件代碼入手。
4、輸入pass錯誤后,程序會退出,搜索退出函數或API下斷,然后代碼回溯。
有了以上4個破解思路,我們就可以打開IDA開始分析了。
待IDA分析完代碼后,按快捷鍵Shift+F12查看字符串。
雙擊可疑字符串,查看代碼交叉引用。
F5查看偽代碼。
1 int __thiscall sub_401890(CWnd *this) 2 { 3 struct CString *v1; 4 CWnd *v2; 5 int v3; 6 int result; 7 int v5[26]; 8 int i; 9 char *Str; 10 CWnd *v8; 11 12 v8 = this; 13 v1 = (CWnd *)((char *)this + 100); 14 v2 = CWnd::GetDlgItem(this, 1002); 15 CWnd::GetWindowTextA(v2, v1); 16 v3 = sub_401A30((char *)v8 + 100); 17 Str = CString::GetBuffer((CWnd *)((char *)v8 + 100), v3); 18 if ( strlen(Str) ) 19 { 20 for ( i = 0; Str[i]; ++i ) 21 { 22 if ( Str[i] > 57 || Str[i] < 48 ) 23 { 24 if ( Str[i] > 122 || Str[i] < 97 ) 25 { 26 if ( Str[i] > 90 || Str[i] < 65 ) 27 sub_4017B0(); 28 else 29 v5[i] = Str[i] - 29; 30 } 31 else 32 { 33 v5[i] = Str[i] - 87; 34 } 35 } 36 else 37 { 38 v5[i] = Str[i] - 48; 39 } 40 } 41 result = sub_4017F0(v5); 42 } 43 else 44 { 45 result = CWnd::MessageBoxA(v8, "請輸入pass!", 0, 0); 46 } 47 return result; 48 }
第15、17行代碼將用戶輸入的pass保存到Str。
第18行代碼使用strlen函數檢測pass長度,如果為空,則第45行代碼彈窗提示重新輸入。
第22、24、26行代碼通過循環比較來判斷pass每個字符是否在0-9,a-z,A-Z范圍之間。通過IDA快捷鍵“R”可以將比較數值轉換為字符顯示。
第29、33、38行代碼則對不同范圍內的字符進行減法操作:
0-9:Str[i] -= 48
a-z:Str[i] -= 87
A-Z:Str[i] -= 29
第41行程序將處理后的pass交由“sub_4017F0”函數進行驗證處理。
1 int __cdecl sub_4017F0(int a1) 2 { 3 int result; 4 char Str1[28]; 5 int v3; 6 int v4; 7 8 v4 = 0; 9 v3 = 0; 10 while ( *(_DWORD *)(a1 + 4 * v4) < 62 && *(_DWORD *)(a1 + 4 * v4) >= 0 ) 11 { 12 Str1[v4] = aAbcdefghiabcde[*(_DWORD *)(a1 + 4 * v4)]; 13 ++v4; 14 } 15 Str1[v4] = 0; 16 if ( !strcmp(Str1, "KanXueCTF2019JustForhappy") ) 17 result = sub_401770(); 18 else 19 result = sub_4017B0(); 20 return result; 21 }
第10行代碼循環遍歷pass字符,判斷pass[i]是否>=0並且<62。
第12行代碼如果以上條件滿足,程序將pass[i]的值當做下標索引來獲取“aAbcdefghiabcde”字符數組對應索引位置的字符,保存到Str1。
第16行代碼使用“strcmp”函數比較Str1是否等於“KanXueCTF2019JustForhappy”字符串,如果等於則調用第17行函數,否則調用第19行函數。
第17行代碼:pass正確!
第19行代碼:pass錯誤!
假設:
字符串1 = "KanXueCTF2019JustForhappy"。
字符串2 = "abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQR"。
通過以上分析我們知道,正確的pass在經過一系列減法操作后,最終應該等於字符串1。
而字符串1是通過字符串2得到的。所以通過反向思維,我們首先獲取字符串1每個字符在字符串2中的下標索引。
然后根據每一個索引值的范圍加上不同的數,最終就是輸入時正確的pass!
1 void deCode() 2 { 3 char* key1 = "KanXueCTF2019JustForhappy"; 4 char* key2 = "abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ"; 5 6 for (int i = 0; i < strlen(key1); i++) 7 { 8 char* s = strchr(key2, key1[i]); 9 char c = s - key2; 10 if ((c + 48 >= '0') && (c + 48 <= '9')) 11 { 12 printf("%c", c + 48); 13 } 14 else if ((c + 87 >= 'a') && (c + 87 <= 'z')) 15 { 16 printf("%c", c + 87); 17 } 18 else if ((c+29 >= 'A') && (c+29 <= 'Z')) 19 { 20 printf("%c", c + 29); 21 } 22 } 23 printf("\n"); 24 }