上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化 / b$ n% M1 }. O1 a6 W
; Y2 b9 Z( p& C+ z: h; O在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵 " r+ z. N: x8 _ }4 M$ ?6 t
这里是用bitblt贴上去的 ' m2 @2 {5 b: M# A) e0 e( z. J5 L
知道是贴图就要寻找相应的字库和字符串
* z, E8 U- ` r6 M0 h' b; }字库在image文件夹就能找到
! q D) K, w9 F- L( D m6 T字符串被压在了EXE里(脱壳后exe增大了3MB) $ H* \* f2 R& g" M$ ]. B
6 F0 F2 Y1 W" S1 c! h% v0 {
下面就是分析游戏是如何显示字符串的 ( N a. d$ \, X+ ^" p
用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit2 O" `5 a% X3 h# k
$ I$ n. Q2 A# \$ L2 n按F9启动游戏
1 s7 r+ s& V& N3 l% D/ Q然后断在4F24D2处: c- y0 H, i0 [# ]! ]8 L
这里是读取字符串用的
! q4 l% Q5 M) B% y" y; \3 i
7 h+ C' K6 ?2 M分析下
# G; v" L# q3 ]' p6 K# g# `004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit+ D F; n8 J @- a' s
004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0; z( Z) D+ M* s) ]+ D5 @' o; [
004F24D7 |. |03D0 |ADD EDX,EAX
( ]9 v3 l7 l6 d2 u4 U2 y/ x3 ]) c004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF
6 ^5 ^: n) d+ U: o004F24DC |. |33C2 |XOR EAX,EDX$ ~/ K( o( q# I7 e& J p, W' @
004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节
8 ~% T) V( A/ T% B; P+ @* @004F24E1 |. |A9 00010181 |TEST EAX,81010100
4 D$ }+ p/ y; h. D004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
' a1 F4 \7 n7 d" \4 G" r& Z& W4 v8 Z& I6 ~/ Z1 }
这里要注意一下# Q! ~+ T, X5 K
因为是4字节一取 \0处一定要是00 00 00 00
& b4 C* Q+ |# C1 r# i3 D如果要把Quit
' l# c8 s6 S/ g3 E& `- A51 75 69 74 00 00 00 00 Quit....Quit....
0 d, `- T0 I7 E, P+ W+ u1 T5 B翻译成"退出吧" 就需要在加4字节
) ]9 A- K$ b' a, t1 \( l地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]
) H) g9 g: @5 w2 y8 t% H只要把ecx里的地址改成你的就OK了
) _# j- ^ f+ R' D6 F+ m: X/ t$ T( U' U: r/ C' s
继续找处理图片的地方 Z! E& c/ @( v# [; @* e) H
一路跟下来到了% ~$ _0 l7 O4 i4 G' N3 t
* N, V' i9 q# t: @" d9 {) G
004F398C |. 83E6 F0 AND ESI,FFFFFFF0
5 `+ U: L I# W4 f. H7 c7 x/ [004F398F |> 56 PUSH ESI ; /HeapSize = 5
( c- a) K. g! o9 @3 m' n2 ~4 ?004F3990 |. 6A 00 PUSH 0 ; |Flags = 0
`' ]) w) X1 _. J004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000
5 I( s( z, y/ V/ @004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc
3 [" B/ ^6 G7 }" n004F399E |> 5E POP ESI
4 W' d5 ]+ B: x3 p/ ?6 t$ h3 K: M: m3 b, s
发现HeapAlloc 这个是用来分配内存的2 P6 r4 i. b; _
F8步过 取eax的地址 就是将写入新的quit的地方. {/ D9 v( F$ @% a" h( h
对其下硬件访问 F9运行; X" p9 W$ y3 O5 [. c
断在了403811% j9 V$ J# P5 F
在F9几次 断在了4091c4
6 N, z6 T6 T$ R$ X/ }6 f& a+ ?在F9几次 回到了403811- }4 b! Z1 C7 h& |" p. m* X
! s2 n7 \4 S* N6 g3 r确定就是这2处地方了
9 \0 F- n m5 B; I! K0 c这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用
& o; Q1 z3 w* Y
7 g2 S3 ], i- k* [004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8], }5 `' Z' E S2 O2 {& z
004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q
! q0 n1 ?. B! S004091C4 |. |85C0 |TEST EAX,EAX
! D6 j+ q( \# ~6 M" \004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出8 m6 k- `0 n0 h2 a1 Y/ x' v
004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
+ P* W1 V3 T, u# Q) b; ?004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q
) c$ Z2 c6 J/ A ^: {) J004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格
1 h P: W$ f, n+ P% k" ?004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8
6 ^3 m$ O+ h5 k& K) q004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]
$ a1 [% i: j1 R4 p8 b% G/ M004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX
, _1 Q8 R/ }2 D, v9 J9 z7 g004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 000000002 x2 |0 w% N; l
004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
G! D2 z0 p' C- p$ {1 O004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0- l8 Z( a+ l0 r4 V
004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]
' `; M9 {* z! q1 h' ^/ [004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX9 ]( Z) V% J" c6 y P* X& W
004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
5 J3 }% U% b+ z, j004091F0 |. |83C1 01 |ADD ECX,1
2 L# [, R v; d5 x$ n; f004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX! e" W s- g/ P( J/ X
004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE
7 k7 m; p9 a. G3 @9 q004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里0 p* o9 k8 ~+ Y, n
004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)8 z5 @9 |! o8 @" ?7 I" A8 k8 p8 n+ N9 T
004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去
% c' u3 i$ |5 k N; E, n3 ]/ w00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]* T/ m& ~% D: }$ l9 ^
00409204 |. |51 |PUSH ECX ; /Arg2% M& i- h8 y+ U1 C$ e3 o
00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |& w' O( W# R! m* h
00409208 |. |52 |PUSH EDX ; |Arg1 压入Q
9 \+ L& u& b- z$ U00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
- H* z8 B5 }9 y: @9 X: t) X0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键
; ]2 a5 V$ N6 M: K* ]6 g$ Z- |00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]
* Q7 A9 E: t9 g% T1 c9 T9 Q00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u$ R. M9 j7 `6 D. J
00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX
( B. j+ d6 _! U* ?: ]) ^% z3 N0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]" v9 k8 o* C' F; i* a& e7 q) g/ q
0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度, o) m' h; J6 c4 E/ [7 E
00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
9 k) q/ T; b7 c/ I) [, |9 h00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],0
: J. c' \3 K8 h. w- T7 p00409229 |. |74 7E |JE SHORT Unpacked.004092A98 k* ?/ r. h& U5 j. e+ M
7 l: r# K7 D7 w3 L' [
- f# B' w- `8 C) U ^* U; i2 B# h. ?
! q! v8 D7 {6 |# l7 X
; l6 P, `% q% z; w0 \9 y! s
2 Y9 d0 v4 b, ]- Y4 [; C* @( |) |) E; e$ k
00404620 /$ 55 PUSH EBP
$ H% W' k1 F4 u0 X9 H00404621 |. 8BEC MOV EBP,ESP
9 d7 a6 r; f! ]' M s00404623 |. 83EC 0C SUB ESP,0C
: X. I7 O& P! G }- {00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX
% P) a* E9 }# R, x. I00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
' K$ W6 A5 ~. G% W0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]- }$ s: g- I! i( ^* B( i+ u" [3 u
0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX
; F3 B. S6 _/ O2 X5 ]. s' l00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查: y4 B+ O6 b' Y/ M( y, H* z8 E! ?/ b( d
00404636 |. 7D 0D JGE SHORT Unpacked.00404645
4 ?3 A7 t! d, `9 e00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]
. r( ]7 M7 s8 Z9 i: h0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL6 [8 m, v0 x! O7 u
0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]
* v+ {5 j. j$ ?00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX F9 _/ K* K" J. `
00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查
) B9 Y! h- R9 q8 l' B( U0040464C |. 7D 18 JGE SHORT Unpacked.00404666
4 v& w' v: m/ V& b. r0 p$ l0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
# M: }, o7 F, m00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
% m4 D: q) D1 M6 U" J: ?00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]1 n) ?" A. j' `( \) E
00404658 |. 6BC0 14 IMUL EAX,EAX,14
$ E5 [: ?5 A% Z8 Z0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
; h5 C R% v- k* D' i0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]% B# c3 I, j& }* {0 H6 V5 t
00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
& |# V/ n' }0 g00404664 |. EB 15 JMP SHORT Unpacked.0040467B8 r' _# _' f4 Y4 c
00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表' a: @/ U7 K2 T8 N4 I
% u7 W5 K7 f2 d3 R% s5 ^
" ^" a. ]. e7 T3 R$ L' J: ~& u0 p/ L c/ u: l
0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..
/ F/ v: X+ g+ g# h0 I0054AA58 1B 00 00 00 ...# j7 Y. _0 Y3 ?5 e% r5 Q7 H
& ^! `) Z: o+ k* M5 p* e1 _ a4 z51就是字符Q9 s& l1 E7 ^" A. I7 r
2D 02 00 00* ?7 _! C5 P5 P7 M' p* {1 m
00 00 00 003 i( d' A+ U" B+ i. x
3C 02 00 00
( h6 @* y1 W) m4 \$ v1B 00 00 00
! I1 M" ?4 T: L7 `* I, J5 Y T* w, n$ u* k2 h
A━━┓
3 f! k5 B% w% U0 x ┃ Q ┃ c/ V; V) T* ^
┗━━B
% [: Z4 e9 S, V* R! |) N
3 U; t4 H2 M. F2 n- cA的坐标 (22D,0)4 Q1 `" D2 e* D9 m, M' J0 T
B的坐标 (23C,1B)
& r) f$ w8 v1 |* R& n' c- |
- }* T' g/ H) b- u( B
- F$ B% Y9 [) u: q; g5 r7 G/ N- J6 A$ T) n
00404669 |. 81E2 FF7F0000 AND EDX,7FFF
! `8 S/ u. Q( n4 Z: N* S5 n9 J+ r0040466F |. 6BD2 14 IMUL EDX,EDX,14
2 F3 n$ o2 U) R) h1 k5 [7 q" ^00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
3 R8 a& i. c! [: R& r2 v5 v00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]
; K: A3 H/ }4 ~. F' O# G. ^' y$ D00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX
! z3 i' A" Z( F0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]2 Y" }$ Q+ i0 o5 M
0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
$ N8 `. t1 N% L! j( B2 e00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4], N; Z) J# k+ f
00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D4 C( Q! S! Z3 s1 k: f
00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]- _4 R3 S" W1 w8 C
00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
@$ {# G2 |) D: o! j0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]
3 a$ B4 M1 G' N) v8 t3 J# f0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C
4 @, W' @0 N! ]$ z% S00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]) Y5 w+ l& f! o1 [0 V$ S
00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
x2 b" h7 K+ o7 ]3 s% v5 v1 f, q) ~00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]
; x( g( F- _/ C }4 ~) J7 q0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入08 I( c6 v) C3 U& k
0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
% T& m* i g$ _, ?004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]: D/ D8 {' B+ t
004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]
& F% @) Q* P$ }( m" u3 j& ^& T0 h004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B" f) _4 _; k8 n. U) m) s9 e9 C
004046AA |. 8BE5 MOV ESP,EBP
5 g8 Z5 V$ n: C) A9 i7 ]+ @004046AC |. 5D POP EBP
: ?7 @* S2 A( ~! T$ ^; e G004046AD \. C2 0800 RETN 8' ?4 |" j* @1 L" T7 @0 e3 Y0 m Y
/ O! W$ O Z2 R$ d
7 [; e6 Y/ r& O, z4 j
: C, ]8 N* U; v1 h6 j& {# n* W# P$ Y7 m
+ n& v/ ?3 k W7 U* [! X% U6 V& m4 a
& U7 z( y h( I6 e1 ?4 P7 e知道这些后 就可以改造了
/ v# _3 s* W- r: _先改字库+ n' t9 p0 k- T
* {7 \4 K$ N8 i& Y. q. v! o
: P% b' \2 S$ \- k2 ?9 V! ?
然后改成支持汉字的
% F4 e) s9 `2 ^/ @8 B把4091d2 的 CMP EDX,0A 改成jmp0 |7 ] F4 a V q3 d2 Y
跳到我的代码上去9 x6 Q8 k: K. }+ a: u7 q- p
" G5 f* ~% A* F+ J! k% zAND EDX,0FF 去除高位的FF
! ]5 o/ D7 o! UCMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的8 Z4 Z) i5 o" Q. Y
JL Unpacked.004091F8 小于 B0的就跳回去2 `2 G7 @# p' r& U$ [/ B7 X) b
MOV EDX,DWORD PTR SS:[EBP+8]
9 ?! {! F( x0 e ?- ZMOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
, \( g& W: } O! j6 K4 zAND EAX,0FFFF 去除汉字高位的FF
% z& L8 L% e: l" ~# kMOV DWORD PTR SS:[EBP-40],EAX 存入形参1 o u( n" z; X: L
LEA ECX,DWORD PTR SS:[EBP-28]* C( ]; e5 I7 A6 X5 h4 A
PUSH ECX ; /Arg21 b2 B' t+ \6 t$ v1 ]! A
MOV EDX,DWORD PTR SS:[EBP-40] ; |' {& ?5 {5 X( P: A
PUSH EDX ; |Arg1
* {( d: ~2 L5 aMOV ECX,DWORD PTR SS:[EBP-8] ; |
5 i2 P5 ^" q$ P8 cCALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标* x; r* [* a6 ~5 \
MOV EAX,DWORD PTR SS:[EBP+8]
& l# Y* k$ T+ W% c KADD EAX,2 call出来后加2个字节9 z2 @* ~; ], R4 [
JMP Unpacked.00409217; {& }" B- D4 \$ V/ ?, \* ^3 }! l
5 }, ~8 S; \* v8 \+ W% u' M( W# O) P" q+ m( l
3 U/ ~! I; V8 X/ | t- k1 z* s5 x: P2 w9 E6 s
PUSH EBP% j5 w9 e5 u/ |2 u; f3 ~
MOV EBP,ESP
6 v6 e, |0 n' @+ d) d# y lSUB ESP,0C
- j8 [3 J# T0 W8 N0 N4 b- B5 aMOV DWORD PTR SS:[EBP-C],ECX2 J1 R! P1 Q8 M9 F$ L! G1 D2 W0 ]
MOV EAX,DWORD PTR SS:[EBP-C]( o* k! J6 M/ e1 H9 K, d/ o
MOV ECX,DWORD PTR DS:[EAX+14]6 d# Z: e* x; t$ G& r
MOV DWORD PTR SS:[EBP-4],ECX
7 e& _% |2 d" d% P/ Y" F pCMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)) c. l6 d4 H) @% _
JG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片
5 N" [! t. P! [# y. jMOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)
4 r* f. Z4 T2 ?8 q1 [0 j, X5 VXOR EAX,EAX/ H& c" _$ P) ^( C
MOV AL,CL 把汉字第一个字节 放入eax
$ {! q5 Z5 j9 t* VSHR CX,8 右移8位取第二个字节9 ?7 x) }- o$ f/ ?1 C5 S/ Z1 c* y
XOR EDX,EDX9 u* s1 ^; Q! ^7 w
MOV DL,CL 把第2个字节放到edx去9 j. O9 a$ [, \# S5 H
SUB AL,0B0 这里是我自己写的算法 不用再读取码表了
( S4 ^( _+ N0 o3 S8 s% m" O6 RSUB DL,0A1
5 O! ^2 j+ h) tIMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16
/ q. q5 P/ x, M6 ^3 Z, pIMUL EDX,EDX,10) d# l5 n7 K! z9 s
ADD EAX,20 加上我图片原来的高度
- A D) @& }( i, k5 n/ iMOV ECX,DWORD PTR SS:[EBP+C]
" u: z- y8 V q. k% z$ E% R3 D2 ?MOV DWORD PTR DS:[ECX],EDX
, J) B6 \# o; {. X" s QMOV DWORD PTR DS:[ECX+4],EAX
! E6 w7 b* U& F5 w8 f3 DADD EAX,10
) J* J( \3 z' k) \0 TADD EDX,102 g0 W% x$ W1 x' b; x
MOV DWORD PTR DS:[ECX+8],EDX# G" g3 K' i6 k1 P+ o1 y' F2 k$ ]
MOV DWORD PTR DS:[ECX+C],EAX
) E& n( H1 J3 oMOV ESP,EBP9 j# r; m4 i( ~3 O) s
POP EBP
+ M* [2 d" B7 @9 S' `6 ^, |- ?2 yRETN 8
5 ^5 Y4 A8 I' E$ X6 A! _8 D
. r: E- c) |# _8 j3 Z3 C( P7 l; |& C0 [4 L" P4 O
|