上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化 ( S( M6 @7 q. Q5 @( d
: R; P ?4 b' q& m. ~! c! O3 k5 k) Y在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵
* g, @+ P* \: G3 p7 `这里是用bitblt贴上去的 , Z- b7 }: n- }. E5 s% o! r
知道是贴图就要寻找相应的字库和字符串 9 x4 T. t+ M7 {7 L
字库在image文件夹就能找到 1 R- L" h. b- j1 o' v
字符串被压在了EXE里(脱壳后exe增大了3MB) 3 w, q0 [$ U! z2 F
2 f3 `5 O. t# n
下面就是分析游戏是如何显示字符串的
% s. [1 \0 E- q1 \. r! n; d$ J" i* r用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit2 v% m/ c) C2 v7 H
$ Z I! n2 S2 y! \. H! [/ D) r按F9启动游戏9 K0 P8 j: e, I- ]4 v0 [
然后断在4F24D2处
# q; N& @& }: m- g这里是读取字符串用的
0 n3 f) O0 r2 }, u9 n$ R, ]- B/ P 9 X% ]$ y" O; v+ O9 V, R
分析下
: T* w( P% `) a004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit
6 l: n9 U( {3 c004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0: O. Y4 B* `7 [& Q/ ~# G7 l
004F24D7 |. |03D0 |ADD EDX,EAX3 }# m `0 `8 V1 s
004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF+ b+ D0 O( s9 _) @! Q' J& s
004F24DC |. |33C2 |XOR EAX,EDX# P @5 x8 x* ]0 L8 D
004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节4 T+ O6 q% U0 M, t' _7 p5 G
004F24E1 |. |A9 00010181 |TEST EAX,81010100 - `1 }4 }; C* h. j1 C
004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
7 U: c" D" f% {# P4 F7 G) ~3 b8 f# w6 i. d( v7 s4 \+ v# v
这里要注意一下
3 P6 f; u/ R; p9 B4 z5 q: d- A因为是4字节一取 \0处一定要是00 00 00 00" w8 J3 u/ O, G* l9 q" V) K
如果要把Quit
3 K0 x: y- v% u2 n. R% X51 75 69 74 00 00 00 00 Quit....Quit....
: T& O% M. V( N$ A翻译成"退出吧" 就需要在加4字节) e7 m5 T2 m- R7 u: C/ m+ R$ s0 ~
地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]! C( `' \" l) b2 e' M
只要把ecx里的地址改成你的就OK了
# _) t8 R8 ?# f/ ]) I: W/ G! k) f, J. Z& `) m2 i/ \
继续找处理图片的地方
: x% B, S2 E9 S }3 S8 {一路跟下来到了
2 D6 p; l' T# |3 B
* w4 l6 [1 F; R/ l3 c" @004F398C |. 83E6 F0 AND ESI,FFFFFFF0; E1 t5 _! |* ^5 n2 I
004F398F |> 56 PUSH ESI ; /HeapSize = 5! Z$ l5 u5 G, ^3 w9 V
004F3990 |. 6A 00 PUSH 0 ; |Flags = 0
$ }5 G7 f$ J; Q: J004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000
2 C" B% {& ?- i( _, P0 f. X) ^004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc
) ]$ f8 \; t$ R9 p, F004F399E |> 5E POP ESI
6 |0 b5 g6 @- T
% i# i; U; _ e7 l; [发现HeapAlloc 这个是用来分配内存的1 j6 H- a9 V( |" m! Z% l ]
F8步过 取eax的地址 就是将写入新的quit的地方- i# `% m* ^, w6 v2 ~+ ~
对其下硬件访问 F9运行& H5 J; W2 D6 h9 U& c
断在了403811
3 C9 f7 k- V8 k7 p8 \6 I4 T! V在F9几次 断在了4091c4
+ L& ~5 v1 a t9 A$ a; Z# ?( R在F9几次 回到了403811
- Y) ~ j0 h" n7 `' Z
4 B% p; O& j2 ^& ~2 V1 z* \" y" E0 B0 l确定就是这2处地方了
' ^/ U0 e. c' D% E) l这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用* L6 r8 a3 k2 X/ q- m
, S$ v7 ?4 Z5 b, } \ J6 Z004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]: K# Q: b- c: u2 o7 x3 l" c# q
004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q( ~( ]( ^7 I. y5 p* x1 c# V
004091C4 |. |85C0 |TEST EAX,EAX
( ?! t1 d" p, g7 T7 [% H/ \004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出. u# G; e4 D" l( P/ w
004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]2 B: E# [, {0 c4 b# d9 z% g
004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q
! Q$ S y6 [& u/ v004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格+ m! x' I0 B+ W7 K) {' G4 v9 b. D' I
004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8
- W) f8 s" G$ R0 S- `004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]. V% N4 ?& g7 Q. \
004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX4 k% z: V$ d {4 F
004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 00000000
& f7 v4 b9 d! e* c, K' V+ ?004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
9 l8 J1 Y5 J* j$ y' v004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0
$ ]1 n+ R' [! k; @004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]
# S8 t" L5 h0 a3 V004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX
, M3 l* {% s" j$ I1 D1 r004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]3 j& A- X: [ U2 K% C, _3 U& M r1 E8 y0 G
004091F0 |. |83C1 01 |ADD ECX,11 g" D; J: N% \3 u2 d3 Z7 ^
004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX) u3 m, k& x, I$ G3 x0 q* R. f( y
004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE
+ u: d0 b. u" P* n004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里 w' n" M2 A! b$ U
004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)
7 {, ^. T; i: @! O; [" S( p# Z4 w004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去2 I1 j }: a2 J& G$ T; u8 w
00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
, M( b+ j6 H; l3 U, f00409204 |. |51 |PUSH ECX ; /Arg2
1 a$ _9 Y/ w* X2 \" A6 Y00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |
+ s5 @& {4 S3 o: m# ~( z00409208 |. |52 |PUSH EDX ; |Arg1 压入Q
2 O" k5 V( v5 z- v00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |! K! g. k6 G5 v. S
0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键
. y0 ~1 b/ B, G" b00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]
- R2 p: ^, N L4 C' R) W4 F8 w00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u/ H7 k. d5 Z" e
00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX% [/ n, l' \2 z0 b/ g4 W
0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
- k: L, Y4 O4 a0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度
; |* {+ d. m: J0 }! W00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
: A* _* z) \8 f1 a6 Q" {' x7 |00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],0
+ {1 g2 X- S# [( O: }. W00409229 |. |74 7E |JE SHORT Unpacked.004092A95 k: }# R1 A( {! J) f
" f% l! V0 \' Z8 q- W: f. G" j3 o! W( N6 Q3 |
/ R( N8 x0 D2 ^ }# j. ~, e( _* O; b) m& Q' E0 Z6 S* \
+ `0 z7 o6 w* l: h% _ {
4 t8 ?# l5 F& L. [! x
00404620 /$ 55 PUSH EBP
+ _( s- \* V7 q+ O7 f- K. L00404621 |. 8BEC MOV EBP,ESP7 p4 j$ p( }1 H/ K) O
00404623 |. 83EC 0C SUB ESP,0C/ R+ M$ S: k+ {$ ?: R. ?
00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX
|) x( r9 u- o% f7 g: c" Y00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
; c" N2 V. D8 p$ T; u5 A0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]- F2 X) W _' _$ a
0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX, b8 K! ~% M. z# }& U9 p" L; o8 W
00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查0 b; l! B: G! F# l, }5 W
00404636 |. 7D 0D JGE SHORT Unpacked.00404645
1 h" w' e- w, R4 j00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]
0 }5 B8 d! B/ a! ?5 p% A4 x0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL
2 m" G& k, `& U0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]$ p: ^& X1 P, a( }8 Z
00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX6 I. _6 P$ g' H; c7 K/ S+ x( [
00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查# _7 a8 a$ [! ~8 x; m
0040464C |. 7D 18 JGE SHORT Unpacked.004046664 i$ s4 _$ S( M6 r
0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]4 F) p2 Z( A" t: X& Q
00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]& q7 {4 ]. r. k1 Q) ?
00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]
( G/ I, ?2 r$ `6 I/ v7 u& h# K00404658 |. 6BC0 14 IMUL EAX,EAX,140 \7 S) j0 a9 Z7 q5 F
0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
5 a) e) G# L0 z4 n1 T( D0 y( c) h0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]7 e" S6 s- u+ |
00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX9 z4 a" M1 G6 w3 g
00404664 |. EB 15 JMP SHORT Unpacked.0040467B
. L% O0 d( C& D! E" ]* Y9 ~00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表
8 ?. `; n3 x: {8 z% d& N
, Q R8 u# @* F+ c0 l" |+ C
/ c3 f. x- Q: ?; {) M" T. r. W6 }( x1 L
0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..1 z$ R$ q) X3 H- u! e
0054AA58 1B 00 00 00 ...
, h# z) g2 N3 e/ D: k! s, l T8 n- d; G2 q5 Z
51就是字符Q+ T! m3 n1 n4 g4 z. d0 \
2D 02 00 00- ?5 L m6 W2 w7 ~& \& S2 d `
00 00 00 00
, K' m1 u* Y; B' a L4 f3C 02 00 004 \: |- X3 B1 O( o5 O' C
1B 00 00 00
( C& u. Z, z# F! X
- I' i# [$ G; a$ W4 A$ x6 K5 [: i A━━┓ ! Y9 L- _) X4 ]" {
┃ Q ┃
2 [- G5 W9 ^. ? ┗━━B5 ~8 |$ {, {6 h) u8 X
: s5 W- R: u2 U: d c6 g/ t( ]; _
A的坐标 (22D,0)
3 w% R, c( v! w$ WB的坐标 (23C,1B)- f( l% i" Y, Q
. ]/ p: g0 ]8 r4 C5 c5 ^9 s' H
4 y/ E; ~* a5 A4 C* J1 Y; W
~; Y( f+ H2 D% t
00404669 |. 81E2 FF7F0000 AND EDX,7FFF/ n- q: l! t+ n/ E
0040466F |. 6BD2 14 IMUL EDX,EDX,14
, c" @' B- N& F* t( w7 j1 f' ` Z5 L00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]- C) v/ t* `+ b8 k: j- i2 {
00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]
) C( ]1 I+ G, K; i1 X00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX
* K- ~7 ]$ @ F/ H0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
0 n- k( I$ \! r+ ~# [) j0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
( s, D) M" W) C( L! S00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]
/ ~+ k3 m& Y0 N$ G* ~, B& T00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D
5 `* \9 m3 ?" ^. T00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
& f6 X) R" e: e* K6 T, n P& D* c" `00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]4 I- u; T% K/ R0 @* k9 U
0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]
7 M7 n9 d3 y; e& l% }, t0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C" |* n/ W3 e) t2 D
00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]) K# j/ }4 X' u, ~! b( _4 p: {4 {
00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]+ U# i) L) R( c O, ^
00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]% r' x6 ^2 s8 g B3 z; ?
0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0
) @% J0 m$ J% s- ^6 L9 {' Z2 P0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
6 S6 G7 w6 k- U$ b; h. k6 o004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]/ I3 a3 Z1 E. A V
004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]1 S" M1 l# M+ L7 S: l- w& k
004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B+ v$ |* E" m/ {. |
004046AA |. 8BE5 MOV ESP,EBP
+ V: P. W0 h9 m% z v5 I004046AC |. 5D POP EBP, P. o# i6 [1 h, H6 j. O% S6 R
004046AD \. C2 0800 RETN 8
$ K; e4 }. G* V' X7 R4 T Y3 t, p4 m
2 l0 E% h9 _) ~! U& G& x8 \ [) g6 ?4 x1 [
9 o# J* U5 ] x% S) I5 m
! j* x: o1 K! g- o
1 j1 c9 h- C5 }6 v1 _; s知道这些后 就可以改造了3 U' J0 R3 w/ y3 R. x' H8 e; x
先改字库( n: p. A+ }$ c; Q2 \8 Y5 m
1 u. n3 f5 M; q
. L* w' L8 Q$ h" U! `3 f然后改成支持汉字的
5 `3 E) F( D( z8 u把4091d2 的 CMP EDX,0A 改成jmp- E" Z z3 X" P; m! t, U7 \8 l3 {
跳到我的代码上去8 G. N4 P& V l
+ ?3 [2 r8 X9 }
AND EDX,0FF 去除高位的FF$ X$ I" y0 y' a. C( L- b- ^
CMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的0 h# Y5 T7 E, X' l, W6 ]; q. d
JL Unpacked.004091F8 小于 B0的就跳回去# [* B8 y6 D; R2 K
MOV EDX,DWORD PTR SS:[EBP+8]4 k0 h9 H/ I$ a5 {2 w: I/ k4 v
MOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
% D/ \1 M% X4 T6 m' ?+ pAND EAX,0FFFF 去除汉字高位的FF' _6 [% B+ f9 k2 X
MOV DWORD PTR SS:[EBP-40],EAX 存入形参
/ k' d' C% H8 SLEA ECX,DWORD PTR SS:[EBP-28]/ C; j7 c9 V) m# O4 F* C
PUSH ECX ; /Arg2# z+ D6 Z+ J% d" a# b" s: F- @( ^
MOV EDX,DWORD PTR SS:[EBP-40] ; |: Q" @! W' v, O
PUSH EDX ; |Arg1
. y) y' J- K0 ?1 H) m3 lMOV ECX,DWORD PTR SS:[EBP-8] ; |
, w: |+ V# R, W r' X5 O3 sCALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标5 l; ?8 e/ V4 x2 x* o
MOV EAX,DWORD PTR SS:[EBP+8], `* [$ S3 E. W- U2 H
ADD EAX,2 call出来后加2个字节
5 C% ?( f ~. _1 h- nJMP Unpacked.00409217
! o# L) F3 D% s
+ V1 V% Q$ r8 A3 U6 u
5 r# [0 a I9 {3 y3 w# q+ V6 Z2 q7 |( @* S6 M! \6 c! L# k; T
3 V2 \7 w. X+ pPUSH EBP
4 |, V; f/ F e& h% rMOV EBP,ESP
; _6 p# H( r$ q+ L# cSUB ESP,0C3 V1 u5 q& z) w6 g8 E
MOV DWORD PTR SS:[EBP-C],ECX& V) h. n& C+ b# d W
MOV EAX,DWORD PTR SS:[EBP-C]5 L" Z: k' [) _; T5 c
MOV ECX,DWORD PTR DS:[EAX+14]9 A) \- ~5 g- f, O4 E4 x
MOV DWORD PTR SS:[EBP-4],ECX
4 _# V1 L/ g9 d6 d5 ~CMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)5 i @) s! F6 h3 t; o) N2 l
JG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片
9 {+ v! C! k! p" M1 s0 QMOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱), d; w& Q E5 L
XOR EAX,EAX2 F5 U' b2 o6 n) q# y( j
MOV AL,CL 把汉字第一个字节 放入eax
5 v2 d& e( b7 a3 u& b$ USHR CX,8 右移8位取第二个字节& f3 d/ i5 c9 ?) H/ w' c! x& i2 Z
XOR EDX,EDX* Z3 R1 R2 L' j" N# l n& V
MOV DL,CL 把第2个字节放到edx去" P* B% c$ s( x' v8 v3 b3 T
SUB AL,0B0 这里是我自己写的算法 不用再读取码表了0 O/ H* s# H/ L: |. l2 j
SUB DL,0A16 C( F( D( @4 h0 c, M
IMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16$ i* M) e0 o' q1 ~- p! H- e
IMUL EDX,EDX,10
3 k3 y/ e% ?6 W6 H- p& EADD EAX,20 加上我图片原来的高度: Q4 `1 A6 B6 d; B( f
MOV ECX,DWORD PTR SS:[EBP+C]
9 D& \- V8 a. q y2 @MOV DWORD PTR DS:[ECX],EDX
' \) m# I7 ]+ J! WMOV DWORD PTR DS:[ECX+4],EAX+ y W; v: e) e8 i0 S2 c4 [4 D7 b
ADD EAX,10
; \7 Q% e6 k) ?ADD EDX,10$ M. A8 R; w! c$ k3 h0 s
MOV DWORD PTR DS:[ECX+8],EDX
) |3 |3 B& l: ^( J7 DMOV DWORD PTR DS:[ECX+C],EAX
/ o5 |! P; N, N" q& h: E) R+ A) a) zMOV ESP,EBP
1 h8 u0 T8 g$ @) `' m$ q! KPOP EBP4 W& B7 |3 S3 v7 S7 C1 U
RETN 83 [$ I) m1 F4 K, ?# U, K1 V
! N0 }0 {" Z) C# _
8 E, f% t( S$ F- l5 g8 Y0 ^ |