要看懂这些代码,要学什么。
pac文件:首先游戏总是忽略文件开始的2字节,在读取了之后的0x3fc个字节(这里以0x前缀来表示数字为16进制的,无前缀则是10进制)后,用以下代码进行了处理(代码用蓝色表示)10006815 0FBE4424 13 movsx eax, byte ptr [esp+13]
1000681A 03C0 add eax, eax
1000681C 03C0 add eax, eax
1000681E 0FB74C04 2A movzx ecx, word ptr [esp+eax+2A]
10006823 66:85C9 test cx, cx
10006826 8D7404 2A lea esi, dword ptr [esp+eax+2A]
1000682A 897424 14 mov dword ptr [esp+14], esi
1000682E 0F84 2C010000 je 10006960
10006834 0FB74404 28 movzx eax, word ptr [esp+eax+28]
10006839 0FB7D9 movzx ebx, cx
1000683C D1EB shr ebx, 1
1000683E 03C3 add eax, ebx
10006840 8D0480 lea eax, dword ptr [eax+eax*4]
10006843 03C0 add eax, eax
10006845 03C0 add eax, eax
10006847 03C0 add eax, eax
10006849 8DA424 00000000 lea esp, dword ptr [esp]
10006850 99 cdq
10006851 8BCA mov ecx, edx
10006853 6A 01 push 1
10006855 895424 20 mov dword ptr [esp+20], edx
10006859 8D5424 20 lea edx, dword ptr [esp+20]
1000685D 52 push edx
1000685E 50 push eax
1000685F 57 push edi
10006860 C1F9 1F sar ecx, 1F
10006863 FF15 ACD00610 call dword ptr[<&KERNEL32.SetFilePointer>
10006869 6A 00 push 0
1000686B 8D4424 1C lea eax, dword ptr [esp+1C]
1000686F 50 push eax
10006870 6A 20 push 20
10006872 8D8C24 34040000 lea ecx, dword ptr [esp+434]
10006879 51 push ecx
1000687A 57 push edi
1000687B FF15 C0D00610 call dword ptr [<&KERNEL32.ReadFile>]
10006881 8B5424 20 mov edx, dword ptr [esp+20]
10006885 B8 20000000 mov eax, 20
1000688A 8D8C24 28040000 lea ecx, dword ptr [esp+428]
10006891 8B2A mov ebp, dword ptr [edx]
10006893 3B29 cmp ebp, dword ptr [ecx]
10006895 75 12 jnz short 100068A9
10006897 83E8 04 sub eax, 4
1000689A 83C1 04 add ecx, 4
1000689D 83C2 04 add edx, 4
100068A0 83F8 04 cmp eax, 4
100068A3 ^ 73 EC jnb short 10006891
100068A5 85C0 test eax, eax
100068A7 74 65 je short 1000690E
100068A9 0FB629 movzx ebp, byte ptr [ecx]
100068AC 0FB632 movzx esi, byte ptr [edx]
100068AF 2BF5 sub esi, ebp
100068B1 75 45 jnz short 100068F8
100068B3 83E8 01 sub eax, 1
100068B6 83C1 01 add ecx, 1
100068B9 83C2 01 add edx, 1
100068BC 85C0 test eax, eax
100068BE 74 4A je short 1000690A
100068C0 0FB629 movzx ebp, byte ptr [ecx]
100068C3 0FB632 movzx esi, byte ptr [edx]
100068C6 2BF5 sub esi, ebp
100068C8 75 2E jnz short 100068F8
100068CA 83E8 01 sub eax, 1
100068CD 83C1 01 add ecx, 1
100068D0 83C2 01 add edx, 1
100068D3 85C0 test eax, eax
100068D5 74 33 je short 1000690A
100068D7 0FB629 movzx ebp, byte ptr [ecx]
100068DA 0FB632 movzx esi, byte ptr [edx]
100068DD 2BF5 sub esi, ebp
100068DF 75 17 jnz short 100068F8
100068E1 83E8 01 sub eax, 1
100068E4 83C1 01 add ecx, 1
100068E7 83C2 01 add edx, 1
100068EA 85C0 test eax, eax
100068EC 74 1C je short 1000690A
100068EE 0FB601 movzx eax, byte ptr [ecx]
100068F1 0FB632 movzx esi, byte ptr [edx]
100068F4 2BF0 sub esi, eax
100068F6 74 12 je short 1000690A
100068F8 85F6 test esi, esi
100068FA 8B7424 14 mov esi, dword ptr [esp+14]
100068FE B8 01000000 mov eax, 1
10006903 7F 0B jg short 10006910
10006905 83C8 FF or eax, FFFFFFFF
10006908 EB 06 jmp short 10006910
1000690A 8B7424 14 mov esi, dword ptr [esp+14]
1000690E 33C0 xor eax, eax
10006910 85C0 test eax, eax
10006912 74 6F je short 10006983
10006914 85DB test ebx, ebx
10006916 74 05 je short 1000691D
10006918 66:291E sub word ptr [esi], bx
1000691B EB 05 jmp short 10006922
1000691D 66:8106 FFFF add word ptr [esi], 0FFFF
10006922 0FB70E movzx ecx, word ptr [esi]
10006925 0FB7D9 movzx ebx, cx
10006928 D1EB shr ebx, 1
1000692A 85C0 test eax, eax
1000692C 7D 12 jge short 10006940
1000692E 85DB test ebx, ebx
10006930 74 07 je short 10006939
10006932 8BC3 mov eax, ebx
10006934 6BC0 D8 imul eax, eax, -28
10006937 EB 1B jmp short 10006954
10006939 B8 D8FFFFFF mov eax, -28
1000693E EB 14 jmp short 10006954
10006940 85DB test ebx, ebx
10006942 74 0B je short 1000694F
10006944 8D049B lea eax, dword ptr [ebx+ebx*4]
10006947 03C0 add eax, eax
10006949 03C0 add eax, eax
1000694B 03C0 add eax, eax
1000694D EB 05 jmp short 10006954
1000694F B8 28000000 mov eax, 28
10006954 83E8 20 sub eax, 20
10006957 66:85C9 test cx, cx
1000695A ^ 0F85 F0FEFFFF jnz 10006850
10006960 57 push edi
10006961 FF15 38D10610 call dword ptr [<&KERNEL32.CloseHandle>
10006967 83C8 FF or eax, FFFFFFFF
1000696A 8B8C24 34050000 mov ecx, dword ptr [esp+534]
10006971 5F pop edi
10006972 5E pop esi
10006973 5D pop ebp
10006974 5B pop ebx
10006975 33CC xor ecx, esp
10006977 E8 96940100 call 1001FE12
1000697C 81C4 28050000 add esp, 528
10006982 C3 retn
10006983 8B5C24 24 mov ebx, dword ptr [esp+24]
10006987 8B2D C0D00610 mov ebp, dword ptr [<&KERNEL32.ReadFile>
1000698D 6A 00 push 0
1000698F 8D4C24 1C lea ecx, dword ptr [esp+1C]
10006993 51 push ecx
10006994 6A 04 push 4
10006996 8D73 08 lea esi, dword ptr [ebx+8]
10006999 56 push esi
1000699A 57 push edi
1000699B FFD5 call ebp
1000699D 6A 00 push 0
1000699F 8D5424 1C lea edx, dword ptr [esp+1C]
100069A3 52 push edx
100069A4 6A 04 push 4
100069A6 53 push ebx
100069A7 57 push edi
100069A8 FFD5 call ebp
100069AA 8B03 mov eax, dword ptr [ebx]
100069AC 8B0E mov ecx, dword ptr [esi]
100069AE 03C8 add ecx, eax
100069B0 894B 04 mov dword ptr [ebx+4], ecx
100069B3 33C9 xor ecx, ecx
100069B5 51 push ecx
100069B6 8BD1 mov edx, ecx
100069B8 894C24 20 mov dword ptr [esp+20], ecx
100069BC 8D4C24 20 lea ecx, dword ptr [esp+20]
100069C0 51 push ecx
100069C1 50 push eax
100069C2 57 push edi
100069C3 C1FA 1F sar edx, 1F
100069C6 FF15 ACD00610 call dword ptr[<&KERNEL32.SetFilePointer>
100069CC 8BC7 mov eax, edi
100069CE ^ EB 9A jmp short 1000696A
004012CC |> \8B96 C4FA0B00 mov edx, dword ptr [esi+BFAC4]
004012D2 |. 6A 00 push 0
004012D4 |. 8D4C24 08 lea ecx, dword ptr [esp+8]
004012D8 |. 51 push ecx
004012D9 |. 52 push edx
004012DA |. 50 push eax
004012DB |. 57 push edi
004012DC |. FF15 ACC04200 call dword ptr [<&KERNEL32.ReadFile>]
以上代码大致实现了这样一个功能:在pac文件里搜索接下来需要使用的资源文件。
10006815 — 1000682E之间的代码意思:将需使用的资源文件名的首字符编码值(1字节)乘以4后再加2,之后在前面读进来的0x3fc个字节中,将此值作为其起始偏移大小并从那里取出2字节的数据(设其为X),如果X为0则说明游戏想要的资源肯定不在这个pac文件中。
10006834 — 1000687B之间的代码意思:将首字符编码值乘以4后作为其偏移大小并从那里取出2字节的数据(也就是刚才取出数据的前两字节,设其为Y),计算出(X / 2 + Y) * 40的值(设其为Z)。之后将Z作为偏移大小接着从pac文件中读取32字节的数据。
10006881 — 100069CE之间的代码意思:比较这32字节的数据是否与需使用的资源文件名相等,不相等则每次将X除2后重新向前读取pac文件中的32个字节数据,相等则从pac文件中读取接着那32个字节数据后的8字节(分成2组,每组4字节,设为A,B)。
004012CC — 004012DC之间的代码意思:以B作为起始偏移从pac文件中读取A个字节数据,到此算是找到资源了……
从以上分析可以发现:首先是那0x3fc个字节,从游戏的搜索方式来看为4字节一组,每组又分成2个,前两字节(仍设其为Y)表示某类资源在此pac文件中的序号,后两字节(仍设其为X)表示此类资源的数量。
由于游戏使用了资源文件名的首字符,于是“某类资源”就是以同一首字符为开头的资源。再将0x3fc除4得0xff,等于1个字节的最大值,通过游戏的搜索方式可以推测出在这0x3fc个字节中,第一组就是以编码值为0作为资源首字符的资源信息,之后以此类推。
通过观察多个pac文件发现,文件开头2字节始终与0x3fc个字节中的最后一组的Y相等,而最后一组的Y是由上一组的X + Y得到的,以此类推可得出文件开头2字节就是此pac文件中的资源总数。
又由于游戏将所需的资源名与0x3fc后的某段32字节数据比较,并且利用了后8字节(仍分别设为A,B)找到了资源,可得出某段32字节数据为一个资源的文件名,A就是此资源文件的大小,B就是此资源文件在pac文件中的位置。则共有N段数据(N为资源总数),每段40字节。
剩下的就是各资源的数据本体了,当然最后我们发现pac文件使用“EOF ”作为封包的结尾标志。