-
-
[原创]游戏《chaos;head》资源解密算法逆向不完整版
-
发表于:
2009-5-17 20:30
10591
-
[原创]游戏《chaos;head》资源解密算法逆向不完整版
首先,我要解释一下为什么这篇逆向分析的题目中有“不完全”这几个字。因为偶是一只小菜鸟,水平有限,而这个游戏的加密方式异常复杂,因此偶未能逆出最后一步的解密过程。只能将前面的解密过程分析写出来。所以请各位看官见谅……
游戏的资源文件以npa以及ngs后缀名结尾,其中ngs猜测应该是游戏的对白内容的文件。而npa文件中有cg.npa(cg资源文件),sound.npa以及voice.npa(声音文件),system.npa(系统设置文件)以及nss.npa(脚本资源文件)。而一般游戏脚本在解密完后都能明显地看出来的,于是我就找nss.npa来开刀了。
游戏使用了AlphaDisc加密,这个保护是以外壳的形式将游戏保护起来的。这个外壳有检测调试器(打开\\.\SICE以及\\.\TRW等驱动来检查,查找窗口名称等)的功能,甚至运行FileMon跟RegMon这些监视软件游戏都不能启动。并且外壳有完整性校验(不太确定,因为免cd补丁中只用了3个字节来修改了一个跳转,而游戏却可以运行)。顺带还破坏了程序的输入表了。
用OD载入游戏的时候要在调试选项忽略掉所有异常,然后对CreateFileA下断,一直按F9运行到打开的文件是nss.npa文件为止:
void deresourcename(char *name,//name指针指向要解密的字符串
BYTE len,//字符串长度
DWORD a,//传入的是在文件读出的数据,此处是0x00013a14
DWORD b,//传入的是在文件读出的a值后一个双字,此处为0x00016b12
DWORD num//资源序号
)
{
int temp1,temp3;
BYTE t2,t4;
BYTE i,j;
for(i=0;i<len;i++)
{
temp1=a; //temp1=0x00013a14
temp1*=b; //temp1*=0x0016b12
t2=i;
t2*=0xfc;
for(j=3;j>=0;j--)
{
temp3=temp1;
temp3>>=j*8;
t2-=(char)temp3;
}
t4=(char)a; //t4=0x14;
t4*=(char)b; //t4*=0x12;
t2-=t4;
temp1=num;
for(j=3;j>=0;j--)
{
temp3=temp1;
temp3>>=j*8;
t2-=(char)temp3;
}
name[i]+=t2;
}
}
DWORD namehash(char *name)
{
DOWRD temp=0x87654321,i=0;
while(name[i])
{
*(char*)&temp-=name[i];
i++;
}
return i*temp;
}
01364518 00 00 00 00 00 00 00 00 11 00 11 00 81 01 08 01 ........ . .?
01364528 B0 45 36 01 02 00 00 00 01 00 00 00 01 00 00 00 癊6 ... ... ...
01364538 16 04 00 00 9F CA 3F 49 00 00 00 00 FF 23 00 00 ..熓?I....#..
01364548 16 04 00 00 2F 1A 00 00 5E 30 00 00 29 79 EE EE ../ ..^0..)y铑
01364558 18 44 36 01 38 46 36 01 00 00 00 00 00 00 00 00 D6 8F6 ........
01364568 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01364578 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01364588 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01364598 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
013645A8 11 00 11 00 90 01 08 01 62 6F 6F 74 5F 91 E6 88 . .? boot_戞
013645B8 EA 8F CD 2E 6E 73 73 00 00 00 00 00 00 00 00 00 陱?nss.........
013645C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
013645D8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
013645E8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
013645F8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01364608 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01364618 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01364628 00 00 00 00 00 00 00 00 11 00 11 00 E3 01 08 01 ........ . .?
01364638 C0 46 36 01 02 00 00 00 01 00 00 00 01 00 00 00 繤6 ... ... ...
01364648 F1 03 00 00 82 D5 1A 91 00 00 00 00 15 28 00 00 ?..傉 ?... (..
01364658 F1 03 00 00 59 1A 00 00 5E 30 00 00 EA 7B EE EE ?..Y ..^0..陒铑
01364668 28 45 36 01 48 47 36 01 00 00 00 00 00 00 00 00 (E6 HG6 ........
01364678 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01364688 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01364698 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
013646A8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
013646B8 11 00 11 00 F2 01 08 01 62 6F 6F 74 5F 91 E6 8E . .? boot_戞
013646C8 B5 8F CD 2E 6E 73 73 00 00 00 00 00 00 00 00 00 祻?nss.........
struct ResourceHeader{
DWORD nameoffset; //文件名在内存中的地址
DWORD resourcetype; //不确定,在解密过程中读出的第一个字节数据
DWORD unknown1; //第一次读出文件的0x29个字节中的第0x10个
DWORD unknown2; //第一次读出文件的0x29个字节中的第0xf个
DWORD unknownsize; //与从文件中读出的数据有关,具体作用不详
DWORD key1; //最后一个变换得出的数值,用于后面解密
DWORD unknown4; //解密过程中读出的第一个双字的数据
DWORD dataRVA; //数据相对文件头的偏移,解密过程中读出的第二个双字的数据
DWORD sizeoffile; //资源文件的实际大小,解密过程中读出的第三个双字的数据
DWORD unknown5; //解密过程中读出的最后一个双字
DWORD sizeofheader; //所有文件头的大小
DWORD hash; //资源名的hash值
ResourceHeader *Flink; //指向前继节点
ResourceHeader *Blink; //指向后继节点
};
{
char *buff;
char *key=0x51ecf8;
char tmp;
int i;
for(i=0;i<unknownsize;i++)
{
tmp=key[buff[i]];
tmp-=(char)key1; //key1为文件头中的数据
tmp-=i;
buff[i]=tmp;
}
}
004010CE |. 6A 00 |push 0
004010D0 |. 6A 01 |push 1
004010D2 |. 6A 03 |push 3
004010D4 |. 6A 00 |push 0
004010D6 |. 6A 00 |push 0
004010D8 |. 68 00000080 |push 80000000
004010DD |. 8D8424 D40300>|lea eax, dword ptr [esp+3D4]
004010E4 |. 50 |push eax ; E:\Program Files\Nitroplus\CHAOS;HEAD\nss.npa
004010E5 |. FF15 F0004E00 |call dword ptr [4E00F0] ; CreateFileA
004010EB |. 8BF8 |mov edi, eax
004010ED |. 83FF FF |cmp edi, -1
004010F0 |. 897C24 14 |mov dword ptr [esp+14], edi
004010F4 |. 0F84 19020000 |je 00401313
004010FA |. 6A 29 |push 29
004010FC |. 8D4C24 20 |lea ecx, dword ptr [esp+20]
00401100 |. 6A 00 |push 0
00401102 |. 51 |push ecx
00401103 |. E8 A8E00B00 |call 004BF1B0
00401108 |. 83C4 0C |add esp, 0C
0040110B |. 6A 00 |push 0 ; lpOverlapped
0040110D |. 8D5424 1C |lea edx, dword ptr [esp+1C]
00401111 |. 52 |push edx ; pBytesRead
00401112 |. 6A 29 |push 29 ; BytesToRead
00401114 |. 8D4424 28 |lea eax, dword ptr [esp+28]
00401118 |. 50 |push eax ; Buffer
00401119 |. 57 |push edi ; hFile
0040111A |. FF15 88014E00 |call dword ptr [4E0188] ; ReadFile
00401120 |. 85C0 |test eax, eax
00401122 |. 0F84 E4010000 |je 0040130C ; 读取不成功则返回
00401128 |. 837C24 18 29 |cmp dword ptr [esp+18], 29
0040112D |. 0F85 D9010000 |jnz 0040130C ; 读出字节不为29则返回
0012EC94 4E 50 41 01 00 00 00 14 3A 01 00 12 6B 01 00 01 NPA ... : . k .
0012ECA4 01 FD 00 00 00 00 00 00 00 FD 00 00 00 00 00 00 ?......?.....
0012ECB4 00 00 00 00 00 35 30 00 00 .....50..
0040190C |> \8B8424 180100>mov eax, dword ptr [esp+118] ; 取文件句柄
00401913 |. 53 push ebx
00401914 |. 55 push ebp
00401915 |. 57 push edi
00401916 |. 6A 00 push 0 ; dwMoveMethod
00401918 |. 6A 00 push 0 ; lpDistanceToMoveHigh
0040191A |. 6A 29 push 29 ; DistanceToMove
0040191C |. 50 push eax ; hFile
0040191D |. FF15 78014E00 call dword ptr [4E0178] ; SetFilePointer
00401923 |. 837E 14 00 cmp dword ptr [esi+14], 0 ; 判断是否成功
00401927 |. C74424 14 000>mov dword ptr [esp+14], 0
0040192F |. 0F86 13030000 jbe 00401C48 ; 不成功则返回
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)