• 标 题:圣天诺的虚拟机保护方式:关于那个巨大的二叉树。。。。。 (5千字)
  • 作 者:ljtt
  • 时 间:2002-7-7 16:14:19
  • 链 接:http://bbs.pediy.com

圣天诺的虚拟机保护方式:关于那个巨大的二叉树。。。。。

先说明一个结构:
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"判断模块,如果你把这个"判断"模块修改一下,会出现什么后果?这样伪处理机反而使暴破省事了。