作者:selph
目錄:
•001-前言1
•002-abexcm52
•003-CrueheadCM33
•004-AcidBytes.24
•005-Andrénalin.15
•006-ArturDents-CrackMe26
•007-reg7
•008-Afkayas.18
•009-Boonz-KeygenMe1
•010-ceycey10
6-10(關注下期文章)
1.001-前言
新160個CM來自:新160個CrackMe算法分析-001-簡介_哔哩哔哩_bilibili
前言
這位師傅整理了新160個CrackMe和配套的逆向視訊來幫助新手練習逆向技能,逆向的基礎便是閱讀反彙編的能力,這正是本練習的核心所在
以前我想過去堅持把160個做完,但沒堅持下來,近期總想着每天多多少少做點逆向練習,于是我打算去再次挑戰,本次以這個師傅整理的為準進行逆向的練習,去紮實自己的逆向功底
這個師傅提供了逆向的講解視訊,主要是基于OD動态調試分析的以及VB版本的注冊機編寫
這裡我從另一個視角去完成本系列文章:以IDA靜态分析為主,x86dbg動态分析為輔,完成程式的調試和分析,使用C++/C# 編寫注冊機
歡迎有興趣的童鞋來探讨交流~
本系列難度星級:
CM難度評星标準按視訊裡的走:
算法:
–⭐:明文字元串操作
–⭐⭐:很容易看懂的算法
–⭐⭐⭐:算法複雜但容易看懂,or 算法簡單但不容易看懂
–⭐⭐⭐⭐:算法難,看懂難
–⭐⭐⭐⭐⭐:分析不出來,以後回來做
爆破:
–⭐:靜态分析就能找到關鍵跳
–⭐⭐:回追一層,或修改2個點的
–⭐⭐⭐:回追二層以上,或修改超過2個點的
–⭐⭐⭐⭐:回追多層,修改點難找且多
–⭐⭐⭐⭐⭐:暫時無法破解,以後回來做
2. 002-abexcm5
爆破難度:⭐
算法難度:⭐
資訊收集
運作情況:輸入序列号,輸入錯誤會提示錯誤并退出程式,這是個驗證序列号的程式
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsQTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SMzEzNzQTM1QjMzAjM2YTMvwVMwkDMyIDMy8CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
查殼:無殼
查字元串:有提示語,疑似寫死的字元串
查導入表:使用了字元串操作類的函數,以及GetVolumeInformationA函數,不知道序列号生成跟這個有無關系
到現在已經知道了軟體大概的運作流程:擷取使用者輸入,對使用者輸入進行一些處理,然後彈框提示
逆向分析
IDA裡選擇MessageBoxA函數查交叉引用,跟蹤到函數sub_401056中,這是CM的校驗邏輯所在:
首先先擷取使用者輸入,然後生成兩個字元串:(注釋寫錯了,應該是do-while循環而不是while循環)
然後把剛剛生成的兩個字元串拼接到一起,生成序列号,與使用者輸入進行比對,序列号幾乎是寫死
暴力破解
驗證邏輯是:生成序列号,通過與使用者輸入的比對來進行驗證
暴力破解的思路是:修改跳轉條件即可,把jz改成jmp即可:
算法分析
注冊碼生成算法:
#include
#include
int main()
{
char VolumeNameBuffer[MAX_PATH] = { 0 };
DWORD VolumeSerialNumber = 0;
DWORD MaximumComponentLength = 0;
DWORD FileSystemFlags = 0;
int i = 2;
char Series[MAX_PATH] = { 0 };
GetVolumeInformationA(
0,
VolumeNameBuffer,
0x32u,
&VolumeSerialNumber,
&MaximumComponentLength,
&FileSystemFlags,
0,
0);
lstrcatA(VolumeNameBuffer, "4562-ABEX");
do{
VolumeNameBuffer[0]++;
VolumeNameBuffer[1]++;
VolumeNameBuffer[2]++;
VolumeNameBuffer[3]++;
} while (--i);
lstrcatA(Series, "L2C-5781");
lstrcatA(Series, VolumeNameBuffer);
std::cout << Series << std::endl;
system("pause");
return 0;
}
效果:
總結
字元串拼接生成序列号,通過判斷+跳轉進行校驗,很簡單,沒啥好說的
參考資料
–[1] GetVolumeInformationA擷取磁盤卷标、檔案系統,_上善若水pjf的部落格-CSDN部落格_getvolumeinformationa
3. 003-CrueheadCM3
爆破難度:⭐⭐
算法難度:⭐⭐
資訊收集
運作情況:是一個空白界面,可能要經過某些操作才能讓内容顯示出來
查殼:無殼
查字元串:看到了一些提示語
查導入表:除去視窗繪制,消息循環用到的函數,這裡還出現了檔案操作相關函數,可能跟檔案有關,結合上面的字元串搜尋資訊,應該需要一個CRACKME3.KEY的檔案
逆向分析
根據之前對檔案進行靜态的資訊收集之後,這個檔案操作很可疑,就從檔案操作函數CreateFileA去搜尋交叉引用看看這裡在幹嘛
首先打開名為CRACKME3.KEY的檔案,然後讀取其中的内容儲存到緩沖區
然後判斷讀取的位元組數如果是0x12就往下走,對讀取到的内容進行一頓操作,然後通過某種計算方法進行校驗,然後把校驗結果儲存在al裡入棧了
然後經過一段視窗建立的操作之後,在進入消息循環之前,做了這樣一個校驗,校驗檔案内容是否正确,正确就彈框提示,正是通過剛剛push的al進行校驗的
暴力破解
整個校驗流程最後還是通過判斷+跳轉進行執行的,暴力破解老樣子,直接修改跳轉條件即可:
前面還有個判斷讀取到的内容是否為0x12位元組,把那個跳轉也nop改掉即可,這裡就不示範了
算法分析
這裡的校驗算法主要是這幾行:
這裡調用了兩個自寫的函數,首先是sub_401311:這裡計算一個前14位元組的校驗和,然後對前14位元組依次與ABCD...進行異或操作,将異或的結果儲存起來,校驗和也儲存起來
然後是下一個函數sub_40133C:就是取後4位元組出來
現在這個校驗算法已經清晰了起來:
–計算一個校驗和,校驗和與0x12345678進行異或,得到的結果與輸入裡的最後4位元組進行比較
–對輸入的前14位元組進行異或操作,異或後的結果作為參數去調用顯示驗證成功提示框
那麼序列号的生成就是:
a.随便輸入一個14位元組的字元串作為使用者名
b.對這14位元組依次異或ABCD...,儲存起來
c.對校驗和異或0x12345678,然後拼接到異或結果後面,即可完成生成
注冊碼生成算法:
#include
#include
void generateSeriesFile(const char* in_pwd) {
char pwd[0x13] = { 0 };
int i;
char var_bl = 'A';
unsigned int sum = 0;
memcpy(pwd, in_pwd, 0x13);
i = 0;
do {
sum += pwd[i];
pwd[i] ^= var_bl;
i++;
var_bl++;
if (!pwd[i])break;
} while (var_bl != 'O');
sum ^= 0x12345678;
*(unsigned int *)&pwd[0xE] = sum;
for (int k = 0; k < 18; k++)
{
unsigned char* tmp = (unsigned char*) & pwd;
printf("%02x ",tmp[k]);
}
HANDLE hFile = CreateFileA("CRACKME3.KEY", GENERIC_ALL, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD retNum = 0;
WriteFile(hFile, pwd, sizeof(pwd), &retNum, NULL);
CloseHandle(hFile);
}
int main()
{
const char *pwd= "[email protected]";
generateSeriesFile(pwd);
system("pause");
return 0;
}
效果:
總結
序列号算法和校驗依然很簡單,這裡有意思的是:這個程式的校驗邏輯位于視窗進入消息循環之前,校驗行為發生在了軟體正常運作之前,根據校驗結果再選擇是否正常運作程式
參考資料
–[1] ASCII碼一覽表,ASCII碼對照表 (biancheng.net)
–[2] setz_ccboby的部落格-CSDN部落格_setz指令
4. 004-AcidBytes.2
算法難度:⭐
爆破難度:⭐
脫殼難度:⭐
資訊收集
運作情況:
依然是序列号驗證,輸入序列号點選Check,會顯示提示資訊
查殼與脫殼:
出現殼了,Die查出來是Upx壓縮殼
對于Upx殼使用ESP定律即可完成脫殼,過程相當簡單,這裡簡述一下就不截圖示範了:
a.運作到OEP,運作到pushad的下一行(執行這個指令隻有esp的值會被修改)
b.在記憶體中檢視esp指向的位址,對該位址下通路硬體斷點,然後運作,此時會運作到popad指令的下一行,是跳轉到真正OEP的jmp,跳轉過去
c.使用Scylla進行Dump和修複PE,得到脫殼後的程式
再次查殼驗證:
查字元串:
有點幫助的字元串是這些,是驗證提示資訊
查導入表:
沒有什麼特别的點,看起來都是圖形界面相關的内容,程式使用MessageBoxA彈窗提示
調試分析
這個程式的傳參方式比較特别,根據查閱資料[1],前三個資料儲存在eax,edx,ecx寄存器裡,超過三個參數部分放在堆棧傳遞
這裡以字元串作為入口進行突破,搜尋字元串Congrats!...的交叉引用,找到按鈕控件的處理例程:
首先是注冊了SEH異常鍊,然後擷取使用者輸入
接下來就是比較+彈窗三連:
輸入字元串和寫死字元串進行對比,如果相同,就彈窗提示成果
如果不相同,就判斷是否輸入的有内容,如果無内容,提示輸入為空,否則提示輸入錯誤
暴力破解
直接Nop掉關鍵跳即可:
算法分析
寫死密碼,無算法效果:
總結
處理該CM的要點就是脫殼,脫殼之後就是寫死判斷跳轉,算是個入門級脫殼練習
參考資料
–[1] Delphi的參數傳遞約定以及動态參數個數(轉載筆記) - 不得閑 - 部落格園 (cnblogs.com)
5. 005-Andrénalin.1
算法難度:⭐
爆破難度:⭐
資訊收集
運作情況:
功能就是輸入密碼,然後驗證
查殼與脫殼:
無殼,是 VB 編寫的 GUI 程式,需要使用 VB 反編譯工具進行逆向分析
調試分析
對于 VB 程式,可以使用 VB Decompiler 進行逆向,本例驗證邏輯較為簡單,估計主要是練習