冒险解谜游戏中文网 ChinaAVG
标题:
【汉化资料】浅谈网络游戏《天XXX》的文件加密格式
[打印本页]
作者:
shane007
时间:
2008-10-30 20:49
标题:
【汉化资料】浅谈网络游戏《天XXX》的文件加密格式
学习一下人家的破解过程,这个游戏是开源游戏引擎OGRE进行开发的喔。
) [6 s D& y M% G# p' r4 y8 j
/ ?. a, k3 {2 k P
: I0 n' N$ P, z' n3 B6 H) I
, m+ g/ ]: d9 a+ n0 k) s
发信站: 饮水思源 (2007年06月22日02:36:37 星期五)
* N: m# H6 X' b& x
( U) C! H6 C0 }7 V' C& ?
作者:
[email protected]
1 P& ^- E# y0 \) K
# L6 R1 j2 A2 O7 R: i# I
三月份时玩了某狐公司的网络游戏《天XXX》,感觉还是蛮有意思的,遂研究了一下。
$ Z3 T3 F2 S- n. R6 B* n8 }( z
这个游戏是利用开源游戏引擎OGRE进行开发的,看了一下目录里面的文件结构,主要的数
4 L2 w9 H7 @ {3 \& K
据都放在Data目录下面。不过文件基本都是.AXP后缀的,每一个动辄几十兆,料想肯定是
n: T: g# ?) ]( L( V# L
把游戏文件打包到一起并加密过的,GOOGLE未遂。开始用UE打开看了一下这个AXP文件,发
" ?9 W8 w5 x8 R6 l0 f; ^4 q6 Q
现里面居然大部分都是明文的,开始以为只是把文件罗列在一起,不过仔细看了一下,发
$ _+ Q6 F0 h$ A
现每个文件都有一段间隔,前面还有一个数据头,而且文件与名字也无法对应。于是打开
3 I, _ j3 W. D8 ]1 a/ b; t
OD手动分析一下,主要过程其实比较简单,CreateFile函数下断,找到文件Buffer位置,
- }7 F# x ] X( w Q5 J
再下内存访问断点即可来到关键代码区域。略过具体跟踪细节及文件校验部分不讲,文件
0 n9 ^# S; X4 Q/ }. }
格式主要分析如下:
- e9 s# ], r7 M0 q% W: A
* J1 w* S6 R. a4 U1 o
整个AXP文件可以分成四个部分:1.文件头 2.文件名索引 3.文件索引 4.文件数据
" {7 y/ }9 u( j! u- v
" l+ W; n6 A3 v5 C/ q1 s. [
- \$ _6 ]; J4 y5 a7 m8 e
1.文件头:
4 _: y$ e# X' }! ], F) w6 D* K
整个文件头固定为0x28字节,其中第20个字节开始的一个整数乘以12代表了第三部分
5 _9 |7 ]$ u& O( K, k7 Z
即文件索引部分的长度(因为每个索引有三个整数构成)
* q9 c' q B$ h
2.文件名索引:
- Q& J6 i, ^1 f/ H2 `
整个文件名索引固定为0x60000字节,其中包含了每个压缩文件对应的文件索引位置
' F0 `; _2 N6 Y- }
' X t) B9 J* O0 _# s: P2 w3 I3 c
3.文件索引:
L3 [' H* Q; G& I. G
本部分长度由文件头相关数据决定,其中包含了每个压缩文件在.axp中的实际偏移位
! r! \: l6 Q: Q
置及文件大小
! U" \- g+ {, X X' V4 y
4.文件数据:
! T2 x/ B" w1 }; \5 m/ M9 m' l
本部分包含所有压缩文件的具体数据,每个文件之间用若干零填充。
Z+ U& A: g7 A% s
+ {) m# i, h6 C5 B, h
首先说说解压总体过程:比如我们要从A.axp中解压出一个叫file.txt的文件,那么先根据
& i0 S2 t) @& l8 h1 n0 T! J4 }
文件名file.txt到文件名索引中去找到对应的文件索引,然后再根据文件索引找到这个文
7 w! t' w6 ~! H' t% V2 a
件在axp文件中的位置和大小,最后把其解压出来。
) ~3 T0 F+ w6 j( q- n# d
" e8 L0 B9 c& W7 S2 _2 f
解压具体过程如下:
0 J; v; m; {' x/ }2 j. i) y
将待解压的文件名转为小写(如果为英文字母),利用GetDisp(char* s,int v)函数
. }& L! A& W% Y7 [2 C
计算相关数据,其中s代表文件名,v代表计算参数,分别计算GetDisp(fname,1),GetDisp
; `3 b6 W. a( \- a9 B) A
(fname,2),
: O5 V" ]7 W2 j' M' n& L
GetDisp(fname,3),得到三个值a1,a2,a3。其中a3低位与在文件名索引中的位置有关,a3最
' s3 M) |. m& @; }0 ~) p- G# x! F
高位及a1,a2用来进行校验,如果三个值不能同时满足要求,则将偏移位置顺移继续验证,
) b6 s) D! u7 K
具体细节懒得写了。
- O3 ^3 B. U8 A' | m/ q3 P8 ^
以下为GetDisp函数具体内容,我直接将跟踪代码里面的汇编改造了一下拿出来用,其中s
9 c7 E" A& e. F3 ~- g7 T
ucks为一个随机数数组,这里不列出来了。
* ?& r) s. J' N# g! U* ^ j4 j
unsigned int TLBBUnpacker::GetDisp(char* s,int v)
5 X1 T B( C6 W, j' f/ ]2 g3 d
{
1 Y; T* R( d5 V# A
__asm
* l3 q6 F9 k2 O
{
: B \2 ^& |# j9 D
push esi
* x* \1 ]7 Q6 [# k
mov esi,s
3 W. M4 h% L |9 s6 |
mov cl,byte ptr ds:[esi]
7 b8 \& L% W5 A- U4 A
test cl,cl
3 V& ]% h$ K& n% ]) K
mov eax,0x7FED7FED
1 |" g0 {6 H% v, B
mov edx,0xEEEEEEEE
6 U, B* z1 ?2 P- R+ o) F+ ~1 y
je end
) [8 e# L# ^. z: [& o% p
push ebx
7 R. f* C, @8 h* I! }$ m
push ebp
+ A9 P; A0 x# C
push edi
1 Y2 t7 N s) n4 U2 q; V
mov edi,v
, x8 Q" G+ Q4 h1 \
shl edi,0x8
" L1 v1 a* E8 M3 C Y( E
iter:
3 d, p4 F, Y( w. G D6 o# C, V
add eax,edx
' F3 t' h1 a) C' j+ x; @: ~0 [) U
imul edx,edx,0x21
4 q% j* A* |" J2 [! A" M
movsx ecx,cl
$ V m8 f9 V% j. D+ ?. l1 @- }& U
lea ebx,dword ptr ds:[edi+ecx]
% }( n! L1 j, a4 g h
mov ebp,dword ptr ds:[ebx*4+sucks]
& l& x" y+ `" ?1 S! g
inc esi
( q. T0 i* |6 S" W5 x* u/ N
add edx,ecx
: i/ |2 m9 ~; ]! |3 {* }
mov cl,byte ptr ds:[esi]
' ^1 A1 ^ ~3 O% C& v
xor eax,ebp
b; y* u9 _2 i+ _- l. t; N
test cl,cl
: d2 m/ a8 H4 d. T, _& n% U+ {. l" h
lea edx,dword ptr ds:[edx+eax+3]
5 `! @/ j9 E0 }6 }% m
jnz iter
5 w$ T4 e: l" ?
pop edi
3 Z. l- | n& m5 h6 o! L6 x
pop ebp
) B# e7 h9 N4 a4 d
pop ebx
7 v1 i8 p" G, n, b0 _' @4 y% [) W( b
end:
- N0 [3 A: L/ [/ K5 d' ?
pop esi
! c, @* J ]5 R$ C: ~
}
5 b2 {0 M; v% t5 Q/ q$ H5 Z$ U
}
$ m! n2 Q0 R' b1 u- n+ m
这个为解压单个文件的函数GenerateFile,用到了QT作GUI,大家就当伪代码看吧。
; F) P1 C7 g# H. t7 R: q/ Z
bool TLBBUnpacker::GenerateFile(QString name)
! K5 V$ ^5 ]+ \" ]
{
' R& W; s8 V+ Y( R. l* O
name=name.toLower();
# V+ g& Y" E8 I6 H( ?' [9 h
unsigned int a=GetDisp(name.toLocal8Bit().data(),3),a2=GetDisp(name.toLocal8B
0 Q2 w; x0 P0 u$ T
it().data(),2),a1=GetDisp(name.toLocal8Bit().data(),1),b,disp,length;
' v4 c l* e9 u" O; G. _
a&=0x7FFF;
3 N# U( E/ `1 [" `8 p$ W- `
while(!((b=((int*)buffer2)[a*3+2])&0x80000000)||a1!=((int*)buffer2)[a*3]||a2!
; u* b% J3 V) n
=((int*)buffer2)[a*3+1])
, z! Q @" a6 \: k: o' b: ]
{
. J) |( W( w$ r' S
a++;
& Z! h- z7 Z& v- d
a&=0x7FFF;
! u7 H; \- \: m2 t
}
4 I6 X; w ?2 h7 K! D/ [
b&=0x3FFFFFFF;
' G" t) ~2 j2 f/ \% I5 }* s7 M5 Z
disp=((int*)buffer3)[b*3];
: n* a+ F& U- Q* F( X
length=((int*)buffer3)[b*3+1];
- o( n8 ^/ ~5 M/ t% C0 w$ Y P" S; t3 x
@2 b. r0 {& A9 |6 N
QFile pdata(this->package_name);
8 g. X/ I8 b# Q; G0 D$ B
if (pdata.open(QFile::ReadOnly)&&pdata.seek(disp)) {
& A, }# h) O; E$ A1 ^) K3 W+ V% J" C
; j8 g( w; X4 p$ u
QString wdir=QDir::currentPath()+QDir::separator()+QFileInfo(pdata).fileName
4 t1 L9 F& z! {/ j: O$ _
()+QDir::separator();
- T) |# g" Q8 L
QDir dir;
! i" B2 u+ ]; Q. ^
QDataStream pfin(&pdata);
4 R% }0 b2 ~; M2 z
QFile file(wdir+name);
5 r3 q3 s6 L! F1 D' D3 W. P5 o
QFileInfo info(file);
* @' G4 n7 T; y5 [5 a
dir.mkpath(info.absolutePath());
* |# L/ m; j( \! G) q! N! Q) i
if(file.open(QFile::WriteOnly))
2 X& P2 M" F/ ~; t8 x! Z
{
. ]" C4 y* z. d( t
char* pBuffer=new char[length];;
, N$ P; [( X( b, i5 v6 Q
QDataStream fout(&file);
M4 |3 ?- ^8 }' p2 W
pfin.readRawData(pBuffer,length);
4 [3 j( q2 \: n* }; L
fout.writeRawData(pBuffer,length);
8 `/ I: ]# b) [
delete []pBuffer;
8 _% e( ~0 ], y/ H% y
return true;
& ~9 _1 x) n' F
}
& k, ~; M$ e: _2 h
}
& }# c/ _2 G1 k- Z# D
return false;
0 g- o1 r+ l d9 t$ [
}
, k" C) p2 i8 e6 c6 \
得到待解压文件在文件索引中的位置後就可以找出该文件在axp文件中的具体偏移量和文件
% W3 E7 E5 s# F& W9 F s: l
大小了,然后直接fseek一下然后在弄出来就OK了。
& I/ f* a, m3 g
最后说一下,这个AXP压缩包本身就含有一个文件列表文件叫做(list),所以每次只要先解
8 E% w% O8 j+ i# W; K0 A8 p
压缩这个文件,然后按照里面的文件列表来一一解压缩就OK了。
/ ?5 k5 H/ c" P+ s Z+ N
0 E3 |6 i# w5 |7 Y6 Y
% A- @; {2 w. M3 C+ r3 B. x/ K7 [0 I8 ~
以上就是文件大致格式,感觉还是比较简单的,也可以考虑在自己的项目中使用类似方法
2 C* t* f: E2 ]9 F0 f( l# l
进行文件压缩。
& W6 C9 c2 \) J0 p
% d# Y3 e! V+ O& w: k! Y' G
PS:本文仅供学习,本人不负任何责任。。。。
欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://chinaavg.com/)
Powered by Discuz! X3.2