# z% [( ~, b7 a! J7 B1 n
经过前边的字库改造,已经完成了一半的工作,剩下最关键的显示汉字部分了。字库已经是中文的了,但是能让中文显示出来却不是件容易的事。不信现在进游戏试试,看到的中文全是乱码。为何?因为中文是双字节,两个字节才代表一个汉字。游戏显示英文字体的函数是为显示单字节的英文设计的,是不适合显示中文的,必须要修改。因为不可能得到程序的源代码,修改方法只能采用比较极端的修改可执行文件的方法了,即所谓的逆向工程。 9 Q! L4 \2 k# ]5 W: R" t5 g: n: V/ s W
' X5 J6 ]4 Q/ { A
于是操起常用的调试工具Ollydbg开始调试游戏。经过2天的不懈努力,终于找到了显示字库的函数,以下列出读取字符的函数部分代码:
9 q' v. K( _' v+ ?, n9 `' i/ L b, D: c) w; d$ U
004AA909 8A08 mov cl,byte ptr ds:[eax]
% q8 ]2 Q5 B/ t. D% h, l% y
3 q9 H: d* ?& Y* q4 W; Q004AA90B 40 inc eax
4 o* m, |1 X9 f) i# |; V1 E9 e; O0 b2 e, M# [
004AA90C 8BF1 mov esi,ecx 0 J- E+ G9 l$ p, a" c
1 C% r2 v# G* h; j& s Z
004AA90E 8BD6 mov edx,esi
' V& X o. ^# P E P4 G& }8 b0 {" `0 d8 @- a: V3 D4 T4 c6 J
004AA910 F7DA neg edx
6 S# Z; D) a! Z( {5 @
7 p* j4 S/ R8 p8 o004AA912 1BD2 sbb edx,edx # c1 h4 t8 b8 h3 g/ x+ W! J, U& w! W
: `5 a; g+ l& _& b! K) z3 T5 S3 v& }0 _; K004AA914 23D0 and edx,eax
) `6 X/ z) `; v X- X
' a' v5 O# l( ]1 K4 F004AA916 85F6 test esi,esi / s$ f: \/ z8 ` T( x7 e
, m7 G3 i W* Z3 e; @004AA918 8917 mov dword ptr ds:[edi],edx
7 o# M M6 B2 [9 ^. h* Z+ j8 _1 l' T$ [4 M, u* c2 |; ~% A$ @
' S& h7 P+ E# E$ O( L3 m; ~
9 |2 ~" u+ v: v- n9 k
函数开始Eax指向脚本文件grim.tab文件中某行脚本的第一个字符。例如主菜单”Control Help”中的C位置。004AA909一行的意图很明显,读取字符串中当前位置的的一个字符的ASCII码到放到cl中,然后Eax加一,指向下一个字符。然后会将此cl中的字符当作参数,调用另外一个显示字符的函数,完成读取字库文件并显示字符到屏幕的功能。 % f* h: p7 R. W
* l6 j" @: r5 W3 G) @0 D) E1 p
我们需要对此部分做改造。如当前Eax指向一个英文字符,则还沿用程序原来的部分,读取一个字节到cl,并使Eax增一;如当前Eax指向一个中文汉字,则需要程序读取2个字节到cx中,并使Eax增二,指向下一个字符的正确位置。那么如何判断当前Eax指向的是一个英文字符还是中文汉字呢,GB2312汉字的编码有一个的特性:两个字节编码最高位(第8位)一定是1。每次读取字符时,先判断第8位是0还是1,如果是1一定是中文,是0则是英文。 . t: h4 w6 U: C u6 Q. r: c
# H! ?9 ^+ X! K1 h/ N% M* f有了思路,对读取字符的函数做如下修改,红色为新增代码:
; N; [* Q3 v$ n1 P+ ?1 Y- h9 }& X0 V+ w
004AA909 8A08 mov cl,byte ptr ds:[eax] - J! p8 e8 a7 ~3 q( u" V
& D3 }5 ^! o% J+ v, K004AA90B 40 inc eax / a9 ] d/ `9 q) P
I j( G0 W% \) W. U. i. I4 ] f6c180 test cl,80h 2 p* [3 Z( C6 h! e. p$ {. w% d1 I
; P, ~, S& {" q+ ? 7406 je 4aa90c 6 s* x4 @4 \- h/ s: ]
4 E! i1 p3 w, z' N6 d
c1e108 shl ecx,08h + Z' g5 L# Q2 e: |3 t: R( S
; a9 v. L3 I' [' F3 U% J 8a08 mov cl,byte ptr ds:[eax] / Y4 o. _+ e) K P6 }
7 I5 w4 x3 M% D0 `# B H$ |3 m 40 inc eax
3 ?; F6 k N V1 N% \ 004AA90C 8BF1 mov esi,ecx
. F0 a% B' J- c
5 k+ }; v$ a+ N% G004AA90E 8BD6 mov edx,esi % Y, i( C/ v4 ~7 ^$ M
. l8 Y1 }3 o5 T* l; S( T+ A004AA910 F7DA neg edx
3 O& K+ f& M: {% e' J- l& r" H1 u/ T
1 Y s- V# [# }# B. b! ?004AA912 1BD2 sbb edx,edx K2 `3 M3 N# G) u6 `, P- E) h
% \( Y. K6 Z$ y7 p' L& o
004AA914 23D0 and edx,eax
i5 K$ ~/ t( U4 `7 O+ W. x) z6 i7 ^ a
004AA916 85F6 test esi,esi * C- S& x2 W* ^3 M
+ j( I2 n7 k" [5 Z! O+ Z004AA918 8917 mov dword ptr ds:[edi],edx 5 ~+ g" l% {; S# P# H U/ r) \5 F
) _) u' ?0 ~4 q: \9 a4 E/ g* F! o
" _; r& u$ b7 z( m/ B1 _! p
$ W3 O0 c( \/ j. I& j' Z先放Eax一个字节到cl中,然后判断cl的第8位是否为1,如果是0,说明是英文字符,则按程序原来的进度跳转到004AA90C这行的代码继续执行。如果是1,则要将cl向左移8位,为汉字第二个字节留下位置,然后再取一次Eax的值,把第二个字节放到cl中。最后Eax增一(Eax增了两次1)。此时Ecx的数值正是一个汉字两个字节的内码。Finish!
* l4 j# E. ?& Z( Z5 A
( q& s& {- H1 L/ n然后用Uedit打开可执行文件,找到一段nop(90)比较多的位置,最后将红色部分的机器码加到合适位置就OK了。
5 U! L7 A% k% |6 [* a6 a' \& G$ Y" y
( J2 ?. C5 u2 n9 S! \- Q5 K9 S9 R2 G原贴地址, u% s9 j: Z. Q( `2 i4 \
http://www.cnblogs.com/pscj/archive/2005/05/10/152760.html |