上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化
4 s. v5 I; |% E$ g
$ `. k3 ]% a1 M' S! a在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵
& I( K, d. w6 G! p这里是用bitblt贴上去的
! b% R3 f1 V3 h1 G" F& O知道是贴图就要寻找相应的字库和字符串
$ p; b/ H7 z$ L) Q字库在image文件夹就能找到 7 O8 L" S) K* G5 ?& T! O( C& P, \( v3 D9 b
字符串被压在了EXE里(脱壳后exe增大了3MB)
6 O1 \+ g, t; x( ? W
$ O3 ?! P" [5 x1 X( D: T" c下面就是分析游戏是如何显示字符串的
, R0 T+ i/ O( v. L: `用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit/ c" }; w7 U/ s3 ]9 H' {
# A# u4 D: S @2 x6 S) R按F9启动游戏1 ]* x; u1 |6 h; u! l
然后断在4F24D2处& T0 P8 z9 T8 R2 w7 i0 H1 e
这里是读取字符串用的
2 N5 K1 R; B2 H7 @$ q # m+ j. v1 F1 \- r! Y) }
分析下
/ }% S# M5 x6 A% [. ~004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit9 j+ c7 y% Z' w3 X% ?
004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\03 k5 b% @: Q: U
004F24D7 |. |03D0 |ADD EDX,EAX+ B0 R, D8 V! p$ ~6 o; Y: X+ J
004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF
& n( i4 ]7 c& l7 ~0 z P004F24DC |. |33C2 |XOR EAX,EDX
# V5 @' _' n; J/ U1 w# q5 V6 [004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节1 x4 O7 t% o" ~- {
004F24E1 |. |A9 00010181 |TEST EAX,81010100
/ w; Z7 E; K, [2 Y, n6 J4 a004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
- L9 e7 }2 j5 ~1 ?' `7 u; B1 b+ w8 y1 S& D! b0 L/ ^
这里要注意一下
/ x' P1 I* Y" b$ [/ [6 ?" z6 T因为是4字节一取 \0处一定要是00 00 00 00
/ P! I3 g5 }6 P3 z如果要把Quit
8 G4 @" D H$ I \: n51 75 69 74 00 00 00 00 Quit....Quit....
3 w5 U% b$ S. e0 c. d翻译成"退出吧" 就需要在加4字节7 C( P" J7 N' Y7 @' w
地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]
# {! K5 O/ |( |: v2 p7 W! }只要把ecx里的地址改成你的就OK了
7 v! e5 d+ U6 B, s! D# N; I7 Z4 ~. N( B% @; t3 q" R
继续找处理图片的地方
' _6 P, Z9 C. ^. y D* g6 L一路跟下来到了1 }# e' h& S" {" ^/ x
# N5 p/ Z$ V4 g
004F398C |. 83E6 F0 AND ESI,FFFFFFF0+ s, w* p _. y6 w9 U
004F398F |> 56 PUSH ESI ; /HeapSize = 5
+ ^9 M4 ?! h% K# L' I0 x2 f004F3990 |. 6A 00 PUSH 0 ; |Flags = 07 `; S# E8 m1 p" }8 c, B5 O; _
004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000
2 j# u7 o: u; F9 o k) w( l; T004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc
9 b/ X9 y) N. y0 g' b/ O5 i- s004F399E |> 5E POP ESI5 S- A" e6 f+ a) I
% t4 j( ?- i8 C* J
发现HeapAlloc 这个是用来分配内存的
' q. Q, N: m, PF8步过 取eax的地址 就是将写入新的quit的地方
) x; i# M9 B9 O9 |9 c( x% Z对其下硬件访问 F9运行
, I N% d9 \0 L; Z9 T- R$ w) O断在了403811( e; E7 n4 u; H9 u: x3 {8 Y
在F9几次 断在了4091c4
' a. D' ]( X0 u( @) q在F9几次 回到了403811
$ ]" a3 u( J8 [/ R# `, ], i s, P, A8 M& f, C1 P9 y
确定就是这2处地方了) W% v9 k4 R+ I8 E
这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用
8 T: S8 L: l, l6 _" a5 ]/ E! B. B5 w; c+ @
004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]
) X1 W5 p; p) U; m( q) D004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q
" y$ E i& w& c, g8 ]3 G004091C4 |. |85C0 |TEST EAX,EAX . K" n3 K4 R; v5 p
004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出
s8 c/ S* x; n. T004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
3 s- q5 Z$ Z# l4 o" ]004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q" R! z+ t& {) t& N% a% q/ G! S. q
004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格' ^. `: N' W/ A* e( [8 O+ ~
004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8
7 O0 e& S9 t' n0 A004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]
, l) R4 M2 V+ t7 d2 L3 P004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX
* z' d) A7 J: {004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 00000000
$ z6 |% @" z; k' G004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |7 \: M/ z b3 [$ Z
004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0
( P! u1 i( F! w* `4 A/ h* b/ m' P004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]# k- b4 f# X K& o
004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX
' Z( {9 f' u7 `: \; E& _004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]# s. U& [) b( r+ U
004091F0 |. |83C1 01 |ADD ECX,16 U) a0 E6 u3 T9 J, O, Q
004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX) T8 h, ?( u# h- p: Y
004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE
& ~ M+ n5 b$ n/ I8 y; q004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里
' A. W; ]+ z: Z3 L, @0 d Y0 j" K004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)+ l0 k, X6 o$ H
004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去6 M+ [* u7 H u$ Q
00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
) f' Y6 g& F( Z1 r+ J& B0 j! ~00409204 |. |51 |PUSH ECX ; /Arg2
& y9 w& F1 v+ G1 I9 P00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |5 d% K& n4 g% y" c- Z
00409208 |. |52 |PUSH EDX ; |Arg1 压入Q1 E( @) u) h \. z
00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |; j2 }8 B3 y9 N% t+ @: v1 R
0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键$ I, o+ e! w8 M, V8 l% n+ j7 ]' {
00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]
( T9 K" M& ~/ g00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u+ U1 u. d0 x" o; @+ Y" G* ^
00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX
( _; B- f% m0 \7 R5 _0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
( J5 b5 u3 v6 @7 b. ~/ D! F0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度
; ?; f- @+ p1 p# @6 w u* N: N00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
2 a0 U! a. e% ^0 _* Y) c00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],07 F+ ^, t8 m/ d4 w% K, i
00409229 |. |74 7E |JE SHORT Unpacked.004092A9* V& B7 m9 c0 {( k# N' j( `
1 D8 e0 r& A! d
. `" H9 e( u! ^/ b: N; c4 b# M% }5 S( }' o0 U
5 i* k: q' q( A) x. M! q) T q' ?! w
' G7 I" `3 Z& `, p, t/ k00404620 /$ 55 PUSH EBP/ d8 |( _+ Y( S( [* |
00404621 |. 8BEC MOV EBP,ESP( ]5 c y, ]/ ~" q- b. |
00404623 |. 83EC 0C SUB ESP,0C
4 \) d+ |2 w4 o: b5 ~# X7 v3 |* \00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX ; a! ~' M2 C9 T* B2 b L
00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]7 s. M/ u( H) o7 N! R
0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]& o+ F; ]% g3 p
0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX* q- {: k% I4 a, X2 y3 b7 m
00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查7 ~# { Q. t' }
00404636 |. 7D 0D JGE SHORT Unpacked.00404645/ J( r& a5 L1 W5 b1 [2 @6 F# n; B
00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]( h- x: q( X( @ S. G
0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL1 G3 u) ~7 s6 v' W3 }' J. s
0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]
$ s8 q! X& ]- Q, b00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX$ U; S' u6 R# B7 d4 ]6 I
00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查9 Z+ M. ^! a( k
0040464C |. 7D 18 JGE SHORT Unpacked.004046669 A% ]2 t+ B& O1 L
0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
/ u& ^: }- _: h: Z$ T4 K, a6 e/ r) I00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]+ N$ H3 p+ }. c' j- M6 O2 Y9 N; E
00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]+ L( H' ^4 {3 ^1 A5 S3 ^3 W
00404658 |. 6BC0 14 IMUL EAX,EAX,14( A# W% O4 Y1 q2 ~5 I
0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]$ q' B# O) `3 Q# _
0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]4 c' k/ Z w0 W9 V
00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
3 F2 i2 R' U* `4 u* V: G00404664 |. EB 15 JMP SHORT Unpacked.0040467B5 R7 u5 \/ a$ c% d
00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表3 P& z! w7 V ]) |0 w0 r
* ~( w5 [6 d2 |2 Z6 o
) Y" I. l* e9 f+ W
9 c9 s5 R5 w8 j4 X0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..$ j, c; C. E8 [& I
0054AA58 1B 00 00 00 ...2 M, Q# L. m+ T2 \
# V1 j Q* @& M2 ]" R51就是字符Q
. i4 A K( j& [( |6 C6 m: b2D 02 00 00+ n# P" q0 R3 J! ^8 V0 l5 c
00 00 00 00
3 N# B( y7 p+ v3C 02 00 00! ^! T- N# `& H0 x, R& s. ?: B1 n$ y7 u
1B 00 00 006 w% s' i3 d! m% d+ g: `( |% t" l
/ m9 H+ j1 @: B/ o7 ~8 z A━━┓
4 k) G( l, W* M. n: a z+ T ┃ Q ┃
1 F; X: _3 M% j ┗━━B, |& @, I# D3 k. ~ n$ _
F5 C1 Z# M. o* k% U
A的坐标 (22D,0)- Q! S/ {; b8 ?+ e9 w& y
B的坐标 (23C,1B)* `& {; T+ n2 a' @" x: j
2 Y# S2 h% R- E( w4 P0 z( ~; f# h0 i3 W. Q( K3 D
/ k! m* O- q& d+ |# m- C* z) T00404669 |. 81E2 FF7F0000 AND EDX,7FFF
+ P3 C7 }, i/ A5 n: }6 j& ]( a0040466F |. 6BD2 14 IMUL EDX,EDX,14, @9 @! Q2 x+ _# n) ~' a7 ]" T5 w
00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
. F, h6 _ V5 x! V. q3 n1 Z. l00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]
9 Y3 u& Q- @" O! z; b00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX
! @. q, ~2 U' u1 Z0 o# O k0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C], B6 l; t7 x* L: Q6 ^: a; x8 d
0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]' E& r! F/ s1 I. E. x% p+ w
00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]
$ h) m* |4 ~: p: N! Y3 O% F00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D
- q1 F$ e8 B7 l" A0 g. r, W) B00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
8 r! W& F, J* A- b00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]9 r) I7 U, U' X+ h1 _% Y& Z, r
0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]' ?1 [% e1 }: F/ y, p+ k: H
0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C1 d$ A% F; v# c5 ~; J5 P
00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
# F! k2 ^7 {# F7 B" y3 Y* g8 z' v00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]- h+ n5 k5 ^( |) c
00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]
, \1 g% y$ X1 L" Y1 X) K4 W) Q0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0
' ]1 Z% p) ~0 R$ \4 T0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
! l3 | W, i) H6 {004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
0 q& e# I0 b; H3 ^8 j' s* m! [; v004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]
& ^) n7 Y X) Y3 L, u7 d# G004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B
, }. G) C' c; Q8 B {" n004046AA |. 8BE5 MOV ESP,EBP
. [- X( @+ {6 K p004046AC |. 5D POP EBP9 \. W) |# r& d
004046AD \. C2 0800 RETN 8' O+ U5 ?( s0 _! `1 O# \
8 G! X) w6 g- Q( \- x
* _6 e' J2 O; M! G% m
. u! s% W% V( ~+ W b5 l5 F. Y) S, f6 V
0 Y/ j# S# A u% [- ]9 M' z
知道这些后 就可以改造了3 z$ C( Q; ^4 }! i K
先改字库. ~. B; u: e2 D8 P
& \. R' j O8 y9 s7 E7 R6 P3 \/ G
, M% `/ \1 G" G7 @5 _
然后改成支持汉字的3 o, B# ~: E4 X9 g: Q
把4091d2 的 CMP EDX,0A 改成jmp8 Y. V5 A6 q' @- J1 M
跳到我的代码上去5 i( P# M. s L) x- o% N
$ q( T7 w( ?3 A4 o( n: p0 [AND EDX,0FF 去除高位的FF
3 z7 z9 V2 p1 O% I( J$ |CMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的7 i5 w' i, j8 V" c; v
JL Unpacked.004091F8 小于 B0的就跳回去
; o i2 v/ B* M0 F5 uMOV EDX,DWORD PTR SS:[EBP+8]
y7 C4 y( x0 jMOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字) m) ^ v N- N; A) h: @
AND EAX,0FFFF 去除汉字高位的FF
5 _& l3 Q6 J( jMOV DWORD PTR SS:[EBP-40],EAX 存入形参! r+ c! o5 t% W9 t# F. s
LEA ECX,DWORD PTR SS:[EBP-28]' x& E& \# h& a7 c( g4 G( S
PUSH ECX ; /Arg2/ ]1 P' _. f K, {1 o: `% M
MOV EDX,DWORD PTR SS:[EBP-40] ; |
5 O2 R* C1 L2 B; i, {PUSH EDX ; |Arg1" z4 z% J) J* z8 P' M
MOV ECX,DWORD PTR SS:[EBP-8] ; |
) K/ }9 e) O! T8 `; TCALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标 @5 R/ O- P; L7 X& q- m+ y
MOV EAX,DWORD PTR SS:[EBP+8]
H0 P _2 B8 eADD EAX,2 call出来后加2个字节
3 e7 M& Q4 d- A, E. K6 G% E( o$ \JMP Unpacked.00409217
: \3 l7 e; G* n5 b
( d, f* w# E- c5 l! _
. R& s- b" [. \7 ~# @3 y' B/ J8 |+ ^/ ~" m3 H% D* L
+ _3 V6 W, M4 t& v: T
PUSH EBP! j% d+ e0 l: ~+ W
MOV EBP,ESP. V& |- s8 ^; r0 d4 D$ Y/ |* a
SUB ESP,0C! F1 ^) B. |' w* ]" K9 }0 S
MOV DWORD PTR SS:[EBP-C],ECX
9 L: S0 z0 E- Y* ^9 o- hMOV EAX,DWORD PTR SS:[EBP-C]7 K; ~; o: g' r5 V
MOV ECX,DWORD PTR DS:[EAX+14], q9 p$ j( G- x7 f* q
MOV DWORD PTR SS:[EBP-4],ECX
5 ^* {! H) z: C% UCMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)2 e% R' A$ a+ ~( |& |" _0 Z
JG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片$ n5 X" y3 {; R) P6 f; ?# p, C h
MOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱): Q) b1 J* U; [1 w2 W
XOR EAX,EAX- U( ^, F8 @0 M8 ?, h
MOV AL,CL 把汉字第一个字节 放入eax
& a( z* ?! y/ s$ ]) @2 M2 \) o% Q' ASHR CX,8 右移8位取第二个字节
8 l- T5 [& h1 eXOR EDX,EDX0 h3 E* c, F& F+ C0 p% Y* S' z
MOV DL,CL 把第2个字节放到edx去
; ]8 b2 ~9 }6 r( Q& z4 Z' y" C; ISUB AL,0B0 这里是我自己写的算法 不用再读取码表了
4 d8 j$ ?& `, SSUB DL,0A1
* E! t/ o! p v2 W o1 OIMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16
# }$ C. C* z" w. n! QIMUL EDX,EDX,10
7 g$ I1 X3 F! j3 B$ z% J% oADD EAX,20 加上我图片原来的高度+ [" j( W( ]/ G
MOV ECX,DWORD PTR SS:[EBP+C]
: h+ G$ Y; V: V( U+ mMOV DWORD PTR DS:[ECX],EDX
8 J8 ?. X7 e. k0 j) TMOV DWORD PTR DS:[ECX+4],EAX) e- z$ }4 k) t+ ^* n2 H4 a( @
ADD EAX,10
$ U1 A" n& V& H/ N& J3 qADD EDX,107 k- y% j2 |: b; @1 ^( S5 b
MOV DWORD PTR DS:[ECX+8],EDX
$ b$ f% ]% N5 ]! M) ~) _MOV DWORD PTR DS:[ECX+C],EAX/ E- J, ~# v& ? A$ O3 K& C8 Z/ g
MOV ESP,EBP4 W( a/ P6 ]: @- g
POP EBP
9 y9 n# `3 ]% q) [- HRETN 8
% Z7 I9 ]" Q/ A- }- f. `
( \( v! g/ W8 b i3 x" g5 h' N
9 g! r3 S& J8 A6 l6 z! m3 {) K |