上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化
9 r9 ]) I% ?- |/ @7 W4 A/ R7 j# T7 ~# u2 c: ]" J0 W9 \
在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵
6 r9 r, g* M( V$ ^; |+ f这里是用bitblt贴上去的 ( E4 A$ Z7 l8 A q% b: a: X* H
知道是贴图就要寻找相应的字库和字符串 9 ~ N& j$ o/ Y' P# E! J
字库在image文件夹就能找到
- q' s5 i. @5 U! ]7 A( H$ z字符串被压在了EXE里(脱壳后exe增大了3MB)
4 O! R% ?/ V4 ?( \) f& i" F; G9 K* R9 q+ ?0 `/ A
下面就是分析游戏是如何显示字符串的
0 D: K" _! D* u用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit% `& R2 i1 d. e6 H+ }6 w2 \
: M- F4 B: }5 P/ G1 w按F9启动游戏! k; Z B( u$ G6 c7 U$ N, q0 B# Q
然后断在4F24D2处# A+ W* {2 p$ q
这里是读取字符串用的3 O8 f$ k7 c% U. S# P
% N. X! J8 m" W, V0 i4 X: J分析下
: b* {! [8 E! n004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit
4 O% h1 m( S5 P. _% N4 _5 Q004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0
& B7 I5 y$ Q4 X8 o' C! b% d004F24D7 |. |03D0 |ADD EDX,EAX
' L* g8 D& Q; K3 L, P004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF' S8 a+ r& C: A' W
004F24DC |. |33C2 |XOR EAX,EDX2 T6 s& O, T& y' p
004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节: v8 z& y$ W6 Z' R. I, H
004F24E1 |. |A9 00010181 |TEST EAX,81010100
; M- R7 r# [! N5 X' Q" N7 N( l004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出% x5 s$ M$ A+ s
' x0 L3 F# |! v7 W3 ~- a3 _这里要注意一下( w1 U2 l: w2 v. m: [; H
因为是4字节一取 \0处一定要是00 00 00 00
. |/ E% y: D# G: G如果要把Quit$ h/ u4 @' J z# O8 y# c6 ?3 d ~
51 75 69 74 00 00 00 00 Quit....Quit....
% J9 X# H. }# v7 ^' Q翻译成"退出吧" 就需要在加4字节* E9 N6 S4 b& t% O: p' K
地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]7 b( P, I+ Z) ~" G0 d
只要把ecx里的地址改成你的就OK了
; W. s4 n ]- U- b( b0 R8 v1 v. Z Q: O; o h
继续找处理图片的地方8 }% m( }) n+ V- M0 o
一路跟下来到了
1 C0 r+ H1 k. K
: W4 E$ b3 P$ j' V1 B004F398C |. 83E6 F0 AND ESI,FFFFFFF0
4 r* c5 X! m" b/ d2 b* ~004F398F |> 56 PUSH ESI ; /HeapSize = 5
! ^0 l6 F- X3 L1 m004F3990 |. 6A 00 PUSH 0 ; |Flags = 07 l; E% j! g0 W, T1 w
004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000
* ~+ f7 y7 x) Z7 b% i004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc
* _6 k! B9 P9 W' K) Y0 p004F399E |> 5E POP ESI
, o; ]9 T1 D, A" _2 `2 u" z' b# Q4 e1 [- f) G- N& S: e
发现HeapAlloc 这个是用来分配内存的
* m6 Z# r1 `4 ]" k3 j8 v/ g8 ~F8步过 取eax的地址 就是将写入新的quit的地方
; I$ f; s$ f$ d1 n9 l, s* c! }) Y8 T对其下硬件访问 F9运行
" L1 B: J- g$ }( K断在了403811$ k0 r8 z8 V0 H$ Y
在F9几次 断在了4091c4
- a' T/ j5 ^* s% m在F9几次 回到了403811
) h/ K; y7 u- h0 x( n + V D1 O$ x( Q1 K" j
确定就是这2处地方了# k$ A5 C! C- ?8 B
这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用
T$ D1 g# L& l" J. [. o: w2 a+ Q- \
004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]& A- m& y3 n Y' ^
004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q B. @+ R( S% p8 w
004091C4 |. |85C0 |TEST EAX,EAX
$ b& [5 p6 w& G; @! m: ^6 v! g004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出
# x4 J9 C2 k W7 O+ N4 W; [004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
. Z. y5 b+ q$ g' i! o$ X004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q
, {6 W7 j' B) L4 @9 Z004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格
: k, H4 X6 [' C. z: u* }004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8, Z3 Q$ o. r4 k0 ]4 _( d
004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]) c1 Y) ?3 H9 `% Z- r2 Z
004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX
/ k4 `9 P/ p5 r$ Y, [' ~! ?004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 00000000: o- o" n& c% t! C$ J* |
004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |- z1 Y4 a `; c
004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0- m9 J! b% r9 Y! S2 g
004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]
m3 J" |: t- y2 W004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX
+ p# T. X2 `! c3 H* x004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]2 R: w; e+ z# t; H
004091F0 |. |83C1 01 |ADD ECX,1. R v8 B. s. S5 g
004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX
+ b+ m) r, A: }4 i1 d004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE
( O5 c O; u) T% P) D. V004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里
, o" U' L7 F! s) |004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)6 J4 n6 \1 m1 Z; }0 Z/ l8 \
004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去
8 N! i* [/ U0 r. Z+ d00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
. { k2 a! L6 g00409204 |. |51 |PUSH ECX ; /Arg25 G5 H2 a- C9 s/ ~
00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |7 x; S6 x9 [0 t5 Z/ p1 ~6 q
00409208 |. |52 |PUSH EDX ; |Arg1 压入Q6 j- {$ ]! T7 ]! z9 P+ Q! d
00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
( p" O; M/ N% G1 m: e0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键) r1 m/ w1 T3 Y3 E
00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]* ], T' m/ X# q
00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u/ }0 s7 j& a1 n: ], D0 m
00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX' ]. O: \0 W1 A, k+ y8 d
0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]7 k$ u0 }6 t+ j8 V6 i+ u; t2 i
0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度
3 u" ?; j+ e5 u0 w- r4 v00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
2 c% U. _, H2 a; q00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],0
/ { f9 i/ G+ r/ ]% A00409229 |. |74 7E |JE SHORT Unpacked.004092A91 T$ F6 Q( J. X) z9 _
6 y% Q- c+ T) i4 @1 |
% e3 C! y/ v/ G0 A) h7 V; C3 D7 W5 f) @! ^( r) S4 K6 f
# W* G- v7 V7 b+ o/ H2 f% `8 Z* G+ ?; k' A/ [3 E4 M- u
! b; S+ p& r5 p$ s00404620 /$ 55 PUSH EBP
% S" Z9 `, B8 W) k! b4 M00404621 |. 8BEC MOV EBP,ESP$ k' S1 m& M& y1 t" b R
00404623 |. 83EC 0C SUB ESP,0C
0 ~, k# X/ v. T8 O. {00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX
* A; [- u0 z$ k% j* q00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]1 Y; \- q8 B7 P3 t7 a* D! \* N
0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]
k. L, D$ c; t6 E) Y0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX5 l0 N. I- U8 Q" }- j- t L1 d
00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查# g, E7 R5 W0 d8 |: }
00404636 |. 7D 0D JGE SHORT Unpacked.00404645
6 N! {2 n A% U. s" S$ _00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]9 r( G; U- }' f! V2 z+ x
0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL* ^5 T: `; G0 _; B# R. d; j
0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]
; U/ N- g5 w. x4 T% l# \$ t6 ?1 k00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX7 S0 c) |* f# I
00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查9 Q3 v% }2 I% d! j! ~
0040464C |. 7D 18 JGE SHORT Unpacked.00404666
+ S. u, O( ?/ b F7 G! L. s" m0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
, z$ ]* H' ~, c* g7 O h7 o0 F5 l00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
8 G# N* f& k4 ]% [* a00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]
2 e; ]1 i P: h y% y4 }00404658 |. 6BC0 14 IMUL EAX,EAX,146 y8 d. y% Z e$ R1 ^5 Q
0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
a9 w" `, b) }' ~0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]
8 p# |- b: \) A. ^0 T( I$ {00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX" q) G% Q7 f) a$ ^4 a7 ~
00404664 |. EB 15 JMP SHORT Unpacked.0040467B
% `" b S, `2 ~& l% \7 t5 h) e00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表
9 i9 m+ t) Z9 j6 ?" T
3 N/ E A* R) d( i, x
- h# f; r# r) {2 u Z, y
7 M& E, f* y5 |2 b) S0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..$ g6 v$ L* o; }: o) c% W* [
0054AA58 1B 00 00 00 ...7 c4 r, u) e5 s( V# R b, g7 S& f
3 G) Z5 N( l4 C. X51就是字符Q
9 }" `- o1 U* P. {2D 02 00 007 i4 M$ c' s% m% G5 q" Z0 t
00 00 00 00' J/ U% s/ g1 z9 t0 Q; g
3C 02 00 00
4 u- P! @; f$ x, y1B 00 00 00
: t0 P0 b/ n, ?, @4 f
! ]- `+ f0 \9 C* x4 x7 m) a A━━┓
: ^0 ?9 P0 ]3 T ┃ Q ┃ ' {# n0 z$ t8 z, g
┗━━B( [6 |" i5 m1 |4 P% f$ ~' H
2 P" {$ p& d4 H
A的坐标 (22D,0)
- K+ p2 O0 x% XB的坐标 (23C,1B)! F! Y8 ^! a) L* a, L @
* B% _6 Y& K# G. }" {% Z6 G8 @( s9 N$ f. v$ G5 `: }5 @. G. E
5 ^. _% ~4 A4 U; E8 v) w
00404669 |. 81E2 FF7F0000 AND EDX,7FFF
+ H7 F p' O6 N, H* N8 h5 U0040466F |. 6BD2 14 IMUL EDX,EDX,143 H3 {3 R% z- `4 B5 O
00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]: g* C x- ~/ {% [2 `' B) F
00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]
; g9 C$ d8 ]4 ?/ D+ H& z3 R7 q( w00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX* H3 |, \6 y7 Z0 e5 n8 q
0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
, b: @$ R( q1 x, W! J1 Y0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
l3 O' N6 M; ?! \8 ?00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]. y' c' {# ]9 k9 s! S
00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D
4 {2 q6 X6 e3 v% h. N' r00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
3 `. t8 @" ~/ R9 i00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]- f: u) _* G' G7 Q5 a! Q0 ^% c
0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]
/ v( y' U0 _, g9 @3 ?0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C/ I& N: B: c6 Z. m& [+ z
00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
4 g& [, K' U5 } w6 N3 S00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
* [( I8 D1 X/ B& e00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]
6 Y2 Z% l: o0 l0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0
' J1 I3 H+ i, q) \0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
# o# I. ?' Y K, D6 k8 t004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]7 _# F8 o5 G2 s" v
004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]
' A; f" z6 ~8 w: w- |004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B
" O6 a7 a& u8 _5 G7 \: a$ g! F |004046AA |. 8BE5 MOV ESP,EBP
+ L/ i& q2 T" ^* }% d004046AC |. 5D POP EBP
; }8 Z4 `& }$ \ W# v4 z004046AD \. C2 0800 RETN 8
7 I/ ^$ o- T& U+ e! A0 N) j" G/ X0 _/ {0 G) h" ?
" |9 s$ Q( H0 {$ b9 H8 m2 `6 S
; k9 x2 L5 P2 L! D/ ?* O$ E y! X" [# r2 L6 K
) K( Z1 h5 b6 F/ t+ O. v
知道这些后 就可以改造了
# T+ N; Y- d8 Y( O I" y先改字库* h- A& b4 O, y3 e4 Y
1 g. E. M9 I3 D2 |) w7 W! O6 d8 F
然后改成支持汉字的8 K" w# R$ T% _' T3 o, o
把4091d2 的 CMP EDX,0A 改成jmp
0 G9 a& ?% A' p跳到我的代码上去5 _* ]% t* A u, y1 z
. d0 c3 b3 G' Y. A: z$ R4 x
AND EDX,0FF 去除高位的FF
( W; Z" Y& p/ g! ICMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的
r% ?; w" @: d* f" O r2 h5 gJL Unpacked.004091F8 小于 B0的就跳回去1 B' s6 g% x8 y. e
MOV EDX,DWORD PTR SS:[EBP+8]* D5 s: B: w( j4 G4 {7 b3 r8 M
MOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
" x, a" E# C: v! o3 e( B" q( VAND EAX,0FFFF 去除汉字高位的FF, [9 e$ _ r4 ~5 Q. Y) F
MOV DWORD PTR SS:[EBP-40],EAX 存入形参, W/ e( U( w; ?/ V( r
LEA ECX,DWORD PTR SS:[EBP-28]
" p* _/ U* N F( I5 `1 q' b2 N3 @PUSH ECX ; /Arg2
9 F1 G% n. z% V3 O3 Y, L; pMOV EDX,DWORD PTR SS:[EBP-40] ; |/ [; `, {( a0 j# L4 @& D
PUSH EDX ; |Arg1* q$ S) t! s2 K
MOV ECX,DWORD PTR SS:[EBP-8] ; |
: a5 S( {: f! q: J- o" _7 X0 C3 k* ?CALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标
1 d+ v! j# h7 ?: aMOV EAX,DWORD PTR SS:[EBP+8]! S; P( O2 }1 p; N, v
ADD EAX,2 call出来后加2个字节4 N6 b! g' ~7 G6 Z/ A
JMP Unpacked.00409217
; P* U/ S+ u: J& v+ y& X9 `) s9 Y3 o+ w/ ~
! ?9 \& a/ K! `- B* }2 T5 ]& T8 H5 M
9 r: p5 _9 x6 @3 C, t) ?& K; w
# t* [: U/ ]! S' H) m# fPUSH EBP
6 c+ H) b+ d! q$ t- f$ k1 ]MOV EBP,ESP7 b6 I& {$ |4 k3 L" o
SUB ESP,0C
E: X4 g5 |3 u; T6 m1 X8 }MOV DWORD PTR SS:[EBP-C],ECX
5 a2 Q* K: `. Z! h/ Y& lMOV EAX,DWORD PTR SS:[EBP-C]
. `9 f& Y+ I" D; d& g8 f9 H3 `MOV ECX,DWORD PTR DS:[EAX+14]
8 u0 @+ I+ @- v- PMOV DWORD PTR SS:[EBP-4],ECX8 a) S7 e# D% t) C2 y
CMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)
8 p+ J& i7 M" M$ w, T& pJG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片
: J* g% w" g$ N+ l9 l# KMOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)3 G/ }6 ]* W+ E/ U
XOR EAX,EAX
& ?" {/ P9 b5 SMOV AL,CL 把汉字第一个字节 放入eax @) ]6 |. W2 O1 c$ ~
SHR CX,8 右移8位取第二个字节+ y" { I! m# E$ Y
XOR EDX,EDX: b" Q5 T7 r; O: k! W- {
MOV DL,CL 把第2个字节放到edx去 o% q7 B* w3 h$ a
SUB AL,0B0 这里是我自己写的算法 不用再读取码表了
5 k/ T% P/ X9 h/ uSUB DL,0A1
5 U# y, W O( {% ?7 c4 NIMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X163 q4 y% ]6 ^; z! \ k" j h
IMUL EDX,EDX,109 k5 ?; W" {# M% m2 R" ?' w
ADD EAX,20 加上我图片原来的高度* q* ^, _; K8 A4 i4 F, y+ v: x
MOV ECX,DWORD PTR SS:[EBP+C]
- O5 r q$ V2 A0 ^MOV DWORD PTR DS:[ECX],EDX) U U% |5 e7 g$ @8 ^& I2 k6 q
MOV DWORD PTR DS:[ECX+4],EAX5 g! K- { o8 l9 F2 }$ V
ADD EAX,10
5 k/ {5 w+ A& k: E( e+ X& jADD EDX,10
; G9 l9 ?4 o9 t( SMOV DWORD PTR DS:[ECX+8],EDX
0 y5 z; Y8 W+ M3 B7 T0 ^MOV DWORD PTR DS:[ECX+C],EAX' W9 T! g* e3 f9 U X, b
MOV ESP,EBP
5 O7 v$ P+ M7 h* L* O* i. O0 gPOP EBP
- P% N* h( }6 ]1 t! Y& P$ ^: p8 P7 `RETN 89 H4 b; C' k; @3 H) u- k
1 A+ [) d, B# |
6 Y- j4 P5 K7 a0 d$ _$ ?2 |' \1 T |