冒险解谜游戏中文网 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; tOD手动分析一下,主要过程其实比较简单,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 e1.文件头:
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 c3.文件索引:
  L3 [' H* Q; G& I. G  本部分长度由文件头相关数据决定,其中包含了每个压缩文件在.axp中的实际偏移位
! r! \: l6 Q: Q置及文件大小
! U" \- g+ {, X  X' V4 y4.文件数据:
! 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& LGetDisp(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 Dpush esi
* x* \1 ]7 Q6 [# kmov 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 yje end ) [8 e# L# ^. z: [& o% p
push ebx 7 R. f* C, @8 h* I! }$ m
push ebp
+ A9 P; A0 x# Cpush 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, Vadd eax,edx
' F3 t' h1 a) C' j+ x; @: ~0 [) Uimul edx,edx,0x21
4 q% j* A* |" J2 [! A" Mmovsx ecx,cl
$ V  m8 f9 V% j. D+ ?. l1 @- }& Ulea ebx,dword ptr ds:[edi+ecx]
% }( n! L1 j, a4 g  hmov ebp,dword ptr ds:[ebx*4+sucks]
& l& x" y+ `" ?1 S! ginc 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; Ntest cl,cl
: d2 m/ a8 H4 d. T, _& n% U+ {. l" hlea edx,dword ptr ds:[edx+eax+3]
5 `! @/ j9 E0 }6 }% mjnz iter
5 w$ T4 e: l" ?pop edi
3 Z. l- |  n& m5 h6 o! L6 xpop ebp
) B# e7 h9 N4 a4 dpop ebx
7 v1 i8 p" G, n, b0 _' @4 y% [) W( bend: - 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/ Zbool TLBBUnpacker::GenerateFile(QString name)
! K5 V$ ^5 ]+ \" ]{
' R& W; s8 V+ Y( R. l* Oname=name.toLower();
# V+ g& Y" E8 I6 H( ?' [9 hunsigned 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- da&=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 NQFile pdata(this->package_name);
8 g. X/ I8 b# Q; G0 D$ Bif (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( tchar* pBuffer=new char[length];;
, N$ P; [( X( b, i5 v6 QQDataStream fout(&file);
  M4 |3 ?- ^8 }' p2 Wpfin.readRawData(pBuffer,length);
4 [3 j( q2 \: n* }; Lfout.writeRawData(pBuffer,length);
8 `/ I: ]# b) [delete []pBuffer;
8 _% e( ~0 ], y/ H% yreturn true; & ~9 _1 x) n' F
} & k, ~; M$ e: _2 h
}
& }# c/ _2 G1 k- Z# Dreturn 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