上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化 " Q- C/ D0 k1 {! D, n1 O' d
2 Q. @/ [1 ]3 A# c+ G, }在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵 ( B! {( W- Y: }- u, {* W$ w
这里是用bitblt贴上去的 , ~! p8 c" m, @
知道是贴图就要寻找相应的字库和字符串
1 t3 Q$ O1 E8 @% I( e$ J% Q l字库在image文件夹就能找到 / e3 {; I7 Y+ s
字符串被压在了EXE里(脱壳后exe增大了3MB) G8 O1 Q' x7 H, a
* ^) } R3 L; n, u
下面就是分析游戏是如何显示字符串的 6 G! B6 L: m( _; N( a/ o
用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit
( k; b3 N, N: k# l6 q
/ q: I' D' m4 ]0 ] ^* e" z0 N按F9启动游戏, G: A; ?/ P3 f: E$ U: L
然后断在4F24D2处
0 d/ a5 j- d0 a$ r: N这里是读取字符串用的
% b! @8 L: q$ R( X 6 j, w# w3 |. v
分析下- a4 F: G$ F: J2 Q- K0 N
004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit
1 c# t+ T: V! }- V2 F: g! Q$ N+ k# I3 N7 u004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0% o' C# ]& A$ ^. D% P+ b: X: K
004F24D7 |. |03D0 |ADD EDX,EAX
, g6 K! }$ u. }$ N2 |; C004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF7 ~5 }( D' V Y( [$ P- H a K
004F24DC |. |33C2 |XOR EAX,EDX
( r+ D" [+ L' n% F4 E9 g C* \' j0 `2 @004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节
& Y7 l- j, M# \# u3 k004F24E1 |. |A9 00010181 |TEST EAX,81010100
/ o& u8 D) z) v( d004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
& I9 O! _6 w; v) Q
: S# w r/ `& d3 o# D这里要注意一下
4 K5 B& B0 ]7 \' [$ h8 l. u因为是4字节一取 \0处一定要是00 00 00 00- {+ K% n; w7 V0 s, R& m, {
如果要把Quit
0 H( t" V# h6 @5 W0 f- \/ a51 75 69 74 00 00 00 00 Quit....Quit....
7 U5 @9 V. X3 p- q7 U翻译成"退出吧" 就需要在加4字节( F% K% X/ D# ]8 Q$ ?) t
地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]
: Z3 f; b& g( |$ F4 K1 u& m只要把ecx里的地址改成你的就OK了
# [. Q# u8 H5 X0 F" c
; @8 _1 E- u8 o0 R) O! }继续找处理图片的地方
8 c. R# i* {8 g一路跟下来到了: T# `: Q! G4 f: p
- f# I- i. S! [: B% `! t' j) \% q3 o
004F398C |. 83E6 F0 AND ESI,FFFFFFF0
- D6 C: I2 U& O6 h4 a2 I004F398F |> 56 PUSH ESI ; /HeapSize = 51 E* F* I8 y+ R! m
004F3990 |. 6A 00 PUSH 0 ; |Flags = 0
. {5 r F% N. X5 o3 b4 ?004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000
; R3 }. R3 ]& b) U8 q004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc
: V$ t7 `: ]. C8 T: `004F399E |> 5E POP ESI2 q1 ^ E# R r' L8 T( S; W! T7 X0 G
# a+ h( [4 y5 e5 g+ x- t, U& p7 z' K
发现HeapAlloc 这个是用来分配内存的2 X2 D# b3 ], _" M- u7 y' g
F8步过 取eax的地址 就是将写入新的quit的地方" U& q: ^) H* g) |6 p$ p
对其下硬件访问 F9运行& j2 ^) `+ `4 G9 J3 n
断在了403811 h( X; ` \9 t- p y
在F9几次 断在了4091c4
6 o4 @/ B$ h! d& k4 [5 \. F在F9几次 回到了4038115 `. \9 B. O ~( D" W6 g
7 a4 h5 p9 P+ h8 `0 j* ?2 i
确定就是这2处地方了
8 o2 b9 n5 d" e: j6 o这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用/ s* B, i4 W0 u' [
$ |# f7 C5 C! p0 o& \004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]
, M1 I4 d. |5 {6 M7 C7 C* r8 m004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q
% N6 i0 [; P$ o$ U: K: }5 M004091C4 |. |85C0 |TEST EAX,EAX E- C% D& U, a0 U
004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出
0 ]/ C1 `+ n5 q% [' }004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
3 b6 _, A0 G( n, G/ M004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q3 X; T l: H* z% t# P
004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格
% X+ k. w, t* {: F004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F87 q5 q- e7 I+ i$ [: q4 N C$ \
004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]
* Q* r. p) C @) C3 V004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX
: v/ Y6 b9 j& ^$ q3 O004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 00000000
$ P; q2 [0 G5 z004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
8 F$ {8 Y8 j! R004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0
! A# t8 `6 Q. x0 A$ \% u004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]
8 t4 N( Y0 c- P5 A6 c7 v004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX
9 j6 {7 @+ R' p2 B6 Y. `004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
* q2 y- o/ } i I( X& {004091F0 |. |83C1 01 |ADD ECX,1
9 i' U" L. h" p* T, {4 p$ ]004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX- S, d2 x. \( w3 I" g( e
004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE
: H& M1 _$ k, v$ U! i7 ^004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里7 P' `5 W/ u! N3 y2 d' | b9 n
004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)
' u8 r& e/ W& Q& ]0 E" \004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去
2 o8 |, T/ S2 u& E8 I! X00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
! U2 i8 u; ` w2 n: x3 I' V00409204 |. |51 |PUSH ECX ; /Arg2/ C& v9 {" c' h
00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |
' Y( x5 p5 K) X* d! s: y00409208 |. |52 |PUSH EDX ; |Arg1 压入Q3 \. M# e4 V$ G/ ~1 T5 T! G" E
00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |2 q: S2 b; W7 w4 h u
0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键
% N; P6 r4 d6 J& m' W: p00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]; M A2 b j8 G/ @5 y, P, Z
00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u: I) i9 v" a: A4 d
00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX0 _0 ]. Q( p/ s ~# P" }5 N
0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
6 r$ p( X A# p& `9 G& G0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度
6 c" C% s) r; f- Q' H; f( c00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
8 Z e5 t- m, G6 s* D00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],0
- d) H: [2 L0 f) b! I: L3 m; k8 l$ y. _00409229 |. |74 7E |JE SHORT Unpacked.004092A97 h6 }) V& S& l3 s# P" M+ n
2 y% e. Q6 r4 X. d% M
6 `/ d- R7 P- b, g7 \& Y5 T
3 S0 l/ I- \* I8 `" x; H% ~5 O( l4 W
+ ?. y# N$ d" T
3 k0 A8 M8 @9 u& b00404620 /$ 55 PUSH EBP$ t9 N# j4 S: M7 H+ h6 B, F
00404621 |. 8BEC MOV EBP,ESP
9 t1 m8 v' |' D, m+ U$ c( o0 m00404623 |. 83EC 0C SUB ESP,0C
- c/ g2 F9 R$ @00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX 3 m8 _0 a) x' _8 @5 j' j% |! e
00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
4 V6 k/ B% p( c0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]
4 w& r( T6 s( I7 c* W1 g! u3 J2 f0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX
. o! @6 V: p! L8 ]' ~: a00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查" C+ E7 q) X/ L# B# O& [: W
00404636 |. 7D 0D JGE SHORT Unpacked.00404645
6 q/ F, w! F) [) b# |7 r( n00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]
; F/ r# V0 S( e9 j5 {0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL6 O( M8 x: x4 A, D2 G) |) y
0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]
$ ^1 K J. q( `# o00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX/ h6 R" ^: Y, c4 Z' o$ K
00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查
/ i$ P" ^' g7 \7 e) I7 a( B0040464C |. 7D 18 JGE SHORT Unpacked.00404666% f& q; d# J+ N; v/ j! b/ o
0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
' ?4 V& v; U# @8 G* V00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
' d0 |( N4 [7 W, Y8 ^00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]
/ G4 N2 u0 H5 D8 I; D+ n00404658 |. 6BC0 14 IMUL EAX,EAX,14
5 u( {; ^3 ?$ x0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]: y, X: C% c, h( _
0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]
4 e" ?4 ]& |% L- Z* c00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
8 M0 _$ k" Q0 \( o; a/ T3 L8 l00404664 |. EB 15 JMP SHORT Unpacked.0040467B
+ ], e/ a5 l! H- D0 b( ]: s6 ?/ F00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表% D7 q1 G3 v1 A0 Q F
7 `4 j( W8 Q3 e2 B0 @" k
( s0 ]3 T3 Z4 C( L& q! ^3 H+ g* v; }& J# w/ G8 N/ V
0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..
2 R4 ~( }! r( p8 l7 B* x: p; _$ v0054AA58 1B 00 00 00 ...3 z; y Z. h$ K+ x' @
1 Z5 v" I* J& S$ q. _51就是字符Q
6 @3 Z) h7 Q: m1 j% q, `/ G H2D 02 00 00( o, r u9 \. }
00 00 00 00
+ Z0 X& w; ] S# |4 R3C 02 00 00
0 z/ M0 ^2 c: G3 K" k: e- |- ?. s7 s" b1B 00 00 00) R4 B q& B. k) R
1 a9 f" l( D& U9 S
A━━┓
6 R% d4 ~- r) s, [ ┃ Q ┃ 7 K) U! h+ A7 b4 R, X+ [
┗━━B# h% T% j' `/ _9 N6 E) ^3 n) m" S
! |' Q* Y) G' u- a* f2 n& Y6 p R# FA的坐标 (22D,0)
# S+ `6 T! H0 x0 cB的坐标 (23C,1B)
7 s c% ` @5 \! D# o' {. M- M6 M7 |; k3 F
+ Y. x ~4 e) X# h' I
# K" u( q& Z" q) D4 Y7 p
00404669 |. 81E2 FF7F0000 AND EDX,7FFF- T9 I, V! Q% U+ n
0040466F |. 6BD2 14 IMUL EDX,EDX,14$ }" y' O" t5 g/ d3 H$ Z
00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]3 h6 r1 \/ f' z. \; C) K
00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]
* d& d) Z+ d! E1 g2 o! Q00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX% y, g' O @. A: V! n5 \
0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
8 h: p* @% o! x' S0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
- d' T7 A( J: N1 u1 J) d% w00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]/ b$ f' h% k: O- W( B# g( P
00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D
]- w5 |7 m, L* o: A2 V! `00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]! G( o7 c5 G" |" U0 N
00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
1 h& ?- I) w% | X* c6 U: r0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]
" d' K; {9 F1 d7 Y* q0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C5 k: z. M1 c. T% }/ m
00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
3 b/ x- s' Q/ I9 y, A* M( \8 d! C00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
$ R/ h. z v. m* ^; h00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]# K) h& _$ E9 v
0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0
: O: ^; j- a* ?9 H+ }0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
# [6 K: q7 {1 ?8 X004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
' G% F: n1 U+ u6 `# d y004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]
+ o! ~" [, g3 h6 S004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B
0 G5 ~9 r. g7 Z: P+ f% Y' R9 p004046AA |. 8BE5 MOV ESP,EBP
+ ~! E# ~% H4 Z7 y004046AC |. 5D POP EBP( Z6 Z9 T$ j8 G
004046AD \. C2 0800 RETN 8
9 d$ N( X" {# G; h; o6 o* |* W. Y4 f4 k! K- r7 Z
, p1 U% [9 p& z. }
) o9 a. t; p. T1 o8 l+ }. I6 i9 N: G1 S. i# t" g; ~) S2 y' E
/ f$ a% `: i/ O/ f F1 o知道这些后 就可以改造了, m/ ]$ D. Z! ~, V
先改字库) r9 J7 K/ Q& r' G
8 f9 B: r" Z+ l W
7 {( K) U# O/ Z+ q( R然后改成支持汉字的
6 L& D: O' B3 \% [, ], H `3 } _把4091d2 的 CMP EDX,0A 改成jmp
, K9 |+ O6 w; M% v3 t& i跳到我的代码上去' Q1 N. V) G) X# g
6 ?: o- u' I9 @* l0 u1 O# n, iAND EDX,0FF 去除高位的FF
) b- p- ?: C! \# JCMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的
5 d/ e; ?/ c5 KJL Unpacked.004091F8 小于 B0的就跳回去8 Q8 T3 {5 P1 j) c# W+ d
MOV EDX,DWORD PTR SS:[EBP+8]$ p# |; M. U7 v) e" q
MOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
3 R7 ^0 \* V" Z, MAND EAX,0FFFF 去除汉字高位的FF
1 |) }/ ^: v4 Z; V6 R* ]2 L" PMOV DWORD PTR SS:[EBP-40],EAX 存入形参
4 l8 Q o% `- Q4 d, K' {LEA ECX,DWORD PTR SS:[EBP-28]
5 O$ C8 t3 N8 P. W: RPUSH ECX ; /Arg2
+ j+ }1 K% ]( \" [8 IMOV EDX,DWORD PTR SS:[EBP-40] ; |0 \; X9 J3 m( w" @% T5 F
PUSH EDX ; |Arg1
) a* G) K) O$ r2 |( g2 [3 V- ?MOV ECX,DWORD PTR SS:[EBP-8] ; |
5 ?3 }/ \4 M! H0 b, @. [CALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标5 h- s2 |; v# ]; }
MOV EAX,DWORD PTR SS:[EBP+8]% I# g+ a( Q/ ]
ADD EAX,2 call出来后加2个字节
& c7 H! |; \4 ~8 }JMP Unpacked.00409217- p2 v8 x2 O0 E
5 R1 X( P' h, H' l% \
/ N7 l+ E1 V1 {3 d' @/ O7 [
% x' S. o W+ s2 H R* _" s1 m& G
PUSH EBP; K1 ~% q6 p0 M( c& b4 H
MOV EBP,ESP
7 D' i; B: e1 F& ?) s& ^SUB ESP,0C
. h& u8 I+ n1 @5 N, H" h( YMOV DWORD PTR SS:[EBP-C],ECX% t1 M; D! e& I5 N r4 t
MOV EAX,DWORD PTR SS:[EBP-C]3 S+ A) K/ t5 j
MOV ECX,DWORD PTR DS:[EAX+14]
: b( B, G5 z5 ^5 g: W; \1 M7 eMOV DWORD PTR SS:[EBP-4],ECX
* |; R. z. q6 P% _3 f/ C+ W. K/ ~CMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)
9 x2 \' n/ l, Y7 Y: ]JG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片
& v* W9 d# G1 b! y7 z- p4 hMOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)
/ ^: t2 u$ T, [9 q/ A* dXOR EAX,EAX/ @* ]5 ?. j* G7 c6 }
MOV AL,CL 把汉字第一个字节 放入eax$ f( t1 j+ M! r; M5 S
SHR CX,8 右移8位取第二个字节0 t0 r5 C y( S6 n
XOR EDX,EDX
2 x3 R6 `) n$ _, UMOV DL,CL 把第2个字节放到edx去- F3 S' f7 E; }4 `
SUB AL,0B0 这里是我自己写的算法 不用再读取码表了+ d7 Y( b( O: l$ x! }1 n- A
SUB DL,0A1
1 g# Z0 V, u7 A9 wIMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16* z* p, k9 N( s- X; D; i4 A; _* N
IMUL EDX,EDX,10
4 M* F; N) C3 D$ s/ {- VADD EAX,20 加上我图片原来的高度
9 F2 v2 h8 }1 ^6 c- ?MOV ECX,DWORD PTR SS:[EBP+C]2 V; U" B! T6 g1 }
MOV DWORD PTR DS:[ECX],EDX/ j$ D/ ^& K+ l5 Z
MOV DWORD PTR DS:[ECX+4],EAX
0 D8 s8 v2 v1 ^ADD EAX,10
9 G F$ l s% g/ ~1 a0 F0 ]& \ADD EDX,103 n" ]6 j3 ]! E. F6 |
MOV DWORD PTR DS:[ECX+8],EDX; R' ]% G& I' N$ ~4 w0 U& o
MOV DWORD PTR DS:[ECX+C],EAX/ Z% R/ ^ u4 E/ f
MOV ESP,EBP
( W( H; s+ e8 }# b2 L GPOP EBP
1 Q/ V& }: J) |5 T9 P7 V7 ^RETN 87 g, n$ j( h" j1 S4 V
7 C ]5 B" k# g, m
& k* r; w) ^7 T. `2 I1 y |