上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化 5 g- K4 F0 @2 }' h& q4 h3 V
* O, _! ^+ [/ ^5 B7 x
在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵 % \: R( K3 x2 X! l% _: p
这里是用bitblt贴上去的 7 C. ?5 L0 }% I: h& {
知道是贴图就要寻找相应的字库和字符串
M0 \$ ?" T F4 z. V字库在image文件夹就能找到 ; P' S% P5 L1 ?( W+ G) X
字符串被压在了EXE里(脱壳后exe增大了3MB) 1 v. N, R5 O& y
8 \% ^4 o' Q" g6 d下面就是分析游戏是如何显示字符串的 5 d* ~6 [" T* Z2 I
用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit* Q6 W% l& y& M& N: }+ G
/ F6 Y/ X2 D5 \! W; Z, T按F9启动游戏
. @/ D/ j: k+ W然后断在4F24D2处
4 k; o. Q" G7 q! o- V; ~. `4 ^这里是读取字符串用的0 H4 I0 \- @6 x- o& b. |
: y# t+ j# v3 G6 W3 t( y分析下0 K. Z7 N) ?* W' p( A6 M! w" _, x
004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit1 j1 z8 y) j! H+ @% c5 [% R
004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0
' C, ^( e+ Z! K$ n" @: {004F24D7 |. |03D0 |ADD EDX,EAX
9 z f! Y, q4 `& q* p/ c004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF
1 S/ x8 h( `# H: i004F24DC |. |33C2 |XOR EAX,EDX
- b* e! W& a5 u' U+ s004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节
" y7 }& B; k) r- W, P7 [$ ~004F24E1 |. |A9 00010181 |TEST EAX,81010100 ' ^4 {( S8 ?/ H
004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
! O3 `0 `9 q# v0 B8 `! e4 W7 ?" n2 n4 w, Z
这里要注意一下2 y6 y& _$ e0 o2 d
因为是4字节一取 \0处一定要是00 00 00 00
6 c. q- u, [( x/ G% U2 s/ y如果要把Quit8 x9 h4 O4 p! Z) `& v; t
51 75 69 74 00 00 00 00 Quit....Quit....
# \% |2 n- C5 O: X& x6 A# n翻译成"退出吧" 就需要在加4字节: Q& |9 D& M# ^/ U/ {5 ^0 C& O
地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]0 }- b2 b" j" d( ^6 w: ?1 A/ z( L
只要把ecx里的地址改成你的就OK了: J. K" g) X0 p$ m* d( J/ y
$ b- t5 _. L0 ]$ l/ G: l% b" N# W$ |
继续找处理图片的地方% X$ C, e- e- k3 z
一路跟下来到了
' Z; C/ k4 Q- v1 b" y1 R; y * D; g& i' h' U2 p) r5 W; r9 j
004F398C |. 83E6 F0 AND ESI,FFFFFFF0' O8 } w% j- Q& e- z2 ]$ v9 c
004F398F |> 56 PUSH ESI ; /HeapSize = 5( {& f2 w, j9 I
004F3990 |. 6A 00 PUSH 0 ; |Flags = 0
" t: R9 h2 U: |! H004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000" C' M9 W% n8 U) s7 d( {4 k* F& ~0 O
004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc
" }# F7 z0 @4 ?9 s/ x' R004F399E |> 5E POP ESI3 m4 @7 ^" _4 C
3 L0 \3 B' k4 z7 X/ o+ V
发现HeapAlloc 这个是用来分配内存的5 z6 g% } L8 g+ k
F8步过 取eax的地址 就是将写入新的quit的地方
& y7 f- e6 L0 W m2 D对其下硬件访问 F9运行7 f0 x# X4 R4 O' t* H$ }4 A
断在了403811
" K; z+ B% K9 x' N* Q2 I0 d在F9几次 断在了4091c49 E" R- _( N; K$ r
在F9几次 回到了4038117 t$ c4 @8 \, s) X* }9 Q
% ~# d! T4 h, M. J
确定就是这2处地方了 F' Y( {' _+ k8 a2 C1 a5 W. ^+ A
这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用2 `& z( F3 \! L: u/ n5 V
) Y% F$ w v' z5 X004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]
. O$ S! V! F* c4 o004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q
+ s; i7 \: X; ^) l' i004091C4 |. |85C0 |TEST EAX,EAX % Z3 s6 {7 h" L( }* M) Y5 x
004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出
6 `" [7 _: F; H( W- I004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
2 K) @% R7 n' ]4 q0 y+ m0 S004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q% u) b4 R# G3 }7 D+ {$ r7 ?8 f1 }
004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格
9 K. `% ]4 u% h004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8( R: a1 U7 O* W3 I# R
004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]
1 C( }; q# B5 d0 i004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX
6 B- i. O1 f, P* x) g8 y, S* s0 y004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 00000000; }& X% M N n! C* g. J
004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
- \* L" Q2 [1 l9 @! q7 H# V D5 p004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0
3 K2 w1 C: ^9 [+ P* p, s% s4 H004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]
# [. p5 b5 d' _* T9 M: c' L004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX
# T8 q0 D* W% a9 D' W004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
$ P- K K. z' e" U004091F0 |. |83C1 01 |ADD ECX,1
/ |# q# S: U: l- j004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX( J# J, Q9 e6 d) G
004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE' l1 K* `. e7 Q& A
004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里
8 |, W. s) y+ X. l* J O- E004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)# ]5 x3 t* i/ z; E# _; U
004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去7 |0 X/ B# M. y, p
00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
( n+ Y7 m2 |1 M# z2 S% d00409204 |. |51 |PUSH ECX ; /Arg2
% `5 _: K; q3 K+ `00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |
+ s* O+ T+ ^; Q* d3 i00409208 |. |52 |PUSH EDX ; |Arg1 压入Q6 F1 S C- H: e, h% H
00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
9 N7 c$ j' {( A- y# q/ ]/ @' I2 v0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键
4 R2 x8 E: Y& z9 J2 w00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]8 s) \3 w0 G9 V U3 q2 k- o
00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u
' V! q, `: I q. c00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX
% E, c! Z6 n) _% \0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
* p# Z& G( l' Y) {0 O+ \* m. E0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度4 i1 s8 l/ ~. y) a) u( Z, i
00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX. |3 u Y! i- V' H$ V4 O
00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],0
, o8 E0 [: e* S6 k' d e: I) a) j00409229 |. |74 7E |JE SHORT Unpacked.004092A91 p! q) ]5 A0 F1 A
2 R% t& t6 j6 o/ W! q
0 P- b5 p' C. k9 Z1 [# D& i: Q3 }& u& v
4 }6 u6 o! G, ^$ M$ [
. x5 N& z. c# B7 b7 A( ^6 W# ^
8 W; z# a* W T00404620 /$ 55 PUSH EBP
# D! u6 `* N" B4 [# D0 `00404621 |. 8BEC MOV EBP,ESP7 F. n' i. f$ o- w g' x# C6 t5 n
00404623 |. 83EC 0C SUB ESP,0C# C' N9 i; B8 K3 @5 ^
00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX 6 s/ g1 l1 C1 _% J
00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]. ?* g) p' _+ S- G
0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]2 j4 _* u6 q# s# q3 @( b7 T$ A( k% |
0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX+ x- ~5 g9 C* A4 Q
00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查; y. E8 u2 M! B5 y6 n$ R1 Q( |& R
00404636 |. 7D 0D JGE SHORT Unpacked.00404645
: Q% d' U) W9 s0 @00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]) f1 X) E/ @- S1 G) Z
0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL
3 P( e1 g$ n5 |3 L m, |, c- r, l0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]/ f c5 @& x) C
00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX+ P8 e/ Y( e @! C0 o8 i/ u- H( Z+ a
00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查
1 Q6 H( R. T0 l# R: p- V0040464C |. 7D 18 JGE SHORT Unpacked.004046669 d1 y J! K5 f, A
0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
1 ]: g7 [- Y7 o0 d! W% q00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]" p6 x9 B6 w0 ^6 u
00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]" x8 e6 \3 B& F
00404658 |. 6BC0 14 IMUL EAX,EAX,14! E1 f8 B: Y% } _
0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
+ i# m4 J8 z* R* I0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]
+ B+ r8 \0 y% G) Q8 \8 b( k00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX0 X* Z+ v- R; Z
00404664 |. EB 15 JMP SHORT Unpacked.0040467B
, O* S$ {1 x* b1 r1 K) P! g00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表8 G' x+ l' b: b9 @/ m+ q. r
8 T0 \, {; h' o6 m
8 |% X5 i4 @% T" F' {, q
; S" ~! l' n' M( j
0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..) E# W7 {! e1 Y) Q* @
0054AA58 1B 00 00 00 ...
/ c/ z& B" k. B# p8 |
$ s- p% I* g+ l- C/ O51就是字符Q
% Z- a" L, G/ |# S2D 02 00 00
. X4 s3 A( f6 w0 y- v8 b00 00 00 007 w) Y R. }. K4 s5 V1 N; M' ]
3C 02 00 00
2 q( _7 m) b% s) {& M. P, `1B 00 00 00
- ?+ }5 B/ ~' z1 K2 L5 E4 b
+ E, Q, v+ O. v x2 C A━━┓
- K9 e* L% @2 M ┃ Q ┃
& F# J: `! P$ J: w2 ^* h$ ^ ┗━━B
: r! Q! Y1 U p' V$ D5 G- e- L J- V2 [& ]5 K, d" b
A的坐标 (22D,0)
+ p1 O2 ~" `! \, B. T' d j7 YB的坐标 (23C,1B)" m* n5 X) O# q: ]% W- C7 W0 Q' W1 m
8 X' f5 y/ w$ A+ n+ }
. X: b: V- v6 @7 P% l- x7 _
: L1 }' \- q# b% L4 R& Y( o8 H, q
00404669 |. 81E2 FF7F0000 AND EDX,7FFF: H4 x: v* r! ]: J \9 p) |
0040466F |. 6BD2 14 IMUL EDX,EDX,14* u9 P: o5 b1 R- |
00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
5 J- M. Z2 R' J. k# W, @) B& @5 y00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]9 [5 j, t$ F0 V1 w
00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX; V# L* r, F7 X# y: w% b! W
0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
, S! Y& X& t# C* g, F0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
9 C* j1 [. |% T: X- p4 C- X00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]; E0 P9 b( R# \0 c8 f
00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D
' d: F% h9 T# N# C ]' D" N00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
3 U) i6 s* Q2 B" v1 k00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]( h9 J4 Q. D$ Z1 u/ A/ E, S5 E
0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]% Y' g9 b- I) M( |' }
0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C
$ r* X: a) A2 ^* B( G: y00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
( T# d- X0 r( W( l! u# ~+ `( N00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]7 A9 H, z$ o) o- v# x7 @ {% |
00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]
% H" H+ L* Z' q8 j' h' R0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0 I, G: {4 e0 q# z; ~! ^
0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]1 c# o9 e) e5 S. v
004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4] |3 w1 M" w0 \
004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]4 G# m& S* |3 O9 a0 u% C, \
004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B Q( Z! G5 W7 U! t1 W8 K" [
004046AA |. 8BE5 MOV ESP,EBP7 R: n$ H# v' m% E5 s
004046AC |. 5D POP EBP
- @4 l- J" q& ]004046AD \. C2 0800 RETN 87 i E0 f* u: |( y5 k
! Z7 P+ o% j( Z8 v6 I
$ ?5 K5 ]* }! y" {# n" K* O! }, h5 D
7 e' |8 Q9 K3 h5 c
1 a3 F& p7 a D* K1 C! g$ p& u
: l3 ]+ F, {2 L( ]
知道这些后 就可以改造了
; _$ y6 w6 I, ]/ e先改字库
7 K; ?( {3 x# R' M5 {5 z' Z: I% W+ l
+ U0 S; j( l: K( x; D( X' W2 u a. g( x% l, Q2 b
然后改成支持汉字的
8 {1 D2 Z8 R5 ?" G$ q+ ]把4091d2 的 CMP EDX,0A 改成jmp
# n% A6 W I8 ^/ M, L! O4 S/ o跳到我的代码上去& E7 x9 o F$ r, h+ n' _+ O
. [: D* g: `3 c+ b9 K* ?AND EDX,0FF 去除高位的FF `8 Q; E' Y3 P; I+ z( I. h0 a; `
CMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的- Y6 _/ |- S' |4 j) j
JL Unpacked.004091F8 小于 B0的就跳回去: i h. J2 v& U8 H) O. S
MOV EDX,DWORD PTR SS:[EBP+8] Z1 v# @+ c4 d" i1 y. H
MOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
) {8 c& s9 E0 c* s& B% I, N% \AND EAX,0FFFF 去除汉字高位的FF
" t4 B9 F' a! }# @7 @MOV DWORD PTR SS:[EBP-40],EAX 存入形参
' _/ o' z& A# }$ H) [/ m/ m& aLEA ECX,DWORD PTR SS:[EBP-28]
* r) H* V3 ^: b0 j+ |0 APUSH ECX ; /Arg2+ G( [% u9 I$ T8 C3 d! B
MOV EDX,DWORD PTR SS:[EBP-40] ; |8 H8 Q2 F; h/ U* ?1 K9 R' m
PUSH EDX ; |Arg1
0 t4 g: U5 e* s1 e4 a. F+ U2 s8 z# rMOV ECX,DWORD PTR SS:[EBP-8] ; |
6 r+ @3 L; c( kCALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标
6 K/ X- E' B( N1 oMOV EAX,DWORD PTR SS:[EBP+8]
" u7 [$ @- K( rADD EAX,2 call出来后加2个字节/ A, p& {# G& u2 `6 }
JMP Unpacked.00409217+ z. ^! T, A/ |: r/ h2 X
$ w. {; y2 N6 e! ~1 b' ?
; {- T- y% i! p8 \0 p" D" d" Z9 p* R8 x( \8 {- R' B, r# h+ k
2 M0 q) S- z" J5 f3 |# b) [3 P6 p$ VPUSH EBP
: l( I7 `* e! p( g2 t; tMOV EBP,ESP
8 ?$ v2 v r7 R uSUB ESP,0C; P& s! f3 I1 t5 J) K. C/ T: a
MOV DWORD PTR SS:[EBP-C],ECX7 Q2 a; _. b; {) O# X
MOV EAX,DWORD PTR SS:[EBP-C]
) o4 z% E) a# a1 Z$ m1 _MOV ECX,DWORD PTR DS:[EAX+14]
7 C- z- F' B( \: i) sMOV DWORD PTR SS:[EBP-4],ECX
" r% e3 P8 m3 k7 Z: m8 HCMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)8 ^/ `) P4 A& U& A, h7 S
JG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片! N/ ~4 K& @) E
MOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)
2 ^ W2 u4 G2 F, Q a6 EXOR EAX,EAX
/ R, b5 O5 p9 O% r' ]& pMOV AL,CL 把汉字第一个字节 放入eax6 I8 V" S Z2 l! X9 ~
SHR CX,8 右移8位取第二个字节
# F- M1 r- O* }, {/ u2 u, {XOR EDX,EDX
5 b' r: e9 p. A: r. @MOV DL,CL 把第2个字节放到edx去
6 z& [+ \& s6 ?+ Q0 JSUB AL,0B0 这里是我自己写的算法 不用再读取码表了0 A; M( q8 f* r$ M2 o- E; x
SUB DL,0A1
" L, F4 h# R, q( q) KIMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16
2 i5 \# @ X1 D+ `" XIMUL EDX,EDX,10+ F/ K& z8 z( p$ g
ADD EAX,20 加上我图片原来的高度: l0 Y: O9 z3 s6 [1 L
MOV ECX,DWORD PTR SS:[EBP+C], J0 b4 I. r* j; [! Z% C; j
MOV DWORD PTR DS:[ECX],EDX
6 {: i* o- p( x Q* ]) {5 `$ w' _MOV DWORD PTR DS:[ECX+4],EAX- ]4 L" Y6 D6 Y9 h
ADD EAX,10$ m$ j- D9 e2 ?
ADD EDX,10
, T. G; P( j& ]5 \' N# NMOV DWORD PTR DS:[ECX+8],EDX
8 R: g0 I; u% [; f3 H4 p1 WMOV DWORD PTR DS:[ECX+C],EAX- z6 C' x g' {' b
MOV ESP,EBP
3 `. w) _) L$ BPOP EBP" a! X/ |$ ~$ v' Q* {! a g
RETN 8
! {' X- W$ ^" ?2 L; N
1 s0 h( R* {5 V
7 i& l" P% a( q/ [* N7 S& X. H |