首页
社区
课程
招聘
[原创]游戏《chaos;head》资源解密算法逆向不完整版
发表于: 2009-5-17 20:30 10592

[原创]游戏《chaos;head》资源解密算法逆向不完整版

2009-5-17 20:30
10592

首先,我要解释一下为什么这篇逆向分析的题目中有“不完全”这几个字。因为偶是一只小菜鸟,水平有限,而这个游戏的加密方式异常复杂,因此偶未能逆出最后一步的解密过程。只能将前面的解密过程分析写出来。所以请各位看官见谅……
游戏的资源文件以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期)

收藏
免费 7
支持
分享
最新回复 (13)
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
nice,很详细
2009-5-17 23:14
0
雪    币: 1319
活跃值: (2306)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
看看,好久没来顶贴了
2009-5-21 09:26
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
顶一下  最近忙 好久没过来看看了;发现变化挺大的
2009-5-21 09:28
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
好,有机会向你学习。谢
2009-5-21 16:49
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
樓主分析一下姫狩りダンジョンマイスター這個遊戲比較有趣,裡面的數值都有做加密,一般的修改軟體都無法改,很不錯玩,分析起來也比較有快感,現在我正在分析中,要不要一起討論一下
2009-5-21 19:33
0
雪    币: 50
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
有机会向你学习。谢 有机会向你学习。谢
2009-5-21 20:23
0
雪    币: 208
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
分析得这样子,我不知道什么时候才能达到这个能力。
2009-5-21 20:40
0
雪    币: 445
活跃值: (25)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
9
orz,4月底的几个游戏偶就下了magisiki来(因为喜欢mitha的人设- -|||)结果花了一个星期通了两条线发现是个雷,于是想下姫狩都差不多过期了orz

听说姫狩是不能用金山游侠修改的,原来是数据被加密了么……不过绯月上好像有强人将修改器做出来了
2009-5-22 00:17
0
雪    币: 348
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
分析的很详细,向楼主学习。谢谢
2009-5-22 00:24
0
雪    币: 97697
活跃值: (200759)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
11
Support.
2009-5-22 01:38
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
姫狩,一開始我也是一頭露水,因為這種加殼方式很特別(之前那個戰女神0我看了一個多月還是手脫不了),修改器SpoilerAL就可以改這個遊戲,我是跟這個修改器才知道原來遊戲它是如何跑的
2009-5-22 18:23
0
雪    币: 460
活跃值: (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
支持下,
PS:想起以前逆向过pajamas的Prism Ark的图片资源解密算法。
2009-5-23 17:11
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
来看看,好久没来贴了,先顶下!
2009-5-23 17:49
0
游客
登录 | 注册 方可回帖
返回
//