上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化
+ T g0 ?& k- T* e- [+ D, o
3 }( t- W3 Y0 z' {, h F; `在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵
& s- M- r6 c, I2 v v5 z这里是用bitblt贴上去的 2 i0 Z$ Q" `! v' m8 m/ A. N! X) X
知道是贴图就要寻找相应的字库和字符串 * j0 l# }2 M$ O# A! J
字库在image文件夹就能找到
T. O3 Q2 x B) Q0 S字符串被压在了EXE里(脱壳后exe增大了3MB)
" t/ Q" a, ^" H% ?: s% O4 S+ b7 d' W1 F7 I
下面就是分析游戏是如何显示字符串的 - y9 ^* O" ^6 ?3 @1 m+ i
用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit' Y: B+ X% ^. \1 `5 {: F0 @5 u% n' C
5 M7 \ E# u% L3 c7 i; O3 T. C9 ]8 _. d
按F9启动游戏) P" k5 \2 L. f: p# o) }$ \6 C
然后断在4F24D2处. f/ h. M4 L! V3 z% R8 [
这里是读取字符串用的8 h6 A+ w' u ]1 P0 m9 r; g
: c6 p a, a3 p# l) z分析下! [ d/ z, X7 d) j) ?5 a
004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit
4 K& J- h1 a# q+ ^004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0
' d9 j5 h$ W7 g+ _004F24D7 |. |03D0 |ADD EDX,EAX
) s7 c: q+ X: S004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF
2 H! v. M+ v: ^" e; g004F24DC |. |33C2 |XOR EAX,EDX
" C( |! j& f& e' c3 y004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节$ m8 Z" V3 s( @% x3 ^0 F& ~; `
004F24E1 |. |A9 00010181 |TEST EAX,81010100 & i- m+ W/ m2 e0 w1 f$ I# ]- U
004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
) B! n: b3 |. Y. }' }% d! z2 t
% a4 g9 \2 Z- n: e: A! z这里要注意一下
: T0 p- O1 Q0 b( d因为是4字节一取 \0处一定要是00 00 00 00
/ R, _% b- x" j' \7 E如果要把Quit! K" r8 ^' ^7 J3 }7 l
51 75 69 74 00 00 00 00 Quit....Quit....
& T: a0 U! Q) @( d" O; f: F翻译成"退出吧" 就需要在加4字节
; Z) U }2 s' ]8 V) I! `! b地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]; i7 a7 u( p4 D; d: z
只要把ecx里的地址改成你的就OK了
" s0 Z. ?6 x# w8 Y
+ u" N U W0 x) |. N9 F3 f继续找处理图片的地方
. f( T+ G# x7 c7 m# j, {一路跟下来到了
- U6 ~8 d, L% ] a6 L
) ^2 f: d0 Y/ W0 v" S004F398C |. 83E6 F0 AND ESI,FFFFFFF03 O" J" B# z) Q- w8 f: k
004F398F |> 56 PUSH ESI ; /HeapSize = 5
k6 Z$ s- }/ Y004F3990 |. 6A 00 PUSH 0 ; |Flags = 05 `. d1 R7 ]! V
004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D600007 C9 h3 _ B! I3 O: \; {* [. C
004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc: n8 x8 Y" k% ^
004F399E |> 5E POP ESI
8 v' d( S4 F! Y" d+ l+ @9 A3 L& T; ?+ q C
发现HeapAlloc 这个是用来分配内存的6 j; w! I& F2 ]
F8步过 取eax的地址 就是将写入新的quit的地方8 m8 ]( ^$ S& D j
对其下硬件访问 F9运行" b. K2 H% p+ z# E- O1 L
断在了4038116 q: ?- t1 c5 t; m# Q
在F9几次 断在了4091c4- C) u9 j3 W8 |; \
在F9几次 回到了403811
% b* n) ?$ K, i& m- ^/ O
" n$ E( Y ]1 e0 N' ?' C' f确定就是这2处地方了5 z( j- q. m% d; y! Y Z
这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用4 ~/ A6 ?; D; |3 ~7 `
) Q; ~1 o" [: p$ G0 C4 s004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]7 Z5 S! H$ q9 K& S% h7 h# c
004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q& j6 g- J% j0 r
004091C4 |. |85C0 |TEST EAX,EAX
! S( F/ A W6 d$ E/ J004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出) s. K9 v# W/ l) |9 A
004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
6 {# `; x5 S% \( O0 h- p/ j$ I004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q: |" z- h& N7 T9 z0 P: d+ P
004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格! D0 Y4 q5 v- z# g# U1 ^
004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F82 d; D6 i( J8 t( b) u* Q! D, o) F" n
004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]# j8 E! N+ M8 t N/ c
004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX
: I# m7 L: _! S004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 00000000
4 e& t: l% V# P* c8 y; @+ e004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
6 H6 U* D. L3 b$ @/ e( [) L004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0
2 R# n& a& G9 S( y$ c004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]& A0 C) Q5 _$ c: W, K
004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX
8 b: P5 Y! Q$ Z6 M) z004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]+ Q' A# y. r& G! V1 ^
004091F0 |. |83C1 01 |ADD ECX,11 S) Z4 {0 i2 Q) ]
004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX! `& q! W2 v+ y
004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE- T) f$ _+ A! ~) S. _
004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里
2 j1 M* I$ _, T' z8 B4 V4 m004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造): _ m- v3 D! W1 |) f+ }) @3 a
004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去
. |3 W; ?: @ h$ ?7 j: u3 h. z00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
/ _( |, a; p' \; _; z& q00409204 |. |51 |PUSH ECX ; /Arg2
$ c; W4 ~1 O$ [7 h9 r00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |0 d* z; ]* U) X* d
00409208 |. |52 |PUSH EDX ; |Arg1 压入Q
& c. L# |$ L. v00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
$ S5 R" g4 n" m: [# g$ d0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键2 d0 c3 P! |% h0 z! b
00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]5 }6 A% I5 d# W
00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u+ e3 h- j) W2 W) N
00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX1 u4 d$ L% [$ Y8 o* W$ q# n. k
0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]0 z; M8 H! r. G( Z, m
0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度6 v# z" p% r- e- W+ @, f0 X
00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX6 E) c4 g# j: X: T
00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],01 |) }8 {- \' S6 Y2 `2 Z% w9 z
00409229 |. |74 7E |JE SHORT Unpacked.004092A9" ~1 g4 S3 H* c" y- u
/ N) t2 e* Z" }) Q+ L
. Y* E- }- Q, A2 _6 i B: e% w, ?* q$ h" z0 d3 Y# D& x
) y0 i2 M2 n0 f- H0 K3 i; P1 I
+ P6 k/ _' I& H) M8 F, y5 U/ L7 |+ y- {' d9 Y
00404620 /$ 55 PUSH EBP' ~3 L. @3 z/ Y# Y; Z0 V) o* E
00404621 |. 8BEC MOV EBP,ESP
* s* B1 n, l1 l3 i- i8 z2 @) @8 d00404623 |. 83EC 0C SUB ESP,0C
+ p/ W+ I+ {2 g8 O+ v00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX # h+ l$ G" p( w
00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
1 z" u& C% ^% c/ c( Z) M% A0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]
1 W% {" A% X) b, o! r0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX3 Z+ ]9 H/ _) A/ ~4 E
00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查" k; ~# x& l; {# O D! z8 |3 Z
00404636 |. 7D 0D JGE SHORT Unpacked.00404645
+ M5 T: }7 ?" u# H9 F u00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]( \8 h% q- O0 c4 x
0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL
; J. a, a. H+ p q! ^. ]0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]
' `2 O P- R# o: K00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX0 {5 \4 X( ^& F' W0 g
00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查
2 A* V4 E0 n' g2 U/ L1 c9 L. i* ]0040464C |. 7D 18 JGE SHORT Unpacked.00404666* G1 X6 u# w+ b5 L* `% S! @
0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
; t- O; L8 y# X( j( G00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
/ }7 P2 c) I3 b; _$ W* C' c00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]* O% H$ ~1 Z- C/ ^1 H% ?: [, D
00404658 |. 6BC0 14 IMUL EAX,EAX,14
3 a' s5 f" ]% x1 x3 `0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
/ F5 @! m6 ?$ J* |0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]
0 x! W6 ^! X3 L/ I1 a7 c00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
$ x2 D5 ]( t v( x00404664 |. EB 15 JMP SHORT Unpacked.0040467B
- b6 w p6 C& Z& \" n( L" O- Y00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表
+ j& r% I# z( P: F# `5 K# {
& b( p" T4 B( t* {: g
$ L& S# l: h; O; L) ]7 j
9 y" E+ c& R7 X1 C1 X* j& h0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..; e ?$ [, Y J& A3 \
0054AA58 1B 00 00 00 ...) Q* e/ i% W! ^0 @9 B) o3 K
0 L- `9 A4 ?6 y! c51就是字符Q" R# C6 V. D. v* M. U; @/ ?: H+ T: f$ A
2D 02 00 00/ X, G3 n2 P" r& t1 c t2 p
00 00 00 00$ s2 j$ V- y e {1 R
3C 02 00 00
7 N; Y% `) j0 W$ Q1B 00 00 00
# J/ h2 D$ g6 a+ m! U8 j) q* w0 r$ m$ L1 p w$ t
A━━┓ ! F8 Q2 c1 Y' K; l+ Q, y
┃ Q ┃ % B% J7 P2 n* l6 S1 b
┗━━B, [9 L% Q+ U# h* r" w
/ ^% X0 w9 f4 G8 D* }- o5 JA的坐标 (22D,0)* N: g3 R& U D; H$ B1 v
B的坐标 (23C,1B)# s7 ^6 o$ v, b/ d- K& T
3 |9 d" o7 e6 i n
0 a% G7 z7 B4 B7 [
' o) \( i8 [/ Z- b! @00404669 |. 81E2 FF7F0000 AND EDX,7FFF+ g Q0 X6 B4 c7 W- i
0040466F |. 6BD2 14 IMUL EDX,EDX,14
7 H2 g! V) n0 `# d00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]4 r" x% i/ S" M6 F. e( U# g: a
00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]2 ~* k: X3 l3 E: X+ q" D7 X
00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX
! I1 j, n$ `' Q; s5 i6 m$ Y$ c# x* K0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
5 \" |1 Q% B" k0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]& F8 B9 ^8 C- {# E. z/ D6 g
00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]
0 P* g$ ]5 y+ G+ t/ X& w1 f/ }9 R00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D8 O5 y" A& ?$ i; ?
00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]9 z6 S5 z/ q1 g
00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
/ L s- f' \( e. i m0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]
* Y+ l$ Q9 w( x9 H0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C
$ a' I7 l8 [8 j4 v' h3 B" `% G6 A00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]1 P3 _! c0 Z1 R+ ?/ i( N
00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
, X3 v0 b' G; ?7 y/ ~00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]& u, v* [) u/ O% Z O( ^0 v
0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0, L# l( `; F* D
0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
: p" ?" [+ P4 x+ C# a8 k+ x' C004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4], h7 k, j: a# E6 C! [
004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]
8 z8 ]+ R- \$ a2 i+ e# k/ [004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B
, P: t! v# p" R7 s004046AA |. 8BE5 MOV ESP,EBP! d ?0 |& L9 ^* r
004046AC |. 5D POP EBP
- a& L: X1 c* o004046AD \. C2 0800 RETN 8
% R K0 M! m1 h8 c# u1 l6 h& V$ i
! \, j2 x6 s2 ^; H( J
6 x- b$ J. m1 Q0 w( e+ j3 L$ M- S% }7 H4 g3 H/ t1 O" `: b, Z
6 I/ i3 p( }) |
知道这些后 就可以改造了% d3 a* }8 i3 R1 G$ [. s" r: {
先改字库! Y5 h8 e, m* m% M2 v+ y7 Z
. X7 T% B) p* D5 V r! l0 ^1 I& R# m
然后改成支持汉字的
% k [; l2 o) a3 i1 H把4091d2 的 CMP EDX,0A 改成jmp
; \) _2 }. |8 T& ~) o! y跳到我的代码上去
& t: H& q9 V! [% P5 @& g: z' L5 U7 e4 G4 q/ J# P% C( z
AND EDX,0FF 去除高位的FF
9 u. D' N" D5 ?8 U: VCMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的
- Y9 x7 ^/ a( r4 A$ vJL Unpacked.004091F8 小于 B0的就跳回去
5 b# U7 W/ Y7 Z3 t/ A- s5 ?9 nMOV EDX,DWORD PTR SS:[EBP+8]
+ [$ t. o2 M& X3 p1 A- ? Q! Z; qMOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
$ F9 K8 z, k: J$ r4 mAND EAX,0FFFF 去除汉字高位的FF& H) {$ C/ K' t5 }! O
MOV DWORD PTR SS:[EBP-40],EAX 存入形参' Q: m; `6 R. R- A' {
LEA ECX,DWORD PTR SS:[EBP-28]
/ u6 Y, @" r4 T' NPUSH ECX ; /Arg2" n; o- ?# ]; e0 Q6 r, Z$ j
MOV EDX,DWORD PTR SS:[EBP-40] ; |, `+ l" i# B) G% X: C
PUSH EDX ; |Arg11 _. q6 x( P: n
MOV ECX,DWORD PTR SS:[EBP-8] ; |2 ^# m; U; x ^+ p7 f
CALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标4 T' D$ g6 T4 T H" D
MOV EAX,DWORD PTR SS:[EBP+8]
7 z% ^* @! T2 L- f- mADD EAX,2 call出来后加2个字节 m+ Z* D) `3 N! }1 B; g& l' Q g
JMP Unpacked.00409217
! h w* ^0 b3 L3 c
3 ]0 I( [* [' d2 A$ M1 @- L
6 Y: U" D6 \. W# n: r5 ` E
( a. p2 {4 x3 R6 C/ Q2 U
8 Q' a1 a& M- r1 ^PUSH EBP7 a& F7 a5 x" m% _! Z7 F
MOV EBP,ESP1 p* }8 y! B @% u/ t w, d3 Y
SUB ESP,0C5 A. y) ]8 n9 B+ H, U
MOV DWORD PTR SS:[EBP-C],ECX# l( c; {& U4 ]- n& [
MOV EAX,DWORD PTR SS:[EBP-C]/ F2 \( b( W, l% j- @9 n* z- Q9 r A
MOV ECX,DWORD PTR DS:[EAX+14]
5 |8 b: x! y0 A+ WMOV DWORD PTR SS:[EBP-4],ECX
* Q/ g! ]6 U( k$ g% h2 Q. |2 m3 fCMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)7 |: t( Q, O* M$ T3 @- S( l
JG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片2 y3 X0 e( C& ^+ }
MOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)
2 E& v3 A8 q. P2 k" m: UXOR EAX,EAX4 t9 U/ C* B% M
MOV AL,CL 把汉字第一个字节 放入eax1 K. N" U5 c; i. z$ L |, s/ u
SHR CX,8 右移8位取第二个字节
9 C- z7 M/ b9 _8 o8 RXOR EDX,EDX
; | Q y) \( y \* L2 r: Q2 YMOV DL,CL 把第2个字节放到edx去
' t8 e5 h5 J8 B: G0 m! H bSUB AL,0B0 这里是我自己写的算法 不用再读取码表了
# u' o: L0 r5 X: S/ g8 G# I0 e- y) l, kSUB DL,0A1
3 }4 p+ U1 d2 A2 N5 y) ZIMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16
6 Z+ b1 @3 g: W7 U5 g$ ]' k4 {IMUL EDX,EDX,10, V5 h- v+ X: j+ e \1 z
ADD EAX,20 加上我图片原来的高度4 k0 u- l6 r2 m. S
MOV ECX,DWORD PTR SS:[EBP+C]
; e v$ Q% E2 U* S; t: t3 NMOV DWORD PTR DS:[ECX],EDX A( ~" k! M9 d* d: i
MOV DWORD PTR DS:[ECX+4],EAX
) M( `, M$ H6 C3 _( C% `2 WADD EAX,106 h+ R0 J2 E. s, ]; c8 k
ADD EDX,10; Q7 a u- O/ q |# G
MOV DWORD PTR DS:[ECX+8],EDX
, K4 U/ W& A4 q# fMOV DWORD PTR DS:[ECX+C],EAX. Z0 y. l( i. z' y
MOV ESP,EBP" ]/ S9 w' Q! b3 k$ i1 S0 Z$ P
POP EBP
) [- [* }/ E/ X, f9 d8 w6 @2 v$ U5 jRETN 8
! K$ C: r1 D" P8 z& D6 J; ?* E. S: L
8 o9 `3 e2 d4 X |