上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化 0 a) q) [, N) m
( {2 T& c6 W. E
在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵
% [; _' @. r( C/ R9 N这里是用bitblt贴上去的 . N- K9 g# ^& {* n
知道是贴图就要寻找相应的字库和字符串
4 u: F) L/ [4 v7 G/ [& }- k字库在image文件夹就能找到
0 d5 p" T% y# W! F% Z! S7 o9 z/ K字符串被压在了EXE里(脱壳后exe增大了3MB) ( h5 W6 l9 a- C8 H; E# C6 _
, ]7 Q% ^2 e* ^7 n. V9 e
下面就是分析游戏是如何显示字符串的 ; y" | l$ S# a1 u, O. ~ L* P# J0 U/ R
用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit
3 a6 E0 n% \# n0 k R 4 `! J& G! U! e' ], ] ~
按F9启动游戏
) `# b: v* e" ^: j+ U1 v然后断在4F24D2处8 R8 V# [. @6 j3 y
这里是读取字符串用的
& r. N" c$ @- j % h0 @7 S; b' C8 F6 j
分析下
/ m2 s8 z+ D0 M8 I1 r3 n7 i004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit
' G: z0 [9 w! ~1 P004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0
& c! q0 {- S/ P( o* X) T/ B, u004F24D7 |. |03D0 |ADD EDX,EAX* X. t; v1 K! ~: u
004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF) W- K' s/ K0 [
004F24DC |. |33C2 |XOR EAX,EDX
1 i- `" o$ m1 g& M$ V004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节& j* m. f3 i: F7 D+ B1 b
004F24E1 |. |A9 00010181 |TEST EAX,81010100 / ^9 l: g3 q9 \# ?9 ] I }
004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
* C* V' Y* Q& ]1 F3 X
2 a2 p. h0 w5 g这里要注意一下$ F/ ~" o: R7 v4 e$ E+ U4 ]
因为是4字节一取 \0处一定要是00 00 00 004 H; ]6 f$ Q* r. e; m8 Y/ d7 O6 S
如果要把Quit
2 q5 |1 |$ J2 _51 75 69 74 00 00 00 00 Quit....Quit.... M6 Z0 `$ v3 M$ u$ K+ v' k& R1 L9 P% `
翻译成"退出吧" 就需要在加4字节
' }$ T* ]) Z4 B9 c7 T) x地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]
) j7 I9 \! F2 N; V只要把ecx里的地址改成你的就OK了! g( u* l1 }( p: K; V2 V
8 W" Q" R/ r' ^( L1 `) u继续找处理图片的地方# ~4 d" I+ D# k$ X/ R K
一路跟下来到了: s: S1 Q/ ?7 s- ?
; x2 ]- c7 T# w& ^004F398C |. 83E6 F0 AND ESI,FFFFFFF0# q) u3 d! {" [2 d0 y
004F398F |> 56 PUSH ESI ; /HeapSize = 5
: {' H, i8 _7 o$ _ o5 p- _004F3990 |. 6A 00 PUSH 0 ; |Flags = 0
' T% K/ j2 d; W$ b( C8 Q9 [- R! @9 h004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000
! e$ F, {. m# D. t004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc& s7 G* Q [4 q1 Y+ m7 p* j
004F399E |> 5E POP ESI: g4 u1 v- G0 D4 v
! L0 K8 z. R7 U0 x发现HeapAlloc 这个是用来分配内存的
W9 d7 u/ H' i D. f. SF8步过 取eax的地址 就是将写入新的quit的地方
) f2 ?. q$ C! D/ Y" X对其下硬件访问 F9运行9 n) Z% L' l4 P* L7 u" }
断在了403811& H% ?6 z+ L6 j
在F9几次 断在了4091c4
$ {' @3 x( p/ Z* e. m在F9几次 回到了403811
6 h1 l& G `5 v; {' t( L; P, Z L
. j0 ]# E1 ~8 t' S, f确定就是这2处地方了
9 L; {; j% q: L( D7 h* V& I4 j这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用. J7 r1 t q) y+ U
$ d. W4 K: V: Q1 h6 N7 Q6 ]
004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]/ c5 B8 ]! V: D5 E1 D0 ?
004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q
$ l% W4 b8 W% x( f004091C4 |. |85C0 |TEST EAX,EAX
: l. ?" d3 X* z" x004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出% B0 [1 Z! a9 o* o N
004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
7 Y% k0 V# ^, b# Y# h4 ?1 z9 X: \5 \004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q2 D$ g' E" e1 F; V0 l
004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格
8 |5 e; F6 c- i- @& X004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8( \, Y7 L: M: B
004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]. T( V( T1 \; ~6 `% `
004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX0 k5 a3 F* F' t8 @& O9 y6 W" d% N
004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 00000000
* ?! Z: ~; W2 R3 ~( y004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |6 S M: B' K. P. u
004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0' V ~# v* p" u2 y/ f
004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]; e: y; E; i9 H
004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX" }4 d3 ]5 y( Y, R& l
004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
; p [ k5 d% S. U* ?004091F0 |. |83C1 01 |ADD ECX,1 j7 a! T5 z) a3 g, {9 C4 w
004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX
+ z! H- B! i1 P& u, |004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE
: z3 y% h) J- ~, I4 B$ X$ }004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里" Z5 ?% o( w- g+ j( Z1 t
004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)
; r) U5 {" A, w; S, ] P004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去
$ P8 R/ L6 M* q" ?9 K* i00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]' l, e$ _9 p6 ~; J: u; j
00409204 |. |51 |PUSH ECX ; /Arg2
4 ]$ n8 }$ }5 s2 n6 R% D00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |
% x- L/ [% V/ z9 p+ y# {00409208 |. |52 |PUSH EDX ; |Arg1 压入Q# N+ J5 h0 e: y6 j
00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
$ @' ]& y) ]7 D0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键
7 o4 p5 O; ?. h) v00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]
" o* s# E J9 g2 H8 m) {& k3 Y00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u
; C; }+ _) Z7 [! P' x; E& p00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX1 R: ]8 R" r. ~* V4 A1 ]! ]
0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]' F$ l; ^% b0 X
0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度
8 p q+ V$ R8 X! ~2 |$ `00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
, j% s% Y* i8 \4 p00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],02 V: m2 X2 I; I5 s0 b
00409229 |. |74 7E |JE SHORT Unpacked.004092A99 T" b' N9 s2 D* \' I1 _% N& q+ F
+ ?* t& u2 n3 S$ Q1 G7 ?- z& e0 G" R2 t9 q
! F2 S6 U7 |, z# D! U M- R; M5 a3 E' s% g
s) U& S2 p# V3 L" x/ T, M1 B: E' C
% @) ?! `. @" s% `00404620 /$ 55 PUSH EBP2 `8 Z1 K* S* W. C" A; S
00404621 |. 8BEC MOV EBP,ESP
8 u/ Z9 |; i4 l9 k% F h+ A00404623 |. 83EC 0C SUB ESP,0C# ?. [' ]" }# u4 l
00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX 1 O& ], J+ [; g% z$ O3 y4 C
00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
1 M) `; L% z8 T8 ^7 X: [0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]
?' k# ]7 B. U* }0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX& n" s5 f$ }) U' e
00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查
' a* C3 F& e9 H00404636 |. 7D 0D JGE SHORT Unpacked.00404645' }# ~8 b: U2 ~0 R2 c p
00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]
% c9 ?& w3 Z6 d0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL8 L' u2 _8 L' E6 E! [! G( ^8 {
0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]8 q2 O) o, w. K2 x
00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX
& U* W7 E. p0 Z: E00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查
: }3 p8 f- G( X0040464C |. 7D 18 JGE SHORT Unpacked.00404666- O7 U# m4 k+ i4 G
0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]' s+ j) \4 }* G- v
00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
# f* q0 D( y$ Y' ~# @: K4 G00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]$ L0 e+ |" p2 B2 a3 { c3 l* T
00404658 |. 6BC0 14 IMUL EAX,EAX,14
+ n" {0 }. P0 F0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]4 G# \& `. f" t" Q
0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]
7 T0 J, j" U+ S z5 C& }. p# L00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX2 d/ R$ d: I) T7 s
00404664 |. EB 15 JMP SHORT Unpacked.0040467B
R) r# q& m* D7 U00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表8 i+ O) I: `, f, Q3 p' {
6 k8 k3 H) i' O% M6 S; x
$ N6 E2 \7 P4 Z+ }: ?/ f2 J; C2 }4 w/ ^: k' o, L+ ~
0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..+ W. Y. ]' ]: g( ~4 j
0054AA58 1B 00 00 00 ...
0 K) E& ^4 F# g( _8 R
8 g9 y7 @* S% m$ D51就是字符Q
, Y, H- N, |! f5 {& |# W2D 02 00 00
# M+ F) w6 ~. x' y# m' A00 00 00 00: Z' `' S- N% F( i! B
3C 02 00 00/ w3 p( x; j, C; b; q
1B 00 00 00 m4 G* U9 k$ U/ u6 b
/ W# x; J8 ~5 I2 U2 B& b* {8 g A━━┓ ( d1 `4 r( R+ y) X, G
┃ Q ┃
7 C* A5 D' w5 g% p* m$ q ┗━━B
- e! ?8 t9 W2 J2 {( V1 o/ y* M$ f4 ?
6 ]7 j/ U! b6 }& p% ~7 i$ aA的坐标 (22D,0)# l# o: U) s- t6 C: ^
B的坐标 (23C,1B)- a: \/ B1 b4 s! B0 r" K; z: }
, Y9 f. q) I1 |2 I8 z5 w& x& _* ?2 o( y- d4 Z2 {
% B1 i- L" E. X3 E00404669 |. 81E2 FF7F0000 AND EDX,7FFF
5 B, z6 W& {% z1 \7 ^; }0040466F |. 6BD2 14 IMUL EDX,EDX,14, G4 c# {6 u' D8 b% P& q. W. N
00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]4 M- K' H! k7 R$ W/ t. @
00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]1 Z' h2 B4 l7 t7 V
00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX
; Y. W- Z1 q+ D) b0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]) ?/ p9 k! t& G5 q; I" f
0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
8 h5 B8 K/ \) B, ^7 f00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]% D! ]* W! P l
00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D
+ j+ `* \7 P! [# h9 Q00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]1 c- e0 N: D8 \# F M i
00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]" t8 M# k4 s# n& a; M* q0 v
0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]
! ?! e( }4 C+ _8 n! N V0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C# H, }0 X" m! r2 T: _8 d* g$ z" S
00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]/ c) i4 C+ E% B% P8 ~3 M* h
00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
7 O. @* b" W4 ` ~! U3 y6 a/ [00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]$ M, I0 i$ `4 ^
0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0
" m" A8 O' \; Y0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
& U5 F& d4 W! r3 S% i3 w. R004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]. h3 R( D q$ `1 ]3 ^2 W
004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]2 @ C ~! x7 \8 }! M; Q+ }* I' V+ h
004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B
! o# l3 M4 N8 e' i% p( f004046AA |. 8BE5 MOV ESP,EBP
3 ~4 `2 M9 m' ~2 W0 _! x( u& G/ C9 K004046AC |. 5D POP EBP/ g# F" J6 y% f% w7 k/ `3 F
004046AD \. C2 0800 RETN 89 `+ a9 z6 D: N
2 G/ h+ r# C& l. U6 m/ E \4 D: `/ ^
1 Z' a% I7 `* n0 ]$ n5 l1 [( T( J8 G& E- w
( ~) t8 ?. W5 ~$ j" [
知道这些后 就可以改造了* X/ }* q3 e9 Y; s' y6 H/ N
先改字库
5 s" [, b# g" j2 Q7 e$ E
' l9 {( }+ B: X! r8 J4 {% N% n% h; p9 z1 a6 `
然后改成支持汉字的
4 o" j' S- @5 K1 o$ m+ ?把4091d2 的 CMP EDX,0A 改成jmp
9 E' ~& p4 T* D% F跳到我的代码上去
! x8 ?7 m* H8 P( ]6 m5 y- [0 k( y1 A/ w
AND EDX,0FF 去除高位的FF
, k4 ]2 n0 d3 }CMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的
8 o& b6 x" C7 U. X8 LJL Unpacked.004091F8 小于 B0的就跳回去2 `: M e! h s* h
MOV EDX,DWORD PTR SS:[EBP+8]6 _; u8 U b9 A* [
MOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
) i6 e3 f) A9 c# IAND EAX,0FFFF 去除汉字高位的FF
) V2 d( F. ?9 c9 C$ Z. S& R1 mMOV DWORD PTR SS:[EBP-40],EAX 存入形参4 K( O$ {' h/ j) y4 o/ u
LEA ECX,DWORD PTR SS:[EBP-28]
1 q4 c* S9 l1 p6 R. I# HPUSH ECX ; /Arg2
]% O2 h' d. ~% K0 F+ |( bMOV EDX,DWORD PTR SS:[EBP-40] ; |4 c R; K& s- c! p, {
PUSH EDX ; |Arg1
$ h' U1 u- v) i$ u& v: p" NMOV ECX,DWORD PTR SS:[EBP-8] ; |# @) ]' t6 x: S# `9 y4 `- Z
CALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标
4 R2 h; \* s7 d QMOV EAX,DWORD PTR SS:[EBP+8]
4 P- V" j/ D$ H7 g6 a; k- c& @ADD EAX,2 call出来后加2个字节
6 r4 x( b8 I. V; X1 |JMP Unpacked.00409217
+ b6 ~, C" N+ G8 G) n6 H/ U1 c- k& L
1 p2 A' }) _0 n4 }/ K1 g" c, h3 F$ A% j' k" d0 [
( S; L1 ~$ q; X3 i3 _3 g3 V
( Q; X9 a! z# Q! kPUSH EBP
4 r& j2 @ A2 w" F& |% K9 l2 aMOV EBP,ESP
4 k# T$ V0 \* BSUB ESP,0C
/ p7 }1 L6 `! ^& h5 bMOV DWORD PTR SS:[EBP-C],ECX, [4 V5 R" t3 w( _* \4 I
MOV EAX,DWORD PTR SS:[EBP-C]
6 H( Z) e. X! {( k6 @MOV ECX,DWORD PTR DS:[EAX+14]/ p& i$ {% ~' `/ x
MOV DWORD PTR SS:[EBP-4],ECX6 j& E5 f: R% h7 [
CMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)9 x7 o+ c8 M) ]" e# t
JG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片, a9 i& z+ V& P2 p
MOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)
" _: J k% W# u4 j( sXOR EAX,EAX
. c; l0 _6 @; ?! h5 \9 sMOV AL,CL 把汉字第一个字节 放入eax: X! h2 b9 C1 R) d
SHR CX,8 右移8位取第二个字节
6 B% h& I; {, aXOR EDX,EDX# r. x$ y6 [- k5 f
MOV DL,CL 把第2个字节放到edx去6 s' s, H0 n/ y. L5 z
SUB AL,0B0 这里是我自己写的算法 不用再读取码表了8 z& y6 l6 r" G3 j% H- c
SUB DL,0A1
: [2 @1 q+ ]9 F3 }! Z- w4 y0 B9 VIMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16
* Q* |: b" a* @; {IMUL EDX,EDX,106 A0 T5 t; y+ S
ADD EAX,20 加上我图片原来的高度2 c1 M" k4 {" ~/ W/ u
MOV ECX,DWORD PTR SS:[EBP+C]6 D) G* e; A/ p. d% w3 B. Z. a
MOV DWORD PTR DS:[ECX],EDX
+ z7 ~: l- i1 J. o2 o! J0 XMOV DWORD PTR DS:[ECX+4],EAX# n9 a; K: L# S' `" E E
ADD EAX,10
5 e/ D0 O9 u! O% a/ U+ AADD EDX,10- L9 a, H& V* z1 K' Z% F& ?
MOV DWORD PTR DS:[ECX+8],EDX
4 Z5 S) f$ J+ @$ C9 V7 gMOV DWORD PTR DS:[ECX+C],EAX+ @# x2 |$ b! C1 X. L; h, k
MOV ESP,EBP
+ s/ s0 x$ r$ |9 j: n, i" W+ |POP EBP% Q# Q7 j& l$ I& J- _
RETN 8
, q7 T2 V0 s% Y) P/ D; V/ {8 O3 o! W. W. [ I& W2 @
- e! X; E% p3 r) O6 i# [7 K2 d7 L
|