上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化 + f( f: r- H: B/ I4 p
; }& \8 Q, `% z+ N3 \- g% W+ V在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵 , i. ~% v# j3 ?
这里是用bitblt贴上去的
: r& C M/ e( [" i* a知道是贴图就要寻找相应的字库和字符串
! `# v4 w5 p" y+ Y8 W8 ]字库在image文件夹就能找到 2 J" ]+ l0 c8 D |1 X& R/ \
字符串被压在了EXE里(脱壳后exe增大了3MB) 6 K! O2 t$ c/ T! Q7 t9 t
# m6 f+ p! K0 I5 T7 L
下面就是分析游戏是如何显示字符串的 . m% G, p$ Q3 M9 h: [
用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit
9 E8 _9 B: ~' J* G$ X# t6 @% `: J ! v0 }7 W/ w! U# A
按F9启动游戏
1 R3 _1 \( |: |7 q) q8 z) j然后断在4F24D2处& z6 [& r; ~& A; ~# C
这里是读取字符串用的
. ^& {2 {, h# @7 c9 [
^, B& R4 P$ E# t8 F( A3 i分析下2 o/ M( }1 M$ e
004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit
" F* {1 Z) L9 m! [004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0# E& h" F6 H+ E( {
004F24D7 |. |03D0 |ADD EDX,EAX' b" o# y, o. n2 M. O& X
004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF6 x/ G" ~; K+ i1 X# O* o) |% \
004F24DC |. |33C2 |XOR EAX,EDX
- G8 H! J& F& q( K. j) p5 p: H004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节
) P+ O8 j; E0 A3 d004F24E1 |. |A9 00010181 |TEST EAX,81010100
$ X- p) a' _/ F9 D5 I: W# O004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
# s% y# i( A, a) H k2 r1 _7 X* k# G( U @8 u1 ^
这里要注意一下
8 _3 s9 E) `6 e# G因为是4字节一取 \0处一定要是00 00 00 00
1 J4 w# j0 m% j( K如果要把Quit
2 {' ^, U0 h( b51 75 69 74 00 00 00 00 Quit....Quit..... S5 c- {, r* H
翻译成"退出吧" 就需要在加4字节, p& X1 F+ s$ J' S+ f9 Z$ ~
地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]* R. D& k c' j6 X' w
只要把ecx里的地址改成你的就OK了, ]+ W! ]8 S* C: L% m! k
9 m4 Q# O8 Q. n& u8 Z8 U继续找处理图片的地方
9 Y2 {3 ? | t一路跟下来到了
: w* ^2 [# U& j: z" K+ N, _ ( | U7 E8 o+ M# M( q: s, z1 E7 H
004F398C |. 83E6 F0 AND ESI,FFFFFFF0
' z2 R& D+ ?6 t/ a5 ] w004F398F |> 56 PUSH ESI ; /HeapSize = 5) V; H8 ~( c! v
004F3990 |. 6A 00 PUSH 0 ; |Flags = 0
1 W x+ b& d& ]- }: I+ Q004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D600002 [" ^; K( u, k! l
004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc8 `1 N' }6 f D# L, c9 }1 r
004F399E |> 5E POP ESI
8 u. B" ?% O( \' ]) Z
, r" c8 m5 _# Q' R _& Y% @发现HeapAlloc 这个是用来分配内存的2 d9 h _5 |2 w9 @
F8步过 取eax的地址 就是将写入新的quit的地方
' y! }+ ]& }0 F对其下硬件访问 F9运行
0 M6 G7 f$ p$ ^7 [: p4 b W+ G+ f断在了4038119 J6 O4 I' g) w4 W
在F9几次 断在了4091c4
. R' q9 d8 U, a! s, V: H z! d/ V) K在F9几次 回到了4038116 {8 n4 o, Z$ T
5 h0 K* k4 K7 h/ r
确定就是这2处地方了; B2 D' k3 B" ]( g- f' @! G$ e
这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用
: ?3 ~4 F( S* Y' G4 ~5 E: ^
1 C" w! [% K( g/ V004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]
' |1 K3 G9 [ y0 p* e. Q7 l004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q
$ h2 r( H2 _# U004091C4 |. |85C0 |TEST EAX,EAX
# C+ @3 Q/ L* d5 D' f" p5 K% O' i004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出
) C0 h/ B# j! E& U M# f; R004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
$ U9 ^% k; M3 b1 b6 E+ V004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q
! \2 I: V8 J6 y$ c6 L2 ]9 \; I004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格
' R1 [. z; Y6 [, Z7 J$ \4 j' {004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8 Q z) w, R, m) Z
004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]' T3 U X( ?+ Z: |
004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX
0 f8 y% `8 ?1 ?$ `; h004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 00000000
, t8 T3 ^# w# L* D8 Y1 q0 [ [$ |1 Q004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |6 @9 C/ l% R9 S
004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0
?" B- c) D7 E0 i* r# E004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]
( ?; y: }/ {" |* V' j2 x004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX2 n/ G/ K; s8 D; |6 y
004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]# V5 T" R/ _( R& i/ R8 T1 x
004091F0 |. |83C1 01 |ADD ECX,1( X, _4 b& X3 O9 e# O) i4 J* ]& {
004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX
+ W4 m+ W' s, a: d- k* s, _004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE
7 C B! y5 X. [004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里
: K9 L) n" E$ A004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)$ p6 H$ m- K0 ~
004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去9 f/ z8 a) a _+ W. ~5 P
00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]9 \/ ~ ~" c$ O: n' W) q$ J" ~
00409204 |. |51 |PUSH ECX ; /Arg2
5 [" V- q& A) x: ~4 U5 n1 J00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |6 ?8 s3 S/ J' Q4 V! m- f! A7 y* e- Z1 H
00409208 |. |52 |PUSH EDX ; |Arg1 压入Q2 g* F8 c6 O; Q; e: x
00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
* ?* J- ^! B! T! h$ r0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键
- r: z. }/ }5 L3 L$ Q00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]% ^5 Q) ?+ |& t
00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u7 A, ]3 y) U1 g! T& k
00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX
: B7 ? k2 @4 J/ g0 ?/ H5 x# s. \0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]* b/ L ~. Z. Y1 p J" h/ @0 g. B
0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度; M" Q+ I' r" l& n% r* b' h
00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX& c$ Z: J6 n- f) N
00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],0
' q9 l6 t0 r" J; b00409229 |. |74 7E |JE SHORT Unpacked.004092A92 K: g; R0 V3 D$ c# o
, f1 L' i* A7 D
8 z6 T6 _% B! H1 b- w2 l
8 q# c# M9 ~; o3 v
$ c; T2 I, b* D' d/ C( z0 ?( Q Y+ ]) B) y8 ]/ c! m3 C5 V
5 V- [- E7 b7 ]3 B& Q# X9 f00404620 /$ 55 PUSH EBP
& `4 n& c; j) r0 X! ?6 y00404621 |. 8BEC MOV EBP,ESP
) W4 E7 z3 ]! H# S! A: S- f00404623 |. 83EC 0C SUB ESP,0C" f* N3 i+ F( V) {7 p% g) ^
00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX
& I9 T7 p2 l% W, D00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]2 Q1 w/ S4 ^6 K/ w. x
0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]/ ~( M2 @) a+ [3 Z8 \
0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX
+ G) T- y4 ]7 `" N4 p* E" w) }* V00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查
; A* P# ~4 e8 p& n* w/ f00404636 |. 7D 0D JGE SHORT Unpacked.00404645
8 p. F; E6 `5 v4 {. [# ?5 h00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]. ^ ]7 P3 U _% u# d0 e8 Y
0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL3 c" @9 V2 p. X1 ^
0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]
1 @, B; T6 }0 o. B# ]# c00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX
: h. ^& p4 ~ p+ u9 R d2 J00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查
% R$ D7 }9 o3 w L9 n- {& ]0040464C |. 7D 18 JGE SHORT Unpacked.00404666
5 w8 I) `! i! r- }$ g2 V0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]. H H& C y. ]) z+ @ m j
00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
- [$ M+ A, G; a: l/ `& l00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]) l4 e+ k' }" J$ {7 ]# K
00404658 |. 6BC0 14 IMUL EAX,EAX,14
# W7 ~# R6 H! K0 p- P; p0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
. _' e. b, A" n0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]) U! D" P9 m0 @9 U* `6 k; l
00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
! S& W0 k0 f; y) F00404664 |. EB 15 JMP SHORT Unpacked.0040467B
% @' m z% c- O3 N9 n" X00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表+ E( H8 A8 }6 [" e
; c5 l6 G$ B; O
6 G9 B- _0 H8 }2 p0 D; v2 g a7 P3 G# N, @& s0 J
0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..
9 l- P7 X+ C. G `2 g) v0054AA58 1B 00 00 00 ...
% e* Y1 V U% d+ f3 W1 n' s0 q2 ?% a: j3 j. i4 L8 [% d
51就是字符Q8 x9 z; \; d, D8 A/ C& W2 ^9 z, g) S
2D 02 00 00
! G% Z X" `) o00 00 00 00$ N# Q0 N6 y- } J5 c
3C 02 00 00, g3 a- p; |' ]7 J( k* O( ?! } ^
1B 00 00 00
( E. v6 `* Q) \% S0 V- o- i. C# p: C& R% _7 q& e1 x
A━━┓
: |. I0 n4 W3 I; u ┃ Q ┃ * S; V$ X( p4 J% s2 X; J
┗━━B
" H; e& L: N: |: _4 r4 h/ I2 T3 Y5 w' ]' u
A的坐标 (22D,0): Y K& h' h$ L9 d" b2 e( C$ n/ i
B的坐标 (23C,1B)$ s8 z. h+ R/ i$ S5 ~' ^: F3 B0 [
% J' n4 W9 g' ~$ N r$ k9 t, M" w+ q, G& }
# a* b r0 j$ Z- l- I0 d# o) W9 |
00404669 |. 81E2 FF7F0000 AND EDX,7FFF
' n8 a9 P- B! n! q0040466F |. 6BD2 14 IMUL EDX,EDX,14' w9 B7 u7 @ v% K7 L1 n X" f4 i' x
00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
0 m2 b: }; X+ K5 L00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]. Z% ?2 f# a6 j' P
00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX
$ i: `5 R$ C7 \' m. c C0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]! Z' X9 W' ?& P. k- G
0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
K" Z1 _! K+ K; |/ E- y0 p7 W00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]- }0 j% `6 G# S
00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D3 J I( a8 k3 [9 B9 S9 @ b
00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
1 R! ], f, v0 }00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
$ k! F8 O2 |% H. g5 c0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]: g8 C3 [3 v- A
0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C/ y+ {9 o$ M9 K$ P4 ?7 b
00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]2 J: U. E1 i% t3 X3 E5 u3 r
00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
! O) z$ l: Y0 o2 a00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]
) f3 A- S0 m+ V7 K5 Z* ~/ \: e! @0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0! N; N0 S$ W5 v# J( p, T; N% H
0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]$ M, r; c# e1 w+ I5 R8 _/ p' K0 i
004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]3 j% f8 _# u/ m1 [; p
004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]
( k5 s" x4 \7 x3 O004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B
' M! c; x# ?' H" {) g. R% u004046AA |. 8BE5 MOV ESP,EBP% C s% i/ Q- P+ ~) z: ~' Q+ b
004046AC |. 5D POP EBP
- j; H. k& U9 m004046AD \. C2 0800 RETN 8/ f1 B! P( S. X# K- n6 a
: T# G+ t" C% c5 \' G
& ?" @! ]& C% L+ A
5 A1 a' ^2 ]1 f L: F! ~+ O* L& N- c$ @ [7 {4 ?
8 d* l k3 ]$ v& e7 b8 ?
知道这些后 就可以改造了
$ ^! ]1 I4 L, Z$ O% x先改字库
0 L0 a. X7 H! J- S
* P, z" [4 B( z* m9 x; x( s8 K9 _4 f, x; Z: x
然后改成支持汉字的
8 ]( s+ n. J5 ^8 c把4091d2 的 CMP EDX,0A 改成jmp9 K/ }; D. f/ O! \6 ^
跳到我的代码上去
2 [* X9 i- j. x
. A* v0 j/ z5 ]0 c# UAND EDX,0FF 去除高位的FF
% p) W v/ _, j2 G0 DCMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的
' P- g2 I$ t# D. RJL Unpacked.004091F8 小于 B0的就跳回去
( ~2 ?& R' u) Y# i2 Z) j% P" DMOV EDX,DWORD PTR SS:[EBP+8]! i" C3 ?4 X6 J7 u2 Q
MOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
6 ?" r' `5 ^5 [AND EAX,0FFFF 去除汉字高位的FF4 y- F5 f. u4 ?& d
MOV DWORD PTR SS:[EBP-40],EAX 存入形参
' g' b, Z( u5 X B+ qLEA ECX,DWORD PTR SS:[EBP-28]8 u) \; H1 c/ z7 m0 ]6 P/ J
PUSH ECX ; /Arg2
+ U( |/ w. H* b+ k( u. ^8 hMOV EDX,DWORD PTR SS:[EBP-40] ; |
1 a7 o; m' A0 Q2 J# y$ g& hPUSH EDX ; |Arg1
; A' c( f8 t7 G4 g6 J. k% qMOV ECX,DWORD PTR SS:[EBP-8] ; |* K ~! c% ^5 Z8 v6 P- ]
CALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标
! V+ A: b) _ r2 Z' ^2 X# t3 GMOV EAX,DWORD PTR SS:[EBP+8]
' I# `. r$ c m" J0 S# @ADD EAX,2 call出来后加2个字节' {; n0 L* u, ?4 h% B7 w5 ?* Y) J& r
JMP Unpacked.004092170 T. \/ O7 m: H# ~/ _- T
7 F @0 m/ A; R5 ]1 c
+ w ]) c' r: `1 R7 q; |; a0 d3 f: B) s3 b
- K X% ]2 [" o# l7 m$ ~2 GPUSH EBP; Y, r4 x. X9 u
MOV EBP,ESP6 K# r! m- a+ J# l1 z
SUB ESP,0C3 G* ~' U# r2 [* B9 ~- q
MOV DWORD PTR SS:[EBP-C],ECX' D8 k$ [( f$ n7 U
MOV EAX,DWORD PTR SS:[EBP-C]/ d$ l: y. r/ J4 B- H/ f+ S& Y/ D1 u
MOV ECX,DWORD PTR DS:[EAX+14]" n: t, [ U- B% y/ n( ~/ i1 Y
MOV DWORD PTR SS:[EBP-4],ECX: A. A& }5 t2 O( Q/ J* e* g
CMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)2 Q" Q8 }0 k1 I( b" ^0 z+ h
JG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片; n% W& O: x+ M# Q' J) |
MOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)
* H" n8 G) ^, C/ iXOR EAX,EAX
1 T4 A* p# ?* t6 MMOV AL,CL 把汉字第一个字节 放入eax3 a4 d( T. r( B, D: T
SHR CX,8 右移8位取第二个字节$ ^# }# x; Z% q$ p0 ]6 G
XOR EDX,EDX! i0 H" s0 b9 M+ }
MOV DL,CL 把第2个字节放到edx去
! S E1 J7 H4 pSUB AL,0B0 这里是我自己写的算法 不用再读取码表了
& g/ E4 |( P# T: v8 ^SUB DL,0A1
% y$ V7 s- ?* ~1 E4 X2 QIMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16
6 A$ X5 I3 G* X3 Y) sIMUL EDX,EDX,10
6 v& F$ a: H$ T, E' J8 v, sADD EAX,20 加上我图片原来的高度
; L, ]& ?+ G( `+ QMOV ECX,DWORD PTR SS:[EBP+C]4 h8 t, j+ n; b$ ~6 p1 w" E5 v
MOV DWORD PTR DS:[ECX],EDX
4 r1 q8 `! m7 E" r; v4 pMOV DWORD PTR DS:[ECX+4],EAX
' K0 J/ G" A N: m+ D5 XADD EAX,109 Z- l" Y5 {7 }0 v& H
ADD EDX,10) N p: W2 W" Q. v$ _* R& R4 X: S
MOV DWORD PTR DS:[ECX+8],EDX! o" E2 e$ l& ]# t$ f
MOV DWORD PTR DS:[ECX+C],EAX
1 W& L8 B9 C2 Y+ TMOV ESP,EBP! q* E% J9 _- [8 @& L0 s
POP EBP& G5 z% X$ s% s* M C) e
RETN 8
$ r3 k- Z( \& x7 V. E
/ p# w& q. u2 C1 _+ F3 Q0 z" P( ^% {5 R9 \! K# @
|