(五星難度)Crackme052

前言

這個 Crackme 不愧是五星難度,看了別的大佬的破文剛開始都沒看懂,固然也多是我太菜了。到最後仍是本身一點點摳出來的,因此我儘可能講的詳細一點,這樣之後若是有人看別人的文章沒看懂的話,看個人說不定就看懂了呢,哈哈哈。git

程序觀察

程序使用了 UPX 的殼。
20191107180328.pnggithub

使用 PEID 自帶的插件就能夠成功脫殼,可是必須在 Winxp環境下,win10 里老是報錯。算法

20191107192237.png

程序分析

使用 OD 載入程序,搜索字符串
20191107192653.png函數

跟進代碼,能夠看到上方不遠處有一個 sscanf 函數調用
20191107194023.png加密

這個函數將咱們輸入的序列號轉化爲長整型的十六進制,分別保存在地址 12EF70、12EF7四、12EF7八、12EF7C 處。
20191107193956.pngspa

因此輸入的序列號要分爲四部分,每部分使用空格或 "-" 分割。若是輸入的序列號不是四部分,就會提示錯誤。
20191107194142.png插件

程序流程

在分析算法以前,先將程序的流程講明白。code

  1. 程序首先取得輸入的 name,而後將 name 翻轉,拼接到 name 的後面,而後將 ProductID 和 RegisteredOwner 依次拼接到後面。這兩項都是從註冊表中取得的。若是 RegisteredOwner 不存在,則拼接兩個 ProductID。若是這兩項都不在,就會再拼接兩個翻轉的 name,造成一正三反的形式。
  2. 程序將拼接後的用戶名計算出 md5。而後取得輸入 serial,通過函數計算。最後將計算出值進行比較,相等則經過驗證。
  3. 也就是 MD5(name) = F(serial),則經過比較。咱們的目的就是來逆這個函數 F。

算法分析

接下來是程序的關鍵了。由於下面要對咱們輸入的序列號進行處理。blog

外層循環

程序首先創建循環,循環次數爲 3次。
在循環中,程序調用函數401B90,這個函數是真正的處理函數。
20191107194959.pngmd5

函數401B90

函數的參數有兩個。
第一個參數是序列號部分1的地址,也就是 12EF70;
第二個參數是 0xBADCODE / (0x50 + i) 的商,i 是 esi 的值。
20191107200036.png

接下來進入到函數內部。
首先是賦初值部分
20191107200800.png

接下里是循環部分

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>

最後是結尾
20191107203717.png
能夠看到,最後將 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。

  • esi1 = C | eax。又由於最初 eax = esi,因此如今 esi1 的值就是 esi 的值左移1位,而後最低位和 C 作或運算獲得的值。
  • edi1 的值就是 edi 左移一位後的值。但由於是左移,因此 esi 的最高位 bit 位移到了 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
  1. 首先,eax 等於 esi 與 0x4 作 and 運算,因此 eax 此時的值除了第3 位外全爲 0,也就是形如 00000000 00000000 00000000 00000?00 的形式。
  2. 將 edx 置 0。
  3. 將 eax 和 edx 繼續左移,分別左移了 11 位、18 位。此時 eax 中惟一不爲 0 的 bit 位移到了第 32 位,也就是 eax 的頭部,此時 eax 形如 ?0000000 00000000 00000000 00000000。edx 還爲 0。
  4. 最後一次左移,eax 第 32 位的 bit 左移到了 edx 中。此時 eax 全爲 0,edx 形如 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 的呢?

  1. esi 左移一位,最低位與 edi 的最高位作或運算,便可獲得 esi1。
  2. esi1 ^ eax 便可獲得 esi2,可是 eax 一定爲 0,因此 esi1 等於 esi2。

edi 是如何變成 edi2 的呢?

  1. edi 左移一位,即爲 edi1,此時 edi1 的最低位是 esi 的最高位。
  2. edi1 ^ edx 便可獲得 edi2。由於 edx 一直爲 0,在最後由於 eax 的左移最後 edx 纔不爲 0。因此 edx 的值爲
edx = (esi1 & 0x4 >> 2) ^ (esi1 &  0x2000 >>13) ^ (esi1 & 0x80000000 >>31)

那麼咱們有了 esi2 和 edi2,如何獲得 esi 和 edi 呢?

  1. 由於 esi2 = esi1 ,因此咱們直接就能獲得 esi1。
  2. 由於 edi1 ^ edx = edi2,因此 edi2 ^ edx = edi1。由於已經有了 esi1,因此 edx 的值能夠經過上面的公式計算出來。
  3. 取 esi1 的最低位 DH (這是 edi 的最高位)。
  4. 取 edi2 的最低位 AH (這是 esi 的最高位)。
  5. 將 esi1 右移一位,將 AH 補上最高位,獲得 esi。
  6. 將 edi1 右移一位,將 DH 補上最高位,獲得 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

20191108090939.png

在 Win10 中,生成的 MD5 爲 0x0000C9B8, 0x37D4BC64, 0x1D98AF82, 0xDBF464AA,對應的序列號爲 2FCEE274 EBC4892B 5EB25588 232790EB

20191108092138.png

相關文件在個人 Github

相關文章
相關標籤/搜索