上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化 " K1 f1 o, B- g0 P9 Q
4 C7 W8 h, S: f' {/ u4 ~2 c9 ?
在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵 . Q$ d5 V* e+ w8 t9 X: }* @
这里是用bitblt贴上去的
. U: u2 U) K: H t: Z* H; m0 R知道是贴图就要寻找相应的字库和字符串
* V% f2 l2 A1 C* O. u0 p字库在image文件夹就能找到 " M( M7 p" j' Y7 u! n
字符串被压在了EXE里(脱壳后exe增大了3MB)
8 }2 a" S, C' K7 Q& V& B3 z' P. I. |8 u0 u$ E4 ]! ?
下面就是分析游戏是如何显示字符串的 ! Q0 j3 n3 H1 m
用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit" K* \8 `9 {* `
4 E Z0 x, Q$ Q) C按F9启动游戏9 C* w7 g7 f5 o
然后断在4F24D2处% |! T! y( e" g" N" U% _% T
这里是读取字符串用的 n& X, s1 b$ d$ {
+ w: O: T" u2 U3 d6 g x4 t
分析下; q0 C8 ], y- D. ]
004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit
: l3 J, R* D3 ]+ G004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0. d' Q# A# H8 s3 }! c9 N( m
004F24D7 |. |03D0 |ADD EDX,EAX2 F: K* {1 U4 L V
004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF
% L$ _/ N% ]6 a4 n: u) D004F24DC |. |33C2 |XOR EAX,EDX
% M" l! l3 w H004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节
2 m# M: a" m% L004F24E1 |. |A9 00010181 |TEST EAX,81010100 ( d9 Q6 P1 X+ a1 q2 K
004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出, {4 M" U1 }+ J( O3 E9 \7 v9 x
/ z$ K' J* T0 i
这里要注意一下9 ?, ~3 W2 E a9 | C$ v; `
因为是4字节一取 \0处一定要是00 00 00 00, x* f: [5 `9 r- N7 |( M
如果要把Quit6 F7 o# ]6 {! s9 g) U
51 75 69 74 00 00 00 00 Quit....Quit...." ?/ X/ \" z3 w4 m$ z, M
翻译成"退出吧" 就需要在加4字节
5 v# F! v; d, b地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]; a. o A$ `: K) @9 L+ t1 G: w$ o
只要把ecx里的地址改成你的就OK了
5 ], ~3 S. B# u( O2 L6 x; I8 M; F4 E7 |4 {' ~) r
继续找处理图片的地方5 K8 J, r" u1 h6 R
一路跟下来到了
' |3 r7 m3 {- s; L7 L+ t b! f 4 K) D1 c& {: u, ^8 T4 b
004F398C |. 83E6 F0 AND ESI,FFFFFFF0
% A S8 Y' `+ Z. b6 a$ T004F398F |> 56 PUSH ESI ; /HeapSize = 5
) u4 v O3 ^6 a( _1 y& I( X004F3990 |. 6A 00 PUSH 0 ; |Flags = 0
# S- t# s5 M+ m* g8 R: A8 e' F004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000
3 Y/ A( R; S7 w! x2 [004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc
; g' R8 W4 V" o8 b004F399E |> 5E POP ESI
% S4 w! {6 g! T1 Q
' n1 [" B- j! p发现HeapAlloc 这个是用来分配内存的
2 b$ a6 j+ P' h( [4 J! b( t1 r6 r0 V$ LF8步过 取eax的地址 就是将写入新的quit的地方
& G M/ n( {+ u7 q0 W对其下硬件访问 F9运行8 u: y+ {$ i5 X
断在了403811
4 A8 N) n5 a: F- x* b8 `, w2 O8 C在F9几次 断在了4091c44 }5 A% D c! n
在F9几次 回到了4038118 L- V# K; F: {, }; A
, @, H% A: n0 M8 N+ m' A m3 Q0 |1 d确定就是这2处地方了
: F" h1 P' n6 I: k5 m' j这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用
5 j0 Y" f$ O2 P( r6 L( h7 S9 V; t5 r
! O$ u$ x: t! r- {1 B" ~ E- a004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]
- V8 s: s% G2 E, T9 O2 y004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q0 J' X n: y. Y( x3 \
004091C4 |. |85C0 |TEST EAX,EAX
) }/ L. i: A3 ]7 z; ^004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出
) q7 M+ s$ W+ @) @004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]) S1 m3 q2 o9 t* q$ Z
004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q. |1 `4 v5 U: m" O J# b
004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格" X2 a0 }2 H: `' D/ P& W
004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8
9 D' |; e0 P/ V2 M8 _! W004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]
) w/ K' `) |( U8 p a. h8 j( G, N004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX
, T% x. [) ?5 w$ C004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 000000007 [( L+ m' K8 h- h6 l$ ]
004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
( L9 M: W1 i$ m4 J) M5 [! g* i- F V004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0
6 |" C) D7 j! [, J7 B004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]6 }' v, X- E% e
004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX8 e1 S* @+ x1 Y' L; x, a& d
004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
, c; w- ^1 E6 [8 L! z004091F0 |. |83C1 01 |ADD ECX,1
/ \0 W; I# x0 S; o. B x004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX! v8 `# d. C% a2 n8 n2 v
004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE L7 @, Q: f( G& _
004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里
) L; H- j1 U, a5 Y+ H( m( y$ v4 P004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)
, @- X4 ^ D7 X8 ?$ I! ?2 g8 h. t& C004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去9 F: o. R0 B+ N* _1 z' [
00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]$ i$ a, L( L" L4 b ^9 M. ^; U: n
00409204 |. |51 |PUSH ECX ; /Arg2
~# s1 `- p! N) P$ s% D00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |: v& w: X% m0 c5 V
00409208 |. |52 |PUSH EDX ; |Arg1 压入Q
7 I g, _3 A# K8 a* ~ ~( G4 M00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |: B6 F! P% A7 m5 ` }
0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键5 B8 q% m* T3 R8 {3 `: V; [
00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]0 I$ P! M5 ]& A6 N8 z3 S
00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u& C. V; u6 W4 J4 ?
00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX- }7 {9 B: ^5 I/ l# O; I9 b& H* [
0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]$ p! r8 g( U6 `$ M6 c9 v/ S
0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度
; N* j1 J/ o# U* O00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
: v3 g/ ]' }5 c8 c9 o9 z00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],0
3 i1 @+ J; Z6 P$ d7 Z/ t5 i00409229 |. |74 7E |JE SHORT Unpacked.004092A9
+ H- I3 q4 h. m3 u+ U/ x5 M) s6 ~1 T; L* Z6 V
* d' G1 N5 Z K7 U$ A
0 u! O9 o3 g% u& x/ @" k5 ]& U& r8 r5 y& `' o8 |- K
' o4 C# N6 C; M, ~
) d8 ?. a! _: Y4 r0 ~) h00404620 /$ 55 PUSH EBP
3 f, Q x) f* i, @& y8 Q5 b# n% W00404621 |. 8BEC MOV EBP,ESP7 c5 Z3 w! q2 M
00404623 |. 83EC 0C SUB ESP,0C
4 U7 r$ I' C* @7 G- z& l. b00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX # Y8 i0 L6 X% R$ L+ |/ J/ g. D
00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]1 r" w" a5 G9 j0 x
0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]9 W w% K2 q( g }; E
0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX
* `& K* j1 ]% J. @: S$ G00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查( `' b2 x. }2 d" ?: n
00404636 |. 7D 0D JGE SHORT Unpacked.00404645
P9 X# { n1 ?1 T& A00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]
3 |5 F2 d3 ]( o8 X8 q3 j0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL( A- }$ q: f' e+ \) ~* P( [; c; v9 \
0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5], V+ }5 |. N! S# N* V
00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX+ ^% o% c2 t$ e+ _1 j) N
00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查# A5 j& k1 \* s# C8 D
0040464C |. 7D 18 JGE SHORT Unpacked.00404666
- Z! o$ \ J1 ?# U0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]) G' ^3 j: W- B8 ?& J3 Z: j% r5 G l) d
00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]' o* H( f7 N: X, q% i
00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]
6 \7 r6 |. j W0 J% S00404658 |. 6BC0 14 IMUL EAX,EAX,147 I/ U) m2 B( A8 A8 r7 L
0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]8 F1 v8 w$ w; y* q0 j
0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]' h2 h6 |6 S6 o9 F1 ^" v
00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
9 o7 C+ h; q( Z& W00404664 |. EB 15 JMP SHORT Unpacked.0040467B
' S$ [, `. @; ?" b00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表5 c6 L# g. i. V4 [
/ \& w. v; @. L: v/ V: Z; `
$ @7 a" I% a* [8 f+ u H* J2 O
2 H* y, B I2 @5 ? D+ x3 r- j
0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<... q+ X M d2 S
0054AA58 1B 00 00 00 ...! `: l2 G/ c- G* W, O* r
( b. j4 O* t. F! E
51就是字符Q. O8 l, U Y5 m- A
2D 02 00 00
F* Q |1 k9 z( o Q) j7 c00 00 00 00
. t0 A" Z4 }2 T3C 02 00 001 `# `! h( d& x
1B 00 00 005 u6 h% n+ x3 L; S. [
, S% I+ W- T% J' z: b% a4 y A━━┓ " |- a) p" B/ u# @5 _
┃ Q ┃
3 w i/ m5 ^* H! w* N ┗━━B! |' `5 Y% g6 l
0 N/ x$ ~; z( AA的坐标 (22D,0)
! j8 c1 K" `# y3 Q( kB的坐标 (23C,1B)) i) k' f) x6 w5 g( M1 H9 c; u
) Z. T- w- I F( K3 W7 t, ]$ O0 x( E: Z J& m
5 r5 x/ ^; D( Q" D e00404669 |. 81E2 FF7F0000 AND EDX,7FFF
+ K0 G5 A. d0 @- Q0040466F |. 6BD2 14 IMUL EDX,EDX,144 ?% ^+ F! r7 [2 F, t6 A
00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]7 \& [! O! ~+ c& u8 ?
00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]
) k0 _ g7 X8 ^0 P; F0 t( }" @& b00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX9 d; g: R& x* W) ^9 c& X' q: b
0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
9 H! c9 \( J1 w, p: Q7 | `# k0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
6 l% Z" F3 `3 U3 x; R/ T00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]
$ y0 }( z3 L6 {. Y4 x5 q00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D$ ^- f9 e ]# C
00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
# S, w* L! r( C6 A3 Z00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]3 w& u0 D% v' b+ D* R# _* l0 |
0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]4 e6 P1 a( ^4 e1 ]% \) {9 G+ l0 t$ d
0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C4 M9 X6 U8 y" B! L
00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]* S! O+ e o# D$ U6 j9 K
00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
8 z9 U, S& g }9 b9 S: P( o, H) r00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]
* g: B) r# {2 Y' ~) z( r: N0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0
$ q7 _4 f, Y. B0 p% D0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
% c+ Y6 `$ B9 l004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]$ e; E0 \& L: P' @" Q! H/ n
004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]
7 a' A9 @! T( T% |8 x004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B! P2 d( B( S/ e( o
004046AA |. 8BE5 MOV ESP,EBP7 L3 `: {, a! ]5 q
004046AC |. 5D POP EBP9 K/ S* B0 p7 Y0 v6 q
004046AD \. C2 0800 RETN 8; Q/ n, c i+ y6 f/ B! {5 f
w. k1 P7 ?# K" D7 y# }
i3 f: {$ N$ R! q
) r. w3 G3 u- ~, q; S
8 N7 d( P, C4 B1 X+ d4 j% E! U8 u
; T5 C. C2 } E* Z0 l
知道这些后 就可以改造了
0 H% a6 A9 S7 ~: Q, c先改字库
) ?1 P& t, [1 y1 c4 z 8 Y6 B( J2 t$ _* O& \
6 }) q+ g; l) n$ ^. r( a7 r
然后改成支持汉字的3 c1 v# \/ D) s5 _+ L: s. ]9 c" V
把4091d2 的 CMP EDX,0A 改成jmp: S( F1 y! y' t7 }# z; a3 w
跳到我的代码上去
1 g% Y+ B' @% [. P q1 r
# E* C5 N! |1 C# XAND EDX,0FF 去除高位的FF
# B: u7 n$ t# G; w3 kCMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的
! l& _! G+ Q0 `+ N' _JL Unpacked.004091F8 小于 B0的就跳回去8 t8 i/ E3 [$ Z# t! a: |8 ~& L- s
MOV EDX,DWORD PTR SS:[EBP+8]$ Y7 q( k' l5 F9 l
MOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
2 G' E' Q: D+ c+ eAND EAX,0FFFF 去除汉字高位的FF/ d) D7 w& V( ?' f& U
MOV DWORD PTR SS:[EBP-40],EAX 存入形参
4 ?6 g1 G6 W- ]' A3 M/ f6 W1 @LEA ECX,DWORD PTR SS:[EBP-28]5 W& c6 k7 m3 p
PUSH ECX ; /Arg2
- A' U; p. {2 U8 ~0 r% LMOV EDX,DWORD PTR SS:[EBP-40] ; |
1 Q) z5 H1 I R& _2 r. LPUSH EDX ; |Arg1
% n" w; Q1 I+ {% c; s2 \MOV ECX,DWORD PTR SS:[EBP-8] ; |
* P" Y' A) ~/ y8 t' LCALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标8 X# w- ?$ {4 `( a* ^4 I- Q. |/ [
MOV EAX,DWORD PTR SS:[EBP+8]) m k D% J/ C# ~: ?
ADD EAX,2 call出来后加2个字节9 \. n3 w {! s/ b7 V
JMP Unpacked.00409217
8 s2 I/ @$ U4 `. x
5 e! Y/ T- y% \( N y, x& l* S; d) [. g; Q$ }9 q% O- V
$ |; R8 M& m! }* W' h" e2 w9 F4 V7 a8 Z- m) }" G( u0 N
PUSH EBP5 B) @1 v& y' Y0 r( S# q4 I
MOV EBP,ESP) K4 i) I. x1 J% @* J/ ]/ n; P
SUB ESP,0C) q8 ~: f% q: Q8 ~0 K# M5 L; j
MOV DWORD PTR SS:[EBP-C],ECX
1 w$ D: {7 x2 x YMOV EAX,DWORD PTR SS:[EBP-C]
* s9 l) h9 W( O AMOV ECX,DWORD PTR DS:[EAX+14]
- ]( O% h! c* g' `& EMOV DWORD PTR SS:[EBP-4],ECX
4 r0 M& A1 g4 M( {CMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)
/ V4 `. m# H! ~; Y9 a7 `JG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片
4 L7 E* v" P% o1 Q" E4 zMOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)
+ v1 m% y7 G" o: v* ?XOR EAX,EAX8 h: L: f2 L+ a1 M% Q/ Y2 i
MOV AL,CL 把汉字第一个字节 放入eax* \) L4 F6 j5 g% X
SHR CX,8 右移8位取第二个字节# a' _# y6 ]8 J+ b
XOR EDX,EDX
# N% b+ ?/ Y" U' l( \+ `/ {* g, BMOV DL,CL 把第2个字节放到edx去
& m; j; k7 G. s, w* i* OSUB AL,0B0 这里是我自己写的算法 不用再读取码表了: [$ N$ E2 U3 y8 u- s# v
SUB DL,0A1. Q8 A7 g3 b4 V8 W, d
IMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16' K" P( L' P2 m' a/ z: o' d% `
IMUL EDX,EDX,10- J! z/ F0 O' Y' k/ y/ v
ADD EAX,20 加上我图片原来的高度/ w+ j1 i6 w) `' W. g. V! b
MOV ECX,DWORD PTR SS:[EBP+C]
2 c4 ]2 d. f9 p+ }MOV DWORD PTR DS:[ECX],EDX3 P# b( k; H( S9 i9 F
MOV DWORD PTR DS:[ECX+4],EAX
0 |" m' Z7 S' i; a. g3 \ADD EAX,10, W2 o( v& y4 L+ }4 T3 G: l+ G! v
ADD EDX,10; E4 |* L2 B q* J& o1 f$ ^2 S
MOV DWORD PTR DS:[ECX+8],EDX
+ I+ S6 |% H2 C) MMOV DWORD PTR DS:[ECX+C],EAX1 ]/ X% x9 e5 e4 e
MOV ESP,EBP2 o6 A! g+ {/ E: Q
POP EBP1 p! {) Z9 E+ Y. G* h
RETN 8
( s n, I$ t6 e, ?$ W$ Z7 u: G, Y3 H5 O6 l! R
' l8 A7 S& G6 f3 O, c |