上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化 , p0 h u0 a$ O" L: b; e
5 B ?# e9 J7 W' Y6 V) @$ q
在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵 5 z; F8 T' L6 j. Y
这里是用bitblt贴上去的
3 {. J8 i* U, W3 T* j知道是贴图就要寻找相应的字库和字符串
) X: g/ ~1 c% U8 C字库在image文件夹就能找到 ' M: F& B5 w, G& b$ r
字符串被压在了EXE里(脱壳后exe增大了3MB) 5 Q% o, x$ K7 z' a1 D* E1 k/ c
' S. b @# k- j
下面就是分析游戏是如何显示字符串的
: `; S) }" r. R: M8 k0 o用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit J! c) A% e6 A6 G- p' [
" G5 j: o( m+ s2 r; \) g
按F9启动游戏
9 }& t! O Y1 m. z9 c然后断在4F24D2处
+ @, f- B. q& @8 V3 x这里是读取字符串用的6 Q& i2 Q& N+ q) a. d2 G
* w& I {/ ~; Q. D& `分析下
W+ r, h0 @" B1 H" G( M004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit6 F! K2 T+ t' _, _0 x
004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0$ V4 F5 ?$ e) s) D7 [- V+ _
004F24D7 |. |03D0 |ADD EDX,EAX
3 x, F# Y, R+ k! a004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF, n# E7 m9 U( L+ P, \0 P4 f: t# l
004F24DC |. |33C2 |XOR EAX,EDX4 i4 Y# v' ?; [3 ~. W
004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节0 P( ~# r" W/ j7 W
004F24E1 |. |A9 00010181 |TEST EAX,81010100
9 x! V, B: b" F1 l- U$ `/ o* y004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
+ ]! R) Y: b: i* O+ p6 P" v
% G! e4 v7 n6 G这里要注意一下/ `; C5 b6 `$ A, i
因为是4字节一取 \0处一定要是00 00 00 00
2 J& D- B% f9 A! T' ^2 E如果要把Quit& {: W& s' ]9 m
51 75 69 74 00 00 00 00 Quit....Quit...., F, l7 E2 }! q
翻译成"退出吧" 就需要在加4字节
) u6 a! ?7 k4 q8 K% z; r1 j地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]
2 ^2 E* }, b1 I9 z2 k3 g* O9 @/ Z只要把ecx里的地址改成你的就OK了
/ V. e/ u% Z# X! x3 O
" j# ?( `- s! R+ I4 s继续找处理图片的地方
/ r/ P) l( s G6 ^- N) L& F0 I一路跟下来到了
5 x( l9 t6 b/ Z4 X3 {
$ U6 Y) f0 t3 k" [7 Y% O3 f$ ?& K004F398C |. 83E6 F0 AND ESI,FFFFFFF04 ~9 U5 |5 k& P! F' h4 s3 i
004F398F |> 56 PUSH ESI ; /HeapSize = 5
7 A( I" k3 a/ l P4 P$ c004F3990 |. 6A 00 PUSH 0 ; |Flags = 0
1 X8 }$ ?9 t9 G- ]6 i$ `0 b004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000
1 e f0 W3 t0 U" ]3 b9 I6 R004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc$ W/ F1 ~, w% p9 R
004F399E |> 5E POP ESI3 g" r' }/ C: s0 v. `' ~
" s3 S4 b; n, R8 R9 H N' [- a
发现HeapAlloc 这个是用来分配内存的 s( J3 K6 a" s1 ` @ M, I
F8步过 取eax的地址 就是将写入新的quit的地方1 ~6 d) O* R, ^' R7 u
对其下硬件访问 F9运行
$ G! ~+ i. G* P) B% d) ~5 b断在了403811
9 X ?/ R6 w( u在F9几次 断在了4091c49 x$ b! g7 L# b' a6 P1 U! X
在F9几次 回到了403811
$ ~- H6 w+ ~: T/ `3 F. @/ @ 0 s j" B* a& E
确定就是这2处地方了$ ~4 _5 c3 F# G+ O! m/ c
这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用
) s' u7 ?1 {, q' d0 b! G
& W& |5 I( y5 q( W% i004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]- s' E/ h2 `3 I: i `: s/ I( m8 n1 J
004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q
1 o0 d) X" |/ o% ~1 }( w004091C4 |. |85C0 |TEST EAX,EAX
- x' p- A& q5 [' d% F# Z+ w004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出, ]1 l. Y% a- A5 e" n% K
004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
( e6 c8 Z. G( x* a i004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q
* C) u* }' v' `$ X+ V$ `/ p$ U( \. [004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格
' T# L( u' T! }: F: F. v004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8
r1 E" c& P# }7 W004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]
i/ H' e' P# n$ |" X6 o004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX
$ e8 y: f' Y% D" v004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 000000008 P: h+ K% u8 Q. s5 E
004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |6 F! S# {' a9 \8 O8 l# V1 C
004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0 S% ?, Q3 |1 i/ G7 E* A
004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]8 y: y' ?: e& X* @) ] t
004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX1 ^2 C/ \- u# G) Q6 `$ G9 a: i
004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]3 w" _7 F, d0 l; S9 s. F5 z9 W
004091F0 |. |83C1 01 |ADD ECX,10 B1 o3 V" g8 F- X" P7 p
004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX
' W2 R, R2 x; _+ G/ \004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE
" d; f0 t+ e: L- q u" D004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里: _9 q* q) U; ]! k" o7 _
004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造): L3 l$ f; z! y- E% F/ n
004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去1 J0 d% b: |+ ^4 I. t
00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
% H& X2 m; \$ J1 [& f00409204 |. |51 |PUSH ECX ; /Arg29 R1 q, H! l- }5 n4 ?
00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |
( w! J( s8 g8 T M/ O00409208 |. |52 |PUSH EDX ; |Arg1 压入Q
! |: w# x/ N8 G2 j+ a; {9 Y( q00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
$ _ e C f, n8 M5 \. V: ?0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键
3 s/ b# V& S* q6 a" k" f4 H9 ], {& ?00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]
4 c% h6 s) t! V% u; y00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u$ X/ S6 X3 I1 t# h' `+ R
00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX
* G$ L0 p1 w( b1 Q8 G. {0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]! [; v0 y. S2 ?7 @6 m/ @5 M% N3 h. c
0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度
9 `. V6 @" A! i* ?2 g V0 O00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
6 D# R0 j6 ^1 [00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],0- Z9 }" z4 e9 t. d+ ]+ P
00409229 |. |74 7E |JE SHORT Unpacked.004092A9
) e" t0 ]0 R% u; D1 K$ ?' g' E
, i' A2 T: \" r- C7 W, L6 S: Z
: x( b/ `; {/ e0 u7 X" X7 [& M/ p! C7 f
0 U: C1 C r: K, j9 n9 F# n8 T
# R$ F" i, c2 M& H# ?) l2 R
5 b {0 y; Q$ m00404620 /$ 55 PUSH EBP; M; |" Z. P. Q6 K8 W; L
00404621 |. 8BEC MOV EBP,ESP+ \7 s; D( a4 z, E! G
00404623 |. 83EC 0C SUB ESP,0C
( y, Y7 S) R# p- K, I/ R9 V00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX
1 l. ~* G9 ^. O7 K. z% g5 ]- r; ~00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]/ O- t4 O1 { R% M8 `6 X: [# q
0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]
) m$ N8 L2 R, r- P) G; Z0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX
1 X; ?! s0 m; D00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查
- S, E) a' o4 x! n00404636 |. 7D 0D JGE SHORT Unpacked.00404645
$ C! A, v% D0 {- u( d8 I. t1 X5 U00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]
$ \# m! m( [ `- _% Q9 W; v0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL
$ E2 G# S! q: Q/ s0 t* }- C0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]8 i, X2 _( R5 }
00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX0 w: M" G/ Y' E
00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查
( H7 g3 b3 a7 x) j0040464C |. 7D 18 JGE SHORT Unpacked.00404666/ B$ p4 Z( D% S# e
0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]: Y: Q8 ^( q1 ^0 ~; s6 V
00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
9 Q z$ ~6 C; K. O v- I7 h5 T00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]/ Z$ R/ K9 q# F. R. x- h4 m) J
00404658 |. 6BC0 14 IMUL EAX,EAX,143 I" o6 o, n. a0 Q
0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
7 Y5 @/ f+ s! Y5 l: ?' Z8 ^2 T0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]
) E) D, l! a! p" m) }- G/ ^00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX8 _7 T S, A1 r* h8 H
00404664 |. EB 15 JMP SHORT Unpacked.0040467B
& Z5 _0 p) L" d7 O00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表
. o/ B4 P- \( T% P8 ^/ z# {4 X$ L. N
6 H8 y& h4 ], A! k/ ]1 O* o: D! ? i" b/ K7 }" W8 n: R8 }9 \* D, m
0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..
# b# h8 b/ R+ \7 F; G0054AA58 1B 00 00 00 ...
4 w$ Q# ~( w" {4 X. W' M( i& v6 k. I! a# I! T$ n$ M/ H0 J% u
51就是字符Q3 s+ X; c( ?$ J0 Q* v5 d0 }
2D 02 00 00
q" {6 B1 B. D- _& m00 00 00 00
2 D4 \# ~1 L" c) s5 h; y3C 02 00 00
4 r4 s! M/ ^9 J1B 00 00 00
5 n. Y0 o, e' r- }( [. |* e
& o6 k$ Y7 ^2 L0 ^- E8 D" }3 o A━━┓ 0 o: k' b& N# ^3 O# ~! f
┃ Q ┃ ! x5 N8 c5 W, q- W/ B: q
┗━━B
# g) N6 t, I) n+ E6 O9 x. L& A$ A- P: z8 I
A的坐标 (22D,0)
3 k( t/ R9 T2 h' I, b: Q8 tB的坐标 (23C,1B); U) l7 K6 i, j8 i! U
/ e+ b7 t) z. G
! M' {7 N5 A' {( |$ ^( T0 a; q
8 F F, t m( Y! w- u+ a
00404669 |. 81E2 FF7F0000 AND EDX,7FFF
5 ^* }; o+ H8 L* E0040466F |. 6BD2 14 IMUL EDX,EDX,143 r% \( r! O4 y& C: |1 Y. v
00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
; }) u' u- }' q% L L00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]9 p1 B$ ?1 k2 K# k9 x8 x' N
00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX# o; y/ D1 a2 }6 q5 l
0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]1 z7 F( O- a3 B p
0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
4 H& |& Q8 }/ a" O; B7 b+ g00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]
2 D6 F9 k0 ?, G. b00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D
0 L$ a4 E7 N% S00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
1 j1 Y+ J* r* _" P" ]) }0 q/ X! @5 \0 e00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]2 R, |9 {3 c m) s' s
0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]* p G1 A% `+ H* y/ p: Y
0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C
% v- C* i: b' h% A$ ^00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]) z$ p! n/ P. Q& x/ n+ h. Q Y. }3 K6 ]
00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]9 v: s& [" K+ y4 R- ^; W3 r6 T$ v
00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]0 \" G. a$ P9 f5 V5 }( k
0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入06 H3 ^+ ^+ e% q& ^
0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
# a( k3 J" e8 I, @' u' p004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
1 c N8 }/ g5 \. L7 T004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]$ f- u* M1 U) _3 P8 U
004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B
. r' O4 y- Y6 Y004046AA |. 8BE5 MOV ESP,EBP
: Z: J- q& l& u* |5 }004046AC |. 5D POP EBP
4 b7 }' E3 Q0 K8 }004046AD \. C2 0800 RETN 8( j# F: E" u" j: e5 f% L
1 U x+ Y( N T" V6 c
D h/ q! s+ \4 q2 T. o }$ `
( j& v. A6 m: ^* P
^$ x/ ?. c7 K0 |# o3 }2 \) q, I( Y* {; t7 S
知道这些后 就可以改造了
. h0 W% B X9 R先改字库
8 D2 ~3 s4 u8 ~3 x8 k0 u( t6 f
" E( i/ A3 Y8 k! m' Y- L& I% n# ^) x U3 q1 _5 ?5 a
然后改成支持汉字的; `+ m) I2 o( l2 f: H( |% {
把4091d2 的 CMP EDX,0A 改成jmp; l# V' X+ D6 M' x2 K1 \/ U* p- ?
跳到我的代码上去
( ~* k# ?& @8 R4 x d0 J a* i
/ M5 e2 H& Y- E M4 ?' dAND EDX,0FF 去除高位的FF$ r, f. ]% b( d* f
CMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的
I+ e7 Y3 r( d6 `2 S* E& j/ XJL Unpacked.004091F8 小于 B0的就跳回去
; x. a7 I1 G& T; ]0 a8 ~4 D1 K6 R3 ~MOV EDX,DWORD PTR SS:[EBP+8]
( l3 o6 }2 S, s; f8 RMOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
5 {) D9 J5 K h. |2 L# eAND EAX,0FFFF 去除汉字高位的FF
. M6 z# R& g' D1 CMOV DWORD PTR SS:[EBP-40],EAX 存入形参% Y: f3 f4 e. v+ h* m/ O6 R+ J: o* l
LEA ECX,DWORD PTR SS:[EBP-28]* S% G/ m6 o" g* b
PUSH ECX ; /Arg2
& t) T% N5 L9 g+ ?: }MOV EDX,DWORD PTR SS:[EBP-40] ; |
/ y* p7 i9 U; nPUSH EDX ; |Arg1; H' R: h9 c) M; h P
MOV ECX,DWORD PTR SS:[EBP-8] ; |
" }; d- b( h: X4 P- VCALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标
8 r9 V- g; G. `. i' b4 N9 MMOV EAX,DWORD PTR SS:[EBP+8]" _! U6 A+ X5 P, q8 M
ADD EAX,2 call出来后加2个字节
/ c1 K6 x& G. o: m3 \5 r0 ^, cJMP Unpacked.00409217
6 g" Y! V" F7 M5 ~- i' \ I" j3 H$ J ]
, W! E- W( P( _9 g3 g+ t" y, A
4 U0 z$ Y8 S( [# t3 a3 L7 K, ^. b% Y4 I6 I# b: z/ Y! n& i
PUSH EBP
& H3 l. ?+ K% q' E6 x9 V+ {4 Q8 E4 oMOV EBP,ESP
; p( U N ?3 c9 |( ?' ~; kSUB ESP,0C
- `! q& w4 \9 X& \/ U+ |MOV DWORD PTR SS:[EBP-C],ECX/ i+ l; v/ ^7 L* @- D
MOV EAX,DWORD PTR SS:[EBP-C]
: }/ L1 g6 B+ c( gMOV ECX,DWORD PTR DS:[EAX+14]
( A; Q7 K# t% d) k& QMOV DWORD PTR SS:[EBP-4],ECX
: J3 T9 o `7 M6 H$ oCMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)
) e" ~: [+ H6 r- h/ ~JG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片* ]" I' @' f; E% l
MOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)
; C* ^5 X6 r, Q; I9 d) j6 ~- t* tXOR EAX,EAX! H& Y; {: z7 |6 P$ d
MOV AL,CL 把汉字第一个字节 放入eax
+ X" t. D1 t# ySHR CX,8 右移8位取第二个字节
6 ^' @; z5 x; `7 b+ H- Z1 bXOR EDX,EDX. f& r" |2 U+ y8 y3 [
MOV DL,CL 把第2个字节放到edx去- p0 m' N% p0 x6 v0 b1 V
SUB AL,0B0 这里是我自己写的算法 不用再读取码表了6 s# M" y: l/ @# ^6 o( H/ q
SUB DL,0A1# k; i: {7 B. @/ S
IMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16
% G) r+ `+ X% m, DIMUL EDX,EDX,10
/ C( k5 s5 i0 Y4 J, CADD EAX,20 加上我图片原来的高度( q3 \/ B: v- z5 a: H
MOV ECX,DWORD PTR SS:[EBP+C], j8 ~( v5 `+ e
MOV DWORD PTR DS:[ECX],EDX
3 n' a$ c. d6 _6 tMOV DWORD PTR DS:[ECX+4],EAX. f& {, s! g0 { D
ADD EAX,10
" \8 \1 D# ?( T* o7 hADD EDX,105 w! k1 v: a, a
MOV DWORD PTR DS:[ECX+8],EDX1 @" e( S9 s# X/ W1 _2 O
MOV DWORD PTR DS:[ECX+C],EAX* n, O- q# B) y0 G# q+ i
MOV ESP,EBP2 F% Y q3 h0 B
POP EBP
" F3 S" b8 x! c6 vRETN 8' T- Q6 K. ]( T
% P( q! S4 @% |" l
- L( e& C, z1 Y9 m* \" w4 ^ |