7 J' a& r/ @0 A" a/ j7 A3 Q3 s经过前边的字库改造,已经完成了一半的工作,剩下最关键的显示汉字部分了。字库已经是中文的了,但是能让中文显示出来却不是件容易的事。不信现在进游戏试试,看到的中文全是乱码。为何?因为中文是双字节,两个字节才代表一个汉字。游戏显示英文字体的函数是为显示单字节的英文设计的,是不适合显示中文的,必须要修改。因为不可能得到程序的源代码,修改方法只能采用比较极端的修改可执行文件的方法了,即所谓的逆向工程。
8 R* t: @3 a; v7 t, r
& Q2 `8 Y4 A" d' ^1 a+ u于是操起常用的调试工具Ollydbg开始调试游戏。经过2天的不懈努力,终于找到了显示字库的函数,以下列出读取字符的函数部分代码: / U1 G) A7 h2 e0 u
9 b1 t3 _% i5 l& G% R# k& @1 G; h
004AA909 8A08 mov cl,byte ptr ds:[eax]
- {. @2 R+ G- o a/ e; D
& L; P0 U% v6 w0 Q1 b1 L* @! a004AA90B 40 inc eax 3 Q6 S! y, ~3 u2 m& w# s" K
( M u1 y/ |. m! P004AA90C 8BF1 mov esi,ecx 3 V4 u6 \" ?9 v6 N/ h: t9 Q8 r
7 t. o% o! _+ v) b& X r/ Z- t004AA90E 8BD6 mov edx,esi
. {& x3 R: ]3 X5 z- u9 G9 N
/ S. X4 c! n& |1 X004AA910 F7DA neg edx
, U2 \/ m! `" Q" m n9 t0 ~/ v
8 y. x! C6 A8 j9 I- ]004AA912 1BD2 sbb edx,edx . T, |- M2 s5 C: m' X
7 G# L b+ A- b) }2 t5 O8 P004AA914 23D0 and edx,eax
6 p/ V- m: V/ t' D1 b" w9 o a+ R4 a( J4 J
004AA916 85F6 test esi,esi 4 s9 w2 J7 B' \# b4 e# d) R
$ @4 T" K, ?9 L0 A& W N
004AA918 8917 mov dword ptr ds:[edi],edx
% z) C: E1 j: e# \
' R# Y- j0 v3 g: b7 Z
; g6 l4 e0 [6 C& @( `
& x3 J$ b3 P! C, Z- Z$ p! t! k0 m函数开始Eax指向脚本文件grim.tab文件中某行脚本的第一个字符。例如主菜单”Control Help”中的C位置。004AA909一行的意图很明显,读取字符串中当前位置的的一个字符的ASCII码到放到cl中,然后Eax加一,指向下一个字符。然后会将此cl中的字符当作参数,调用另外一个显示字符的函数,完成读取字库文件并显示字符到屏幕的功能。 ( { B* ^- U6 |# r* r. {) R
0 ]* n6 T) f, h' _7 a
我们需要对此部分做改造。如当前Eax指向一个英文字符,则还沿用程序原来的部分,读取一个字节到cl,并使Eax增一;如当前Eax指向一个中文汉字,则需要程序读取2个字节到cx中,并使Eax增二,指向下一个字符的正确位置。那么如何判断当前Eax指向的是一个英文字符还是中文汉字呢,GB2312汉字的编码有一个的特性:两个字节编码最高位(第8位)一定是1。每次读取字符时,先判断第8位是0还是1,如果是1一定是中文,是0则是英文。 - k+ N/ U9 V* P4 r
/ A' ^5 h" W o. }# ~有了思路,对读取字符的函数做如下修改,红色为新增代码: 2 M4 e1 W `* `6 }
0 E: a6 g; \5 e7 w$ f$ L
004AA909 8A08 mov cl,byte ptr ds:[eax] & w7 j4 h f1 ] v E& p
! B$ s- t( q* K7 R; ]$ b) J) x
004AA90B 40 inc eax ' J" J! ]2 K1 Y, l* A# y! X3 @
3 T- |6 Y5 z9 X4 I6 f
f6c180 test cl,80h
- S4 A4 P0 q" U3 M0 q5 F
9 D* s1 E6 Z: K+ i 7406 je 4aa90c
* `5 P8 s& `2 I g7 c3 `& Z( B
4 t4 r9 Q, o* v' h9 f Z c1e108 shl ecx,08h 5 @" O, M) I8 b7 m' L0 ^
& A) d0 j4 I+ ? 8a08 mov cl,byte ptr ds:[eax]
( P7 | A, ?# ^) {, B* [5 Q0 t9 K [
40 inc eax
7 Z8 R+ R& \$ E7 ~9 C 004AA90C 8BF1 mov esi,ecx ! K6 y2 r) W# a
/ y- ~8 k- c2 [1 ^- f/ V0 | L004AA90E 8BD6 mov edx,esi 2 _2 K) Z1 W* z* n$ d: ?+ g
# I5 _6 |4 p4 M; _- K& Z. g
004AA910 F7DA neg edx 5 m% e1 H% ~3 D' d* Q1 q6 ?5 n
* M+ y+ _$ t m, x
004AA912 1BD2 sbb edx,edx
3 Y/ P, k) g8 |$ H9 t& K3 o$ i2 u6 l6 Z) [, x/ c* }
004AA914 23D0 and edx,eax 5 D+ Z, ]: [3 B. k3 Z" v
- O! y9 ]& J; X) C/ Y C
004AA916 85F6 test esi,esi & l5 c1 U% E) u9 D/ M1 J
4 c' b! X9 ~4 l0 j( P$ W
004AA918 8917 mov dword ptr ds:[edi],edx
& S' e: I8 b: \4 K* I9 X" Z7 x" V) K* b, d9 Z* { |( @" ~
$ }" n9 j1 V9 W/ x. b5 ^. [4 R
5 z! G A( a' y: c5 o8 y% h
先放Eax一个字节到cl中,然后判断cl的第8位是否为1,如果是0,说明是英文字符,则按程序原来的进度跳转到004AA90C这行的代码继续执行。如果是1,则要将cl向左移8位,为汉字第二个字节留下位置,然后再取一次Eax的值,把第二个字节放到cl中。最后Eax增一(Eax增了两次1)。此时Ecx的数值正是一个汉字两个字节的内码。Finish!
, j+ |8 K2 W' I2 ]2 P$ D* b- K! F* W j3 F( O/ F. H9 {9 A+ H
然后用Uedit打开可执行文件,找到一段nop(90)比较多的位置,最后将红色部分的机器码加到合适位置就OK了。
. z. w% P5 Y6 p# A5 l' A: x" M* r6 R* b' l- K+ D$ U4 v8 u
原贴地址
" E( S4 X1 d3 ahttp://www.cnblogs.com/pscj/archive/2005/05/10/152760.html |