上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化 ; E9 l0 Q7 o3 v( b2 V! O$ Y
& v9 H2 b9 b3 ]% t在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵
. L5 V% ]8 y/ U这里是用bitblt贴上去的 9 @" \5 E! {+ E& n
知道是贴图就要寻找相应的字库和字符串 : Z0 F1 d1 I) s7 V9 j
字库在image文件夹就能找到
) e! F* h7 e i* k* y% {字符串被压在了EXE里(脱壳后exe增大了3MB)
, F# |3 t- F' i4 Q7 ]5 |/ }2 n+ s* C( c! ^6 \- s A$ B5 T
下面就是分析游戏是如何显示字符串的
! |" Z3 e' e! a2 i1 g用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit1 v9 D4 `" z3 Z0 l: H
$ f% C. v3 m1 h2 y# B3 t3 d3 i
按F9启动游戏- f5 {- }. A6 K7 c# g
然后断在4F24D2处
' P5 ?% U0 {) [; u; [3 W3 p这里是读取字符串用的
" O g2 Y2 s2 R% P
" Z+ E9 t' F+ _, \+ s4 l分析下
8 ]6 E" p2 k& K9 r/ K004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit7 n8 y' Y% y g# q( C6 f
004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0
! A1 v0 l1 L! M( F2 {004F24D7 |. |03D0 |ADD EDX,EAX
1 q l, {( E0 v004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF" J: ^3 S! }! W H7 O9 W9 [
004F24DC |. |33C2 |XOR EAX,EDX
1 T* |% {/ b! a4 W. u004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节; O& M1 m$ Y5 K( T5 s F6 k, O; M
004F24E1 |. |A9 00010181 |TEST EAX,81010100 . e6 }' v( X8 ], z! e1 ]0 ~+ W" [
004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
/ I( N- [/ t, G1 u9 }4 l
2 G, B' a j! }- d) \# T0 V m这里要注意一下% T3 K- r3 Q8 U$ H
因为是4字节一取 \0处一定要是00 00 00 005 Y1 A# t' P5 J# e* x+ U- ^
如果要把Quit/ G4 j7 ]9 F' @. S$ o
51 75 69 74 00 00 00 00 Quit....Quit....
8 J+ c2 L) Y( d翻译成"退出吧" 就需要在加4字节/ p! s, n4 L7 D5 [2 W
地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX], @9 E0 p6 o4 Z9 X+ `
只要把ecx里的地址改成你的就OK了% H! X/ @' Y' F, v) W' v
# y( ]7 s: |2 Z b
继续找处理图片的地方4 z0 m: [+ {* M3 e
一路跟下来到了
* c O# X3 _# q' u( C# s 0 P5 N' Y- `+ ]. A
004F398C |. 83E6 F0 AND ESI,FFFFFFF02 q' a k, w2 _
004F398F |> 56 PUSH ESI ; /HeapSize = 5
- e' E6 |& n1 p& ?! P9 m" X) F' p004F3990 |. 6A 00 PUSH 0 ; |Flags = 0
v! v; V. j1 d; Q004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000
% y% I' T" o' F9 u9 O& r004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc
' L2 ~0 \ q/ t004F399E |> 5E POP ESI
4 D% ]+ E9 @$ b3 R6 u! p- U' a, e! I3 ^9 `5 f; ^- _ j( I
发现HeapAlloc 这个是用来分配内存的" H/ W; \; }5 a3 T @
F8步过 取eax的地址 就是将写入新的quit的地方
4 G. P) [& R& _1 u6 w对其下硬件访问 F9运行, `, _; I5 i4 n Y2 G
断在了4038119 ?9 S) E% N9 S5 Q$ W) J2 T
在F9几次 断在了4091c4; g1 r- l! m3 @ D5 M
在F9几次 回到了4038113 E. t! {" [. g
' b3 J9 M$ i% _0 v1 _: c* p- W( g
确定就是这2处地方了
, i8 }+ W S* q" i2 Q! w4 L这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用 g1 K- q# B1 T* q
" k; n' |) A8 P9 f5 w
004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]
7 y2 j# l9 I9 b8 S8 {. A; H( t004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q: E1 ^) s/ y6 [' h8 H
004091C4 |. |85C0 |TEST EAX,EAX
0 x0 H4 Y9 {; J+ F# F. |004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出
" @# ]9 |" P( e* w& Y004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
" S$ W5 _: ~. v7 k: X* l004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q+ b9 s* z, J7 O0 Y8 x0 u
004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格
2 m9 c; L- P% _/ R0 c2 p' O1 }004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8
, D: C3 l* l9 C( @( P004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]3 X3 |8 }& j4 }: w9 p6 D
004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX
+ L, W8 ?% }7 B/ ?004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 00000000
2 ~7 D0 R) y- \$ A/ e004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |% Y; C l! H; Y. @' G
004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B04 D0 b* l; M# j# \4 ~0 e9 }
004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]+ y! F) x" q% n4 H2 X W
004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX
( ^3 T( q) i) [! v$ W- M$ Z% d004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]& r7 g$ j$ p; E( P3 G. |1 J
004091F0 |. |83C1 01 |ADD ECX,1
* y3 ?8 g# j9 ^, Q3 {0 g- G004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX" X! y5 z" W% U; J" }5 ^4 C
004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE$ f- w- N3 Q2 |% E/ ^: q' r
004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里% R$ {$ a7 R$ ]! q
004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)
4 }1 j9 z8 \: F- r' G1 Z* P004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去
+ {) l! I- n5 ~00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]' W3 @; h* E3 A7 I0 ?: l
00409204 |. |51 |PUSH ECX ; /Arg27 l6 N. Q) S9 e3 G8 v- i+ ~8 h
00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |( D4 q1 K! V* E, j: N/ p0 K. H
00409208 |. |52 |PUSH EDX ; |Arg1 压入Q
( R; B$ Z/ N O% m- x00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |
6 v J5 H7 \9 v @7 E: p0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键
% g( b Y4 \. `00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8], [! [' d8 A* R$ Q2 s8 Y
00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u4 g( F6 A) F2 F
00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX; f/ p, S; z, d+ d) H
0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]1 `: Y1 s) o% l2 h1 ?) Z# p
0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度
4 l) F* I1 W3 H+ B6 c00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
, {+ X5 |+ z" x- r00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],08 Q! P9 G, J! u+ s& \
00409229 |. |74 7E |JE SHORT Unpacked.004092A9% A! ^: a" v; [* [
6 g9 ~1 ~2 D: Q& n- E/ O. s3 C( z
0 R& c/ P2 {- K, k8 t
2 L4 H: H1 {+ L0 w6 ^5 q S
& i# ~; f- K9 J' n; F. W7 [* I# C; A8 R8 c
& L/ m0 N7 I6 L! A& R/ T
00404620 /$ 55 PUSH EBP
1 |) U2 F l% c/ Y/ i00404621 |. 8BEC MOV EBP,ESP
( o. ?% h7 G' }" ~7 S( n+ w0 F00404623 |. 83EC 0C SUB ESP,0C
, o# v9 v) S/ L$ f }; A00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX
) o# R, |3 z2 Y00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]6 |! j/ I+ q+ V1 U
0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]; G. m8 d0 w5 V: w1 r( f! r" H
0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX8 X/ b) O4 V" J/ {
00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查
7 ~2 A; s& Q( j$ I$ `5 ^00404636 |. 7D 0D JGE SHORT Unpacked.00404645
9 g! b: F" \" ~0 u2 D0 D" o3 f9 t00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]
/ J; E" ?' {0 M7 F0 r7 |7 m$ c0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL! K, d2 L8 P5 _0 h! }# e
0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]0 N" ^6 X A1 @3 L$ X: @
00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX/ h, A, N1 C; ?2 D6 M
00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查( L+ ]% R# c1 c' a9 M7 i
0040464C |. 7D 18 JGE SHORT Unpacked.00404666* D7 ~6 J( H2 n
0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
) {( @$ u* \1 o. F* }- H# |6 V! X00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C] q, ^9 u: P6 Z5 {3 K# l5 q& Z3 P
00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]4 y) b8 z6 G, |2 E4 A6 P
00404658 |. 6BC0 14 IMUL EAX,EAX,14
) W$ Q. m) f/ N9 q3 x' D0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]( t8 h$ E& U6 a6 u4 C8 T
0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]
6 m7 i, @3 @1 K+ O6 L5 Y0 G$ Q00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
" }, n$ |; _5 v1 b9 A# g1 [00404664 |. EB 15 JMP SHORT Unpacked.0040467B9 I0 E; y3 G: h* E+ P( b2 d/ m. ~
00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表
# E: W$ v0 j3 x" _ }0 L, W: B* Y- ]
* |2 Q/ p8 D9 M* _0 H6 M* S7 _# h7 V- n6 Q. f
0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..- b# G+ S+ l2 T% o- N
0054AA58 1B 00 00 00 ...2 a: `4 l( A* s1 K- u5 Z7 Z- M2 h/ l
4 K$ M* S' Y4 L51就是字符Q
) H# e% M0 g3 {2D 02 00 00' }4 W. O. O0 i9 S- Z8 \, c6 I! Z
00 00 00 007 f2 e0 U4 X7 v+ w) F2 m2 I* U
3C 02 00 001 r& z8 G) J j8 `( X' p" E
1B 00 00 00! B! e# a' Q1 f3 n) V
O- X+ R) r5 ^ _+ m2 Z' I
A━━┓
* b0 ]# G$ h) G& T, z6 n ┃ Q ┃ - m5 C; |4 l, I2 \/ x5 S
┗━━B
l! \0 h9 Q5 ^) U1 ~+ ~8 W0 P. ]" ~* {$ m; S6 m+ u" p/ i
A的坐标 (22D,0)) m8 G; Y5 y# r0 J, F, y
B的坐标 (23C,1B)6 ^" l6 c, o$ q
& h8 r) W9 g( i' A( K/ ~
t, r$ L5 q/ H; M
3 `( w/ q9 G/ o- r! i' |% k5 h00404669 |. 81E2 FF7F0000 AND EDX,7FFF
$ E$ d' ~7 k4 s S& C0040466F |. 6BD2 14 IMUL EDX,EDX,14
/ u: O2 T# e, y$ E3 s- R! v9 v% z00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]( `6 j0 G% n+ b3 P
00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]
" K6 m9 d# q) \" e00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX8 Z" b8 N' m; n
0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]; a) ]' Q# H/ D0 s7 e
0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]- h n, O7 a8 l0 \3 M& m8 x
00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]
2 t" M& D e8 J( |2 K00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D
. r: d6 W% W# C# ]$ R) T- R& a, l- ]00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
: c- f1 f9 E4 ?00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]) c* o* E1 [* s0 H' b
0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]5 y4 ?8 a! m" Y% c, R
0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C
# Y) f% a* d# g# p6 j& m00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
' Y2 n. R+ ^8 i9 N1 w" ~- k00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
; u; V0 j: O3 I' M( V2 y00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]
) w, O0 k$ s( L+ T6 t0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0
2 x# n2 m# W% @0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
) E. A$ r5 {5 c4 [" @004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
7 w1 i; |2 k- C004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]
9 r: t% h" J( A' d- B! O- }004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B
! G% M2 ?0 d; \5 G004046AA |. 8BE5 MOV ESP,EBP
- Y) [& x* M/ }) G/ w. ^004046AC |. 5D POP EBP
4 B( @# {- r* ]" u1 l" Z" @; O004046AD \. C2 0800 RETN 8
4 N4 u9 l# T! m. y& B. B+ }, O8 g( G9 m9 h5 G
) m6 i8 n& K) B' t& K" W* x4 V# W1 ~( M) k( \/ r
$ k) H; B; l- l% ~/ A# F$ _1 ^; X! l% r
知道这些后 就可以改造了+ q$ N: m6 ]' U+ v9 \
先改字库5 ]; x e1 E7 w# q
3 L/ M% a. [" O3 O* V& B, G
y9 o" b. H; I' T$ @, G: k. y然后改成支持汉字的# s& k- B4 y- l% l& T( n
把4091d2 的 CMP EDX,0A 改成jmp
" h9 T/ q2 A5 W: I. n u" w跳到我的代码上去/ r+ N$ [" B5 P: i( W
2 M8 A: F" n. x& w q, n1 V
AND EDX,0FF 去除高位的FF
/ _' H' Q/ O8 c% ], H4 z+ KCMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的# `8 [! p! a# E1 S3 o2 G% I7 s
JL Unpacked.004091F8 小于 B0的就跳回去- w. x; K: z" P! L! k* z2 J9 O
MOV EDX,DWORD PTR SS:[EBP+8]# u/ W4 C% Q, M' }4 L0 X
MOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)1 a! \4 Z' R& C7 f+ x2 y
AND EAX,0FFFF 去除汉字高位的FF
( H7 K; ~) W1 c7 c/ _$ g0 w; LMOV DWORD PTR SS:[EBP-40],EAX 存入形参9 o, F8 F1 R& h
LEA ECX,DWORD PTR SS:[EBP-28]
' Y; P, ], k8 ]9 k0 NPUSH ECX ; /Arg2
6 [2 s9 k" b- z5 Q: o5 o p0 {3 KMOV EDX,DWORD PTR SS:[EBP-40] ; |" X$ K( g3 w! e) ~
PUSH EDX ; |Arg1
) {* r! \- w( H+ hMOV ECX,DWORD PTR SS:[EBP-8] ; |
- f' q% S7 x1 C9 L5 K+ Q9 dCALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标
1 ]# Q8 ~' c: `6 t( m* }MOV EAX,DWORD PTR SS:[EBP+8]
; W7 O/ n- X8 R3 [+ |ADD EAX,2 call出来后加2个字节3 @* h! ~+ T, d, E: H
JMP Unpacked.004092175 @3 m6 V% o" x6 u& l7 ^
k2 C @4 z6 W& }/ U! R, L2 z9 r* @4 H
1 p ^5 v1 o v% u/ N
$ }1 d o3 ?4 X. W3 Y1 R% kPUSH EBP, c* U3 [! C) @
MOV EBP,ESP
' q8 H; R8 P2 T$ W3 eSUB ESP,0C
' A' t) {7 W3 q* hMOV DWORD PTR SS:[EBP-C],ECX
! S" n; j+ K+ Y, p# NMOV EAX,DWORD PTR SS:[EBP-C]( W+ X9 |) {' C @9 Y
MOV ECX,DWORD PTR DS:[EAX+14]
; g/ e+ D2 k! ]6 l: l" OMOV DWORD PTR SS:[EBP-4],ECX# K ?; m) A; c
CMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)
' a* \' f m d8 @2 K# qJG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片
6 Y; i* w, Q v, F. kMOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)
8 q5 R% w: i7 G4 x2 p. DXOR EAX,EAX
, x* Q# R- M# ]7 u. _: \MOV AL,CL 把汉字第一个字节 放入eax
$ [9 X7 T. i- a$ X# K5 i% w& ^SHR CX,8 右移8位取第二个字节
9 [6 ?9 F6 \5 G- S& mXOR EDX,EDX2 W7 [. V' i; P, Y. |5 c* U7 ?& o
MOV DL,CL 把第2个字节放到edx去
) S+ v8 h% G' G# ^SUB AL,0B0 这里是我自己写的算法 不用再读取码表了
# e+ X% ]" c# j; h% q4 BSUB DL,0A1
6 T' D# n: u0 J" t1 nIMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16
, G+ Z: x& ^# E, j: l. QIMUL EDX,EDX,10& l3 \2 N$ S( A2 Q+ k
ADD EAX,20 加上我图片原来的高度- Z! i: J: w+ a) a- A1 P
MOV ECX,DWORD PTR SS:[EBP+C]* B) I5 @4 f) }
MOV DWORD PTR DS:[ECX],EDX! n# Y3 A$ V# p' f
MOV DWORD PTR DS:[ECX+4],EAX
( L7 p J& E6 T8 cADD EAX,10' E& Y$ F: X9 ~4 I
ADD EDX,107 J7 B, k5 X3 `. _7 L. g+ |2 d
MOV DWORD PTR DS:[ECX+8],EDX5 w$ s+ p+ H2 w }# o0 y" h1 h+ L
MOV DWORD PTR DS:[ECX+C],EAX
! i0 P ]5 x& X aMOV ESP,EBP
# g( Z M6 m' K3 Z- o2 O( E& SPOP EBP& O: o7 [( X* ^& N% ~5 b" [
RETN 8! @4 {0 E* Y1 B: ~
5 }" \ ?4 A8 d( @) K+ ?% Q
, b8 e3 j+ W7 E6 o% @ |