<題目>
無語了,想給你們製造點懸念都沒有了,哎!直接去做題吧。
解題鏈接: http://ctf5.shiyanbar.com/crack/3/
<解答>
該題目雖然被列爲難題,但是如果選對正確的工具和應用正確的方式去操作還是很容易解答的。
※拿到題目後,打開程序,按照要求輸入hello,註冊碼隨意,點擊按鈕出現錯誤信息,記錄下錯誤信息內容。
※使用PEiD查殼,發現該程序未加殼。
接下來介紹兩種不同的方法來破解此程序,
- OllyDbg法:
※將exe文件丟到OD中去,主窗口上右鍵->中文搜索引擎->智能搜索,然後在其中找到剛剛記錄下的錯誤提示:
※雙擊“祕鑰無效”轉到相應位置,向上找發現該命令可以跳過“祕鑰正確”,因此這是一個關鍵跳轉,那麼jnz上面的call命令就極有可能是一個關鍵call,讓我們在這兩個關鍵點處 摁f2設置斷點。
運行程序後,輸入用戶名和密碼,發現程序斷在了剛剛設置的call處,因此可判定這是一個決定註冊號正確與否的關鍵call。
※單步步入(f7)關鍵call之後再連續單步步過(f8),仔細觀察寄存器直到出現了註冊碼。
記錄下該註冊碼打開程序並輸入,最終提示祕鑰正確!
但是,當我們換一個用戶名時發現其註冊碼相應也隨之改變了。
因此我們推測註冊碼可能是根據用戶名計算而得來的。
2.IDA法:
※將exe再次丟進IDA中,在Dialogfun中發現其判斷標準都來自sub_4011D0
。雙擊call sub_4011D0
進入查看。
※使用f5將其嘗試反彙編成c/c++語言。
IDA反彙編代碼如下:
BOOL sub_4011D0()
{
unsigned int v1; // esi
CHAR String[4]; // [esp+8h] [ebp-60h]
__int16 v3; // [esp+25h] [ebp-43h]
char v4; // [esp+27h] [ebp-41h]
CHAR String2; // [esp+28h] [ebp-40h]
char v6; // [esp+29h] [ebp-3Fh]
__int16 v7; // [esp+45h] [ebp-23h]
char v8; // [esp+47h] [ebp-21h]
CHAR String1; // [esp+48h] [ebp-20h]
char v10; // [esp+49h] [ebp-1Fh]
__int16 v11; // [esp+65h] [ebp-3h]
char v12; // [esp+67h] [ebp-1h]
String[0] = 0;
memset(&String[1], 0, 0x1Cu);
v3 = 0;
v4 = 0;
String1 = 0;
memset(&v10, 0, 0x1Cu);
v11 = 0;
v12 = 0;
String2 = 0;
memset(&v6, 0, 0x1Cu);
v7 = 0;
v8 = 0;
if ( GetDlgItemTextA(hDlg, 1000, String, 16) < 5 )
return 1;
GetDlgItemTextA(hDlg, 1001, &String1, 16);
v1 = 0;
if ( strlen(String) != 0 )
{
do
{
*(&String2 + v1) = (v1 + v1 * String[v1] * String[v1]) % 0x42 + 33;
++v1;
}
while ( v1 < strlen(String) );
}
strcpy(String, "Happy@");
lstrcatA(String, &String2);
return lstrcmpA(&String1, String) != 0;
}
※只需關注最後幾行代碼,將其拷貝到VS2010中去,並稍加修改使得其能夠被編譯器所編譯。
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
string String="hello";
string String2;
int v1 = 0;
do
{
String2.insert(String2.begin()+v1,(v1+v1*String[v1]*String[v1])%0x42+33);
v1++;
//cout<<String[v1];
}
while (v1<String.length());
cout<<"Happy@";
for(int j=0;j<String2.length();j++)
{
cout<<String2[j];
}
cout<<endl;
return 0;
}
我們爲其添加頭文件和能夠輸出的函數,運行後得到了值“!GA0U”,該值就是IDA中的“&String2”。
IDA最後三行的意思分別可以大致解釋爲:
strcpy(String,"Happy@"); //將"Happy@"賦給String;
lstrcatA(String,&String2); //將計算得到的String接到"Happy@"後面;
return lstrcmpA(&String1,String)!=0 //將這個值與用戶輸入的註冊碼相對比並返回。
這就解釋了爲什麼不同的用戶名對應了不同的註冊碼。像剛剛編譯的帶有類似這種算法的程序大致就是我們所常見的算號器的原型。