聖天諾的虛擬機器保護方式:關於那個巨大的二叉樹。。。。。 (5千字)

看雪資料發表於2015-11-15

聖天諾的虛擬機器保護方式:關於那個巨大的二叉樹。。。。。

先說明一個結構:
CallStruct
{    WORD    key1;
    WORD    key2;
    DWORD    code;
};
這個結構共 8 個位元組長,程式開始部分即取自該結構中的資料進行分析,從而進入不同的模組部分。
程式碼如下:

:004119A6 A194754000              mov eax, dword ptr [00407594]        <---注:地址 00407594 中儲存的就是 CallStruct 結構的指標
:004119AB 8B0D94754000            mov ecx, dword ptr [00407594]
:004119B1 668B00                  mov ax, word ptr [eax]            <---取 key1
:004119B4 66A3A0754000            mov word ptr [004075A0], ax
:004119BA 668B5102                mov dx, word ptr [ecx+02]            <---取 key2
:004119BE 668915A4754000          mov word ptr [004075A4], dx
:004119C5 8B4104                  mov eax, dword ptr [ecx+04]        <---取 code
:004119C8 A398754000              mov dword ptr [00407598], eax
:004119CD 011D94754000            add dword ptr [00407594], ebx
:004119D3 33C0                    xor eax, eax
:004119D5 66A1A0754000            mov ax, word ptr [004075A0]
:004119DB 3D42030000              cmp eax, 00000342                    <---判斷 key1 從而進入不同的模組部分
:004119E0 7F13                    jg 004119F5
:004119E2 0F84C6010000            je 00411BAE
:004119E8 3DE4000000              cmp eax, 000000E4
:004119ED 0F8481010000            je 00411B74
:004119F3 EBB1                    jmp 004119A6

從以上可以看到 key1 用作代表不同的模組。進一步分析程式,可以分析得出其使用 00407590 、00407594 、00407598 這三個地址來用作資料傳遞的地址。以後我將其簡稱為 A 、B 、C 。

key1 的作用知道了,那麼來了解一下 key2 的作用。分析可以發現 key2 是在某些模組內部(也就是說並非所有模組)用作取值方式的。
下面以分析 004116D0 處的模組為例:

其呼叫形式為:    CALL_004116D0(var1,var2)
當 key2 = 68 時,程式實現功能為: 返回 [var1] 的 Byte 位元組。(注: [var1] 表示取 var1 地址中的值,以下類同)
當 key2 = D4 時,程式實現功能為: 返回 [[407590] + 4*var1] 的 DWORD 位元組。
                (注:即 取407590地址中的值,然後加上4乘以var1的和,再取以"和"為地址的值,返回)

下面列舉幾個模組和 key2 的組合及相應實現的功能:

key2    模組名1                            模組名2                                模組名3
        4116D0                            411670                                411E1D
        功能:返回 eax                    功能:賦值                            功能:判斷某些位是否不為0
68         byte [var1]                    [var1] <=  byte var3                if( [[407590]] & 0x000000FF <> 0 ) ok!
D4        dword [var1 * 4 + [407590]]        [var1 * 4 + [407590]] <= var3        if( [[407590]]              <> 0 ) ok!
234        dword [var1]                    [var1] <= dword var3                if( [[407590]]              <> 0 ) ok!
28E         word [var1]                    [var1] <=  word var3                if( [[407590]] & 0x0000FFFF <> 0 ) ok!
3D5            var1                        直接返回                            if( [[407590]]              <> 0 ) ok!

下面再來具體分析一下各個模組的功能及作用,列舉如下:
為簡化表達,把 4116D0 、411670  、411E1D 分別命名為 f1( )  、f2( ) 、f3( )
key1    模組起始地址    模組功能
0611    411BCA            類似 411E1D 模組,也會根據key2的值實現判斷功能。只是 <> 變為 == 判斷即可。
0704    411C1B            f2([407598],key2,f1([407598],key2))
1092    411C4F            [[407590]] += f1([407598],key2)
1109    411C72            [[407590]+4] = call_407598([[407590]+4],[[407590]]) , [407590] += 4
1992    411C9B            f2([407598],key2,[[407590]]) , [407590] += 4
1A44    411CC4            [407590] -= 4, [[407590]] = call_407598()
22B7    411D00            [[407590]] -= f1([407598],key2)
3345    411D23            [[407590]] &= f1([407598],key2)
5910    411FBA            [407594] <= [407598]
654D    411D9D            [[407590]] |= f1([407598],key2)
698A    411E00            f2([407598],key2,0)
7123    411E1D            判斷
74BB    411E6E            [[407590]] ^= f1([407598],key2)    
9104    411ED1            [[407590]] *= f1([407598],key2)
AA61    411F2D            [[407590]+C] = call_407598([[407590]+C],[[407590]+8],[[407590]+4],[[407590]]), [407590] += 0xC
B323    411F5F            [[407590]+8] = call_407598([[407590]+8],[[407590]+4],[[407590]]), [407590] += 8    還原演算法
BA51    411F8C            [[407590]] = call_407598([407590])
CDD3    411FC9            [[407590]+edi] = [407598] = f1([407598],key2)

歸納可以發現如下:

1、判斷模組,分別實現不等跳轉和相等跳轉。
0611    411BCA            類似 411E1D 模組,也會根據key2的值實現判斷功能。只是 <> 變為 == 判斷即可。
7123    411E1D            判斷

2、四則運算模組,分別實現加、減、乘、等運算。
74BB    411E6E            [[407590]] ^= f1([407598],key2)    
9104    411ED1            [[407590]] *= f1([407598],key2)
1092    411C4F            [[407590]] += f1([407598],key2)
22B7    411D00            [[407590]] -= f1([407598],key2)
3345    411D23            [[407590]] &= f1([407598],key2)
654D    411D9D            [[407590]] |= f1([407598],key2)

3、賦值模組,分別實現不同方式的賦值。
698A    411E00            f2([407598],key2,0)
0704    411C1B            f2([407598],key2,f1([407598],key2))
1992    411C9B            f2([407598],key2,[[407590]]) , [407590] += esi

4、呼叫函式模組,分別實現不同引數方式函式呼叫。
1A44    411CC4            [407590] += edi, [[407590]] = call_407598()
BA51    411F8C            [[407590]] = call_407598([407590])
1109    411C72            [[407590]+4] = call_407598([[407590]+4],[[407590]]) , [407590] += esi
AA61    411F2D            [[407590]+C] = call_407598([[407590]+C],[[407590]+8],[[407590]+4],[[407590]]), [407590] += 0xC
B323    411F5F            [[407590]+8] = call_407598([[407590]+8],[[407590]+4],[[407590]]), [407590] += ebx    還原演算法

5、其他,實現資料的傳遞和控制程式流程。
CDD3    411FC9            [[407590]+edi] = [407598] = f1([407598],key2)
5910    411FBA            [407594] <= [407598]

最後說明一下:
這是以前自己分析聖天諾外殼時整理的一些記錄。那個二叉樹的各個分支就是用於識別各個偽機器碼的。上面分析的就是各個偽機器碼的作用,以及偽機器碼的格式的。這個偽處理機只使用在聖天諾的最外層結構上,而進入核心部分還是和我們常見的程式沒有什麼區別。

如果你不瞭解聖天諾的這個偽處理機,那麼從某些方面來說防止了暴破,因為我們常常會從彈出的對話方塊入手來找到程式的關鍵暴破點,但是這個殼中使用的偽處理機的判斷部分是在偽處理機的一個用於進行"判斷"的模組(實際上就是一條偽機器碼)中,如果你不明白這其中的道理,把這個"判斷"模組中的"JZ"語句修改了,就相當於把整個偽處理機中的"判斷"模組修改了一樣,想象一下如果我們把程式中所有的"JZ"指令改成"JNZ"的後果,這有些類似這個道理。

當然,也有有趣的地方,在其中一個早期版本的聖天諾外殼中,倒是程式中只使用了"JZ"判斷模組,如果你把這個"判斷"模組修改一下,會出現什麼後果?這樣偽處理機反而使暴破省事了。

相關文章