(接上文)
现在,我们知道v8绝对是一个类。接下来,让我们暂时离开这里,回到50B9A2,考察一下50B91E函数:
_BYTE *__stdcall sub_50B91E(int a1, int a2, unsigned int a3)
{
unsigned int v3; // ecx@1
unsigned int v4; // esi@1
_BYTE *result; // eax@2
v3 = 0;
v4 = 0;
if ( a3 )
{
do
{
result = (_BYTE *)(v4 + a2);
*result ^= *(_BYTE *)(a1 + v3++ + 116);
if ( v3 >= 0x1000 )
v3 = 0;
++v4;
}
while ( v4 < a3 );
}
return result;
}
首先,让我们重命名最后两个参数,因为我们已经知道它们是什么:
_BYTE *__stdcall sub_50B91E(int a1, int pDestBuffer, unsigned int uiSize)
{
unsigned int v3; // ecx@1
unsigned int v4; // esi@1
_BYTE *result; // eax@2
v3 = 0;
v4 = 0;
if ( uiSize )
{
do
{
result = (_BYTE *)(v4 + pDestBuffer);
*result ^= *(_BYTE *)(a1 + v3++ + 116);
if ( v3 >= 0x1000 )
v3 = 0;
++v4;
}
while ( v4 < uiSize );
}
return result;
}
在这里,最重要的一行代码是:
*result ^= *(_BYTE *)(a1 + v3++ + 116);
我们知道,XOR通常用于加密/解密过程。如您所见,它会逐字节地处理位于a1+116+v3处的数组,并对该数组的加密缓冲区中的每个字节都进行异或操作。如果v3等于4096(0x1000h),则将其重置为0。XOR处理将持续到v4(在每次迭代结束时都会递增)大于或等于参数uiSize时为止。
看上去,这可能有点难以理解,不过不要担心,我们可以借助于动态分析工具来搞定它。
使用OllyDbg进行动态分析
首先,在OllyDbg中加载pbclient.exe,然后,单击“E”按钮并选择“pbclient.exe”模块,以确保我们处于正确的模块中。
我们将在CreateFileA调用(还记得吗?它位于0x52AE36中)的第一个参数处设置一个断点,它实际上就是我们的文件名。
(注意:设置断点后不要删除,我们以后还会需要它们)
它位于0x52AE82处:
让我们现在就开始动手吧!如果它崩溃了,建议先下载Stealth64插件,如果您使用的是64位Windows 7系统的话。同时,我们还启用了除“Misc”部分之外的所有选项。
如果一切顺利,它现在将会在图示中的位置中断:
我们已经知道,它将使用anim.bus(同时,我们也得到了它使用这个函数的确认)。让我们在调用MapViewOfFile之后立即设置另一个断点,以获取映射视图的地址(返回值通常存储在EAX中):
(顺便说一句,一定要确保Anim.bus没有在任何地方打开,否则,它可能会失败!)
如果成功,映射视图的地址将存放到EAX寄存器中,因此,请在“Registers”窗口中右键单击它,然后,单击“Follow in Dump”选项:
让我们直接转到0x50B91E(执行XOR的地方),并在那里设置另一个断点,然后恢复游戏从而再次触发断点。
使用F8单步跟踪指令,直到:
0050B931 |. 8A540A 74 |MOV DL,BYTE PTR DS:[EDX+ECX+74] ; 74h => 114
这里,ECX用作数组的计数器。这里,它会与EDX的值相加,而EDX就是a1(记住,a1是一个类)。然后,加上距离数组开头的偏移量(74h/114)。
下面给出更加易于理解的伪代码:
uint8_t DL = a1->xor_array[ECX]; //xor_array starts 74h into a1, and ECX is incremented after each iteration
我们来看看下面一行代码:
0050B935 |. 03C6 |ADD EAX,ESI
EAX保存的是目标缓冲区的地址。它也会在每次迭代结束时递增,直到“uiSize”字节被异或为止。
之后,继续在转储中跟踪EAX,并在循环(POP ESI)指令后设置断点,恢复游戏并查看转储窗口:
它好像已经解密了缓冲区中的内容!
我们跳过33个字节,并解密了272个字节的内容。但是如果我们仔细查看Data文件夹中的Anim.bus,会发现文件Anim.bus的大小是305个字节。而272+33正好等于……
305!这说明整个Anim.bus文件都被解密了。
那么,解密缓冲区后,我们能够得到哪些有用的信息呢?不多,只不过看起来更像个文件夹名称而已。
接下来,我们可以编写一个简单应用程序,让它执行与游戏完全相同的操作,即使用CreateFile、OpenFileMapping和MapViewOfFile将文件映射到内存,并解密缓冲区中的内容(记住,起始位置是缓冲区地址+33个字节!)。要想完成解密,还需要获取用作XOR密钥的字节数组中的内容,该数组长度为4096字节(4KB)。为此,需要重新关闭并运行该游戏,并触发该指令:
0050B931 |. 8A540A 74 |MOV DL,BYTE PTR DS:[EDX+ECX+74] ; 74h => 114
读取EDX的值,加上114,即数组开头的地址。在转储窗口中跟踪它,将该地址加上4096,即数组末尾的地址。之后,只需复制这两个地址之间的所有字节,并利用Notepad++格式化所有内容即可。
如果在此过程中遇到麻烦,可以访问下列地址:
· http://pastebin.com/skZKAx27
下面给出解密函数:
bool DecryptData(uint8_t* puiBuffer, const size_t uiSize)
{
if (!uiSize)
{