3 l) N4 a: l* }" J
经过前边的字库改造,已经完成了一半的工作,剩下最关键的显示汉字部分了。字库已经是中文的了,但是能让中文显示出来却不是件容易的事。不信现在进游戏试试,看到的中文全是乱码。为何?因为中文是双字节,两个字节才代表一个汉字。游戏显示英文字体的函数是为显示单字节的英文设计的,是不适合显示中文的,必须要修改。因为不可能得到程序的源代码,修改方法只能采用比较极端的修改可执行文件的方法了,即所谓的逆向工程。 ; j3 {) u0 q' X9 C+ c+ K
' y' v \# e4 c; p" i$ Z
于是操起常用的调试工具Ollydbg开始调试游戏。经过2天的不懈努力,终于找到了显示字库的函数,以下列出读取字符的函数部分代码:
8 O: ^- c- U: B" h( k |3 V7 m h+ g( q" ~. M3 v6 N
004AA909 8A08 mov cl,byte ptr ds:[eax] % `1 f) J4 V. |4 M
5 S9 g, u4 x7 F6 f* o; L, c
004AA90B 40 inc eax $ l* W5 U. g" h8 w$ \
5 Q+ q- I2 G" m" x
004AA90C 8BF1 mov esi,ecx
3 z8 B$ }5 u5 b; D5 L9 y3 ^& ]' k7 h9 y7 l! ^
004AA90E 8BD6 mov edx,esi 4 W4 N5 o6 P7 E5 c: T j, `
/ m4 A$ t4 c2 L2 e. ?5 l5 M
004AA910 F7DA neg edx . a9 n' p* F4 D) [/ B
/ h& P3 T4 ~! p1 y7 m004AA912 1BD2 sbb edx,edx
/ `4 u# g) ~3 R6 P) e7 k9 S0 K% V4 x+ f% ?# x. R5 e' n9 C$ B
004AA914 23D0 and edx,eax
+ u+ G& C4 ]% c1 ?5 J& P; f5 b' i1 L0 L/ J0 R. w
004AA916 85F6 test esi,esi
" }, _0 l' T1 o! f- I8 p) ]+ i4 t9 F
004AA918 8917 mov dword ptr ds:[edi],edx
, T1 P+ \5 d. g t e( @
2 p$ Y- i! o* K) Y% y- J- |2 I 3 J# H; N, s& u
" ?7 |% e4 G L# T4 Y0 @函数开始Eax指向脚本文件grim.tab文件中某行脚本的第一个字符。例如主菜单”Control Help”中的C位置。004AA909一行的意图很明显,读取字符串中当前位置的的一个字符的ASCII码到放到cl中,然后Eax加一,指向下一个字符。然后会将此cl中的字符当作参数,调用另外一个显示字符的函数,完成读取字库文件并显示字符到屏幕的功能。
' W7 L* v6 Q5 [4 c. U, V/ |/ r$ C' A/ N, x% ?/ K, k/ ^# x7 @
我们需要对此部分做改造。如当前Eax指向一个英文字符,则还沿用程序原来的部分,读取一个字节到cl,并使Eax增一;如当前Eax指向一个中文汉字,则需要程序读取2个字节到cx中,并使Eax增二,指向下一个字符的正确位置。那么如何判断当前Eax指向的是一个英文字符还是中文汉字呢,GB2312汉字的编码有一个的特性:两个字节编码最高位(第8位)一定是1。每次读取字符时,先判断第8位是0还是1,如果是1一定是中文,是0则是英文。
6 n. ~. {! {2 z# g8 D. `7 p
c% u5 _- J) I, x. b4 M' t有了思路,对读取字符的函数做如下修改,红色为新增代码:
7 t& y) h- Q/ \) h1 q( I& B3 D% l" b @( G. x
004AA909 8A08 mov cl,byte ptr ds:[eax]
" h* W# [% o+ p* ?9 C/ l* S. A5 H7 N) b- O8 D9 @, g$ k
004AA90B 40 inc eax " G( r% s4 V7 M2 K
1 ^3 F0 c2 [2 j f6c180 test cl,80h $ Z; s0 V$ l0 v; G7 r
! L1 c- O7 `! ~ 7406 je 4aa90c
. f4 g8 e7 g/ m6 Y# T7 z4 h" p# M1 t$ q7 @5 _2 [
c1e108 shl ecx,08h # G" c, }3 `* {; ]% u
! f; n3 E; v2 w8 i: g
8a08 mov cl,byte ptr ds:[eax]
6 q( q3 R) k( v8 O( {7 T% t7 c7 ?4 T" D2 O: m
40 inc eax
/ Z1 L" z8 G3 b2 ~; q S. M' p 004AA90C 8BF1 mov esi,ecx
/ J- ]( j! ~9 |9 @3 ^( ~$ ?! I' ?' i! b' u5 B0 J6 D+ I; N% }0 l0 \
004AA90E 8BD6 mov edx,esi * @8 l. E4 z/ U, C2 P
1 i+ L. Z- y9 O+ T- i+ P
004AA910 F7DA neg edx 3 @! V( G1 l) e/ g# a: W5 x, y u/ m
: _, z8 N5 l0 i5 d! M- M( l/ |
004AA912 1BD2 sbb edx,edx
) s5 X/ f# W7 u( W
7 V6 O" ~' \* B T* c8 W$ V004AA914 23D0 and edx,eax
. i, }! }7 }2 v
" x$ g' C+ l3 v( H" ?( T( i004AA916 85F6 test esi,esi 8 t3 b8 ]" m9 @
9 v4 y F y5 g1 u" c004AA918 8917 mov dword ptr ds:[edi],edx . N4 A8 _ k2 E5 ^* H
- q) B% l) W; d6 ~4 V# P9 C & s3 e% ~+ y" u' f4 I# P: ]0 |- B
1 I. |5 ]9 { q2 g8 r/ ^
先放Eax一个字节到cl中,然后判断cl的第8位是否为1,如果是0,说明是英文字符,则按程序原来的进度跳转到004AA90C这行的代码继续执行。如果是1,则要将cl向左移8位,为汉字第二个字节留下位置,然后再取一次Eax的值,把第二个字节放到cl中。最后Eax增一(Eax增了两次1)。此时Ecx的数值正是一个汉字两个字节的内码。Finish! , O+ c+ K/ c9 v# X0 H! O
% {2 L, M' T! v
然后用Uedit打开可执行文件,找到一段nop(90)比较多的位置,最后将红色部分的机器码加到合适位置就OK了。 3 O4 N9 J0 Q: [, F% ~7 {) O
8 w I+ {3 r4 B原贴地址; a" W! o3 T4 E+ i9 _
http://www.cnblogs.com/pscj/archive/2005/05/10/152760.html |