《同級生1》DMM版(即Original版)是2007年3月1日由DMM發行的《同級生》復刻版,似乎DMM發佈的elf遊戲都採用了新的系統,加密方式和原來的win版有所不同,使用目前網絡上流行的遊戲文件提取工具(如mlist3等)均無法提取本版本遊戲的文件,於是只能手動逆向調試進行文件提取。按照elf遊戲的一貫做法,腳本文件都被打包在mes.arc文件中。採用調試器OllyDBG v1.10,操作系統爲Windows XP。
關於od的基本使用方法不再贅述,這裏只記錄主要解密過程。
1、啓動調試器,運行AI5WIN.exe,給ReadFile函數下斷點:
然後按F9運行程序,直到ReadFile第一次斷下mes.arc:
此時可以記下句柄號。
2、繼續運行程序,直到直到ReadFile第二次斷下mes.arc,在數據窗口中查看Buffer的地址,並在第一個字節上設置內存訪問斷點:
繼續運行程序直到命中剛纔的內存訪問斷點,此時所在的代碼段就是對mes.arc的文件頭進行解密的代碼,其中包括了被打包的腳本文件的文件名、文件長度和在mes.arc中的偏移量,核心部分如下:
004019FD . B3 35 MOV BL,35
……
00401A10 > 309C08 FCFEFF>XOR BYTE PTR DS:[EAX+ECX-104],BL
00401A17 . 83C1 01 ADD ECX,1
00401A1A . 81F9 00010000 CMP ECX,100
……
00401A22 . 8170 FC 8B5F5>XOR DWORD PTR DS:[EAX-4],A25D5F8B
00401A29 . 8130 69A7FEA9 XOR DWORD PTR DS:[EAX],A9FEA769
……
這一段彙編沒有任何難度,首先是對文件名xor 0x35,最大長度0x100個字節,然後對文件長度xor A25D5F8B,最後對文件偏移xor A9FEA769。至於爲什麼能確定第一個是長度、第二個是偏移,這個只要觀察連續兩個文件的信息就能看出,偏移量的改變比較會小而且是遞增的。
3、繼續運行程序,直到直到ReadFile第三次斷下mes.arc,這次就是讀入具體的腳本文件了,遊戲啓動第一次讀取的文件是start.mes:
在數據窗口中查看Buffer的地址,並在第一個字節上設置內存訪問斷點,運行程序等待斷點命中,此時我們就找到了解密腳本文件的代碼。這段代碼比較複雜,本質上是個解壓縮的算法,我們到下一篇文章中再探討。
在寫解包器的程序時,我們可以直接把這段彙編拿過來用,不必一定改寫成高級語言的形式。在我寫的解包器中,核心解密函數的代碼如下:
DWORD Decode(void* srt, DWORD len, void* dst)
{
g_bufSrt = srt;
g_lenRaw = len;
g_bufDst = dst;
_asm
{
mov ebx, g_bufSrt;
xor eax, eax;
push ebp;
xor ebp, ebp;
xor esi, esi;
sub esp, 0x2000;
mov edi, esp;
mov ecx, 0x2000;
rep stos;
}
__asm
{
cmp g_lenRaw, eax;
mov edi, 0x0fee;
je ai5win_0042c404;
jmp short ai5win_0042c344;
jmp short ai5win_0042c340;
lea ecx, dword ptr ds:[ecx];
ai5win_0042c340:
mov eax, dword ptr ss:[esp+0x10];
ai5win_0042c344:
shr eax, 0x1;
test eax, 0x100;
mov dword ptr ss:[esp+0x10], eax;
jnz short ai5win_0042c363;
mov al, byte ptr ds:[esi+ebx];
movzx eax, al;
add esi, 0x1;
or eax, 0x0ff00;
mov dword ptr ss:[esp+0x10], eax;
ai5win_0042c363:
test byte ptr ss:[esp+0x10], 0x1;
je short ai5win_0042c38c;
mov al, byte ptr ds:[esi+ebx];
mov edx, g_bufDst;
movzx eax, al;
mov byte ptr ss:[esp+edi+0x1c], al;
mov byte ptr ds:[edx+ebp], al;
add edi, 0x1;
add esi, 0x1;
add ebp, 0x1;
and edi, 0x0fff;
jmp short ai5win_0042c3f5;
ai5win_0042c38c:
mov dl, byte ptr ds:[esi+ebx];
mov al, byte ptr ds:[esi+ebx+0x1];
add esi, 0x1;
movzx ecx, al;
mov eax, ecx;
and eax, 0x0f0;
movzx edx, dl;
shl eax, 0x4;
or eax, edx;
and ecx, 0x0f;
add esi, 0x1;
add ecx, 0x2;
mov dword ptr ss:[esp+0x18], ecx;
mov edx, 0x0;
js short ai5win_0042c3f5;
lea esp, dword ptr ss:[esp];
ai5win_0042c3c0:
mov ebx, g_bufDst;
lea ecx, dword ptr ds:[edx+eax];
and ecx, 0x0fff;
movzx ecx, byte ptr ss:[esp+ecx+0x1c];
mov byte ptr ss:[esp+edi+0x1c], cl;
add edi, 0x1;
mov byte ptr ds:[ebx+ebp], cl;
add edx, 0x1;
add ebp, 0x1;
and edi, 0x0fff;
cmp edx, dword ptr ss:[esp+0x18];
jle short ai5win_0042c3c0;
mov ebx, dword ptr g_bufSrt;
ai5win_0042c3f5:
cmp esi, g_lenRaw;
jnz ai5win_0042c340;
mov eax, ebp;
ai5win_0042c404:
mov ecx, dword ptr ss:[esp+0x101c];
}
__asm
{
add esp, 0x2000;
mov g_lenDecode, ebp;
pop ebp;
}
return g_lenDecode;
}
函數參數分別爲解密前數據的地址、長度和解密後數據的地址(要求事先分配好足夠空間),函數返回值爲解密後數據的長度。
在下一篇文章中會解釋這段彙編代碼的原理,以及如何設計封包算法。
轉載請註明來源及鏈接:未來代碼研究所 本文鏈接地址:http://blog.atelier39.org/rev_eng/615.html