本帖最后由 shane007 于 2011-1-30 14:08 编辑 3 m: Q3 }1 H& R6 G. Z
1 J3 d) w0 [/ u! g! ^! S+ W' Z" g' { v在上文中,我们已经成功的使游戏创建了我们需要的汉字纹理,不过仅仅有这1024个汉字纹理是不行的,还得想办法让它显示出来。前面说过,游戏会调用glCallLists显示显示列表,glCallLists原型如下:/ x% c5 Z7 l! \
7 S) H/ a' ]) L6 z! }( D' t3 ~
代码:) q% L$ C) R4 `
void WINAPI glCallLists(" q& d% z$ P9 u! } ]. M
GLsizei n,
/ e! A5 G( @' k/ Y, U& H, U GLenum type,: r* c+ g5 R! S6 E0 x; E5 y
const GLvoid *lists Q2 j6 W+ z2 i
);
" ]5 x! r( ` E0 Q6 [3 Z# P4 V) y 其中n为字符串长度,type为字符串类型,*lists为字符串指针,glCallLists函数下断调试发现,游戏在调用glCallLists时第二个参数使用了0x1400,即GL_BYTE,表示单字节:1 y6 p4 D9 y( x5 {
& I8 S9 T) F/ l; K* L9 S2 v% `4 J代码:
. }( F! f! T8 G! [" v00439D3D |. BB 00140000 mov ebx, 1400 ; 第二个参数,GL_BYTE( |7 \4 x! a( f1 u
00439D42 |. 890424 mov dword ptr [esp], eax8 q6 c' ~: e3 D# u# J% o
00439D45 |. E8 464B0500 call <jmp.&OPENGL32.glListBase>- V8 \" d& i! ?: }" K9 k0 v
00439D4A |. 83EC 04 sub esp, 45 M3 B& f* m& d S! Y+ Y
00439D4D |. 893424 mov dword ptr [esp], esi ; |
% `: D' s$ ^* \00439D50 |. E8 136D0800 call <jmp.&msvcrt.strlen> ; \strlen+ B* M+ t; c8 C) G$ S, b
00439D55 |. 890424 mov dword ptr [esp], eax ; 将strlen结果作为第一个参数5 L8 ]0 T1 T, R ?: @4 M
00439D58 |. 897424 08 mov dword ptr [esp+8], esi+ T; S- Y. p7 _( q
00439D5C |. 895C24 04 mov dword ptr [esp+4], ebx7 @1 {' \8 K, r. h y
00439D60 |. E8 234B0500 call <jmp.&OPENGL32.glCallLists>6 I/ A/ z/ L. U/ Y
因为游戏原有256个显示列表,字符串为单字节字符串,而现在我们要使用1024个显示列表,相应的字符串必须使用双字节编码,这里第二个参数必须修改,截取gl.h部分如下:
8 ]/ j2 b8 B8 t: ] x' }6 U! I8 S
l1 _8 v0 n9 m" k2 c f代码:6 W& V0 k0 G+ L$ C i2 f
#define GL_BYTE 0x1400* [" }& H9 k( n. C& R7 z& J
#define GL_UNSIGNED_BYTE 0x14010 \& r1 c' Z4 r% j; [0 [
#define GL_SHORT 0x1402
8 v1 W+ \! ~3 J2 j' m6 ` j1 ^#define GL_UNSIGNED_SHORT 0x14038 M* p2 V8 U; w
#define GL_INT 0x1404
- ]8 Y1 W( G) [. U J5 G/ y#define GL_UNSIGNED_INT 0x1405
& w. \, u. q# v* q#define GL_FLOAT 0x1406
; g, V0 N! c3 r6 s: R. M#define GL_2_BYTES 0x1407
( Z }8 q5 \0 g. ^) f#define GL_3_BYTES 0x1408- P. z$ M. C0 ~) j) [- H! i# n
#define GL_4_BYTES 0x14096 L7 @# ]% A: x! U7 Q; I0 o
#define GL_DOUBLE 0x140A
+ e, C( Q( N( T2 V2 D; s 无符号双字节为GL_UNSIGNED_SHORT,所以应将0x00439D3D处指令改为mov ebx, 1403。接下来修改第一个参数,第一个参数改起来比较麻烦。原游戏使用ascii码所以可以调用strlen,而我们使用双字节字符串,那么必须使用wcslen来求字符串长度,而导入表里没有wcslen,这时候我们必须自己加入一个导入函数。这里为了省事我使用了pe工具stud_pe,wcslen位于动态链接库msvcvrt.dll中,增添该函数后如图:
" k0 |# u( h5 }/ a. k
) I9 L2 T+ w0 ~. w; U/ g 图中可以看出,导入函数wcslen的RVA为0xB05214B,理论上只要我将call <jmp.&msvcrt.strlen>改为call [0xB05214B],便可以达到目的。但是事情总是比之前想象的复杂一点。这里如果直接改为call dword ptr [0xB05214B],目的地址与指令地址偏移量大,call为远call,指令长度为6字节,而原来的call是近call,指令长度为5字节,这将覆盖掉下面的mov dword ptr [esp], eax指令。后果将是glCallLists永远得不到正确的字符串长度。
7 {$ Q) A( j2 K 不过这很容易解决,我们只需要在代码的缝隙中找到一块6字节的空白区,然后加入指令jmp [0xB05214B],再call到这条新加入的指令,就可以顺利解决了。我将这条指令放在了0x439c36,然后修改0x439d50处的指令为call 0x439c36,问题解决。如图:9 ^. ^9 i& J: `0 R
1 a- S: c3 Z. e7 h! H 至此,对游戏引擎的修改已经结束。
. t6 N, P" a& g @' U/ t& ]3 C) L. U 剩下的工作比较简单了,将游戏的英文文本找出并逐一翻译。统计翻译后的中文文本,也就是统计使用了那些汉字。然后制作一张32格*32格的字库,其中前128个位置放置游戏原有字符并与游戏原来字符顺序保持一致,以便能够正常显示一些特殊字符,后896个位置依次放入统计出的汉字。按照字符在字库上的位置制作一张码表,然后按照码表将翻译后的文本进行编码转换,再将转换后的结果导入游戏。
9 @1 Z) |4 ]4 N U$ h2 d: [# f/ L5 q 事实上,我有意无意地淡化了在分析调试过程中遇到的种种麻烦,因为事后想想也不过如此。逆向是非常痛苦的,因为下一步总是不可预料的。任何一个小小的麻烦都有可能导致前功尽弃。然而逆向的魅力也正在于此,当咬牙踏出那一步之后,回头看看自己的脚印,我想这都是值得的。 } f# G) {) D
最后附上一张中文版截图: # t& ?9 j, Y7 E. ~( A; L( n9 V6 g
|