這個 Crackme 不愧是五星難度,看了別的大佬的破文剛開始都沒看懂,固然也多是我太菜了。到最後仍是本身一點點摳出來的,因此我儘可能講的詳細一點,這樣之後若是有人看別人的文章沒看懂的話,看個人說不定就看懂了呢,哈哈哈。git
程序使用了 UPX 的殼。github
使用 PEID 自帶的插件就能夠成功脫殼,可是必須在 Winxp環境下,win10 里老是報錯。算法
使用 OD 載入程序,搜索字符串函數
跟進代碼,能夠看到上方不遠處有一個 sscanf 函數調用加密
這個函數將咱們輸入的序列號轉化爲長整型的十六進制,分別保存在地址 12EF70、12EF7四、12EF7八、12EF7C 處。spa
因此輸入的序列號要分爲四部分,每部分使用空格或 "-" 分割。若是輸入的序列號不是四部分,就會提示錯誤。插件
在分析算法以前,先將程序的流程講明白。code
接下來是程序的關鍵了。由於下面要對咱們輸入的序列號進行處理。blog
程序首先創建循環,循環次數爲 3次。
在循環中,程序調用函數401B90,這個函數是真正的處理函數。md5
函數的參數有兩個。
第一個參數是序列號部分1的地址,也就是 12EF70;
第二個參數是 0xBADCODE / (0x50 + i) 的商,i 是 esi 的值。
接下來進入到函數內部。
首先是賦初值部分
接下里是循環部分
00401BA5 > 8BEF mov ebp,edi ; loc_401BA5 00401BA7 B9 01000000 mov ecx,0x1 00401BAC C1ED 1F shr ebp,0x1F 00401BAF 896C24 18 mov dword ptr ss:[esp+0x18],ebp 00401BB3 8BC6 mov eax,esi 00401BB5 8BD7 mov edx,edi 00401BB7 33ED xor ebp,ebp 00401BB9 E8 B21B0000 call <egis_11.__allshl> 00401BBE 8B4C24 18 mov ecx,dword ptr ss:[esp+0x18] 00401BC2 0BEA or ebp,edx 00401BC4 0BC8 or ecx,eax 00401BC6 33D2 xor edx,edx 00401BC8 8BF1 mov esi,ecx 00401BCA B9 0B000000 mov ecx,0xB 00401BCF 8BC6 mov eax,esi 00401BD1 8BFD mov edi,ebp 00401BD3 83E0 04 and eax,0x4 00401BD6 E8 951B0000 call <egis_11.__allshl> 00401BDB 8BCE mov ecx,esi 00401BDD 33ED xor ebp,ebp 00401BDF 81E1 00200000 and ecx,0x2000 00401BE5 33D5 xor edx,ebp 00401BE7 33C1 xor eax,ecx 00401BE9 B9 12000000 mov ecx,0x12 00401BEE E8 7D1B0000 call <egis_11.__allshl> 00401BF3 8BCE mov ecx,esi 00401BF5 33D5 xor edx,ebp 00401BF7 81E1 00000080 and ecx,0x80000000 00401BFD 33C1 xor eax,ecx 00401BFF B9 01000000 mov ecx,0x1 00401C04 E8 671B0000 call <egis_11.__allshl> 00401C09 33F0 xor esi,eax 00401C0B 33FA xor edi,edx 00401C0D 4B dec ebx 00401C0E ^ 75 95 jnz short <egis_11.loc_401BA5>
最後是結尾
能夠看到,最後將 esi 的值賦給了 serial[0],edi 的值賦給了 esi[1]。
因此在循環處理的時候,咱們只要緊盯着 esi 和 edi 便可。
關於 allshl 函數,也要說幾句。 allshl 函數是用來在 32 位CPU上進行 64位數的左移運算。共有 3個參數,eax爲64位數的低32位,edx 爲64位數的高32位,ecx爲這個64位數要左移的位數。
由於循環部分有點長,因此咱們將其分爲三部分
00401BA5 > 8BEF mov ebp,edi ; ebp = edi = serial[2] 00401BA7 B9 01000000 mov ecx,0x1 00401BAC C1ED 1F shr ebp,0x1F ; serial[2] >> 31 00401BAF 896C24 18 mov dword ptr ss:[esp+0x18],ebp 00401BB3 8BC6 mov eax,esi ; eax = serial[0] 00401BB5 8BD7 mov edx,edi ; edx = serial[1] 00401BB7 33ED xor ebp,ebp 00401BB9 E8 B21B0000 call <egis_11.__allshl> 00401BBE 8B4C24 18 mov ecx,dword ptr ss:[esp+0x18] 00401BC2 0BEA or ebp,edx 00401BC4 0BC8 or ecx,eax 00401BC6 33D2 xor edx,edx 00401BC8 8BF1 mov esi,ecx 00401BCA B9 0B000000 mov ecx,0xB 00401BCF 8BC6 mov eax,esi 00401BD1 8BFD mov edi,ebp
咱們能夠將其簡化爲下面的部分
C = edi >>31 ebp = 0 SHL(edx, eax, 1) ------------------------------------ ecx = C ecx = ecx | eax = C | eax ebp = ebp | edx = 0 | edx = edx esi1 = ecx = C | eax = C | (esi<<1) edi1 = ebp = edx = edi
C 即爲 edi 右移31位後的 bit 值,也就是 edi 的最高位 bit。
在這部分,esi 和 edi 的值發生了變化。我將其稱爲 esi1 和 edi1。
00401BC6 33D2 xor edx,edx 00401BC8 8BF1 mov esi,ecx 00401BCA B9 0B000000 mov ecx,0xB 00401BCF 8BC6 mov eax,esi 00401BD1 8BFD mov edi,ebp 00401BD3 83E0 04 and eax,0x4 00401BD6 E8 951B0000 call <egis_11.__allshl> 00401BDB 8BCE mov ecx,esi 00401BDD 33ED xor ebp,ebp 00401BDF 81E1 00200000 and ecx,0x2000 00401BE5 33D5 xor edx,ebp 00401BE7 33C1 xor eax,ecx 00401BE9 B9 12000000 mov ecx,0x12 00401BEE E8 7D1B0000 call <egis_11.__allshl> 00401BF3 8BCE mov ecx,esi 00401BF5 33D5 xor edx,ebp 00401BF7 81E1 00000080 and ecx,0x80000000 00401BFD 33C1 xor eax,ecx 00401BFF B9 01000000 mov ecx,0x1 00401C04 E8 671B0000 call <egis_11.__allshl>
在第二部分,esi 和 edi 的值並無發生變化,因此主要是觀察 eax 和 edx 的變化。
將其簡化爲下面部分
eax = esi1 & 0x4 = (C|eax) & 0x4 eax 除第3位其他全爲0 edx = 0 SHL(edx, eax, 0xb) eax 除第 14位其他全爲0 ---------------------------------------- ebp = 0 ecx = esi1 & 0x2000 eax 除第14位外其他全爲0 eax = eax ^ ecx eax 除第14位外其他位全爲0 edx = edx ^ ebp = edx ^ 0 SHL(edx, eax, 0x12) eax 除第32位外其他全爲0 ----------------------------------------- ecx = esi1 & 0x80000000 eax = eax ^ ecx edx = edx ^ ebp = edx ^ 0 SHL(edx, eax, 0x1) eax 全爲0
00000000 00000000 00000000 00000?00
的形式。?0000000 00000000 00000000 00000000
。edx 還爲 0。00000000 00000000 00000000 0000000?
。因此不管 eax 和 edx 的值爲多少,到最後 eax 一定爲 0,edx 只有最後一位 bit 不爲0,是由 eax 左移得來的。
00401C09 33F0 xor esi,eax 00401C0B 33FA xor edi,edx
轉化爲
esi2 = esi1 ^ eax = esi ^ 0 = esi1 edi2 = edi1 ^ edx
由於 eax 一定爲 0,因此 esi2 的值等於 esi1 的值。
edi2 的值等於 edi1 和 edx 作異或運算後的值。
那麼在一個循環中,esi 是如何變成 esi2 的呢?
edi 是如何變成 edi2 的呢?
edx = (esi1 & 0x4 >> 2) ^ (esi1 & 0x2000 >>13) ^ (esi1 & 0x80000000 >>31)
那麼咱們有了 esi2 和 edi2,如何獲得 esi 和 edi 呢?
我並無寫出完整的註冊機,只寫了將加密運算後的用戶名逆算法生成序列號的部分。也就是說 md5(name) = F(serial) 中,我只寫了後半部分,沒有寫 md5(name) 部分,因此使用的時候須要本身到程序中找加密後的 name,而後改成 Key[4] 的值便可。
#include <stdio.h> #include <string.h> #include <Windows.h> int Keygen() { unsigned long key[4] = { 0x0000D886, 0x36542100, 0x6C7EC6F2, 0xD4D3E0AE }; for (int i = 2; i >= 0; i--) { unsigned long num = 0xBADC0DE / (i + 0x50); unsigned long esi = *(key + i); unsigned long edi = *(key + i + 1); int C = 0; for (unsigned long j = num; j > 0; j--) { C = ((esi & 0x4) >> 2) ^ ((esi & 0x2000) >> 13) ^ ((esi & 0x80000000) >> 31); edi = edi ^ C; int DH = esi & 1; //DH 爲原來 edi 的最高位 int AH = edi & 1; //AH 爲原來 esi 的最高位 esi = (esi >> 1) | (AH << 31); edi = (edi >> 1) | (DH << 31); } *(key + i) = esi; *(key + i + 1) = edi; } printf("%X %X %X %X", key[0], key[1], key[2], key[3]); return 0; } int main(int argc, char* argv[]) { Keygen(); return 0; }
好比個人用戶名爲 1234
。
在 WinXP 中,生成的 MD5 爲0x0000D886, 0x36542100, 0x6C7EC6F2, 0xD4D3E0AE
,對應的序列號爲 83627B75 47DC1507 CE6FBBCD DAA36F3E
在 Win10 中,生成的 MD5 爲 0x0000C9B8, 0x37D4BC64, 0x1D98AF82, 0xDBF464AA
,對應的序列號爲 2FCEE274 EBC4892B 5EB25588 232790EB