上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化 % Y2 C8 b0 M9 O- H" r# `' r- V- o
% r4 q; O0 N3 ?! \5 ?4 ]* S2 N% r. B% b* D在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵 3 W+ f5 c4 g$ m! K e
这里是用bitblt贴上去的 : B+ a3 _: U3 X) N) [6 I+ V( M+ S
知道是贴图就要寻找相应的字库和字符串
' I6 z+ c! ^$ V1 _- N& `字库在image文件夹就能找到 $ d! e# c' {( |/ S: V
字符串被压在了EXE里(脱壳后exe增大了3MB)
" ?. u8 N2 Y$ N Z
- `- t' h. V* h, L6 P3 l下面就是分析游戏是如何显示字符串的 . l- l+ H/ f E, D8 O
用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit
7 [9 `& U' [9 v
/ M' O! i1 q- y& A3 `按F9启动游戏
2 T6 d; a' A. M1 N, ^然后断在4F24D2处
: q% S6 |' l5 N这里是读取字符串用的9 S4 p8 ]4 A) V8 J1 C
0 ?; `4 G. W2 ?( y1 m
分析下
! a7 n. Z( M# F' R, J( ]4 N% F- N004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit
1 o- r" I: H' t% ]# M) g# \004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0
6 U2 q( I1 M+ @% s004F24D7 |. |03D0 |ADD EDX,EAX: h. v9 b6 h- ?1 X! n
004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF7 l4 A6 v/ F% J. U* U ^
004F24DC |. |33C2 |XOR EAX,EDX" c; s# U" u7 _- f3 I' T0 X8 L
004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节
' C2 ?2 A/ v) X2 f3 B' W! `004F24E1 |. |A9 00010181 |TEST EAX,81010100
! {/ n7 @2 {: Y* G004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
" L( s* E0 G) N% T
6 i$ g2 |- T/ s. J* p% r这里要注意一下
1 Y1 Y& \5 k+ |+ z5 |/ l因为是4字节一取 \0处一定要是00 00 00 00
# K! N* d1 @1 W3 I ]& @ @+ Q如果要把Quit
" {7 Q- o |% h5 b51 75 69 74 00 00 00 00 Quit....Quit....5 t3 P- A/ ^; c2 A: E
翻译成"退出吧" 就需要在加4字节* ~0 k0 J; f. w7 H% r# x
地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]
1 @6 R* q* I1 x- I h; b1 [: J+ I只要把ecx里的地址改成你的就OK了; `( }+ a2 u, [& L" w
& N/ [% r. {. Q继续找处理图片的地方
1 J% g4 i; c: i* ~一路跟下来到了
" X& \* K( g! l" }
, Q+ K; l& i( U; G7 s, y9 O0 m004F398C |. 83E6 F0 AND ESI,FFFFFFF0; _8 k/ y% `$ }3 V. J& B: B. A
004F398F |> 56 PUSH ESI ; /HeapSize = 5
3 Z& f8 R/ t+ E4 l- P004F3990 |. 6A 00 PUSH 0 ; |Flags = 09 }1 _' j% B2 }# t3 Y' D
004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000
; J( u+ a! i+ U4 Q- G, Z! D5 W% a2 g7 ^004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc
$ {# x0 ~1 }( H- i: }/ z004F399E |> 5E POP ESI
/ y0 K* O: |( p/ I4 _9 h
/ _7 {5 S. ~0 f. s3 ?( \发现HeapAlloc 这个是用来分配内存的( f) S% n/ ?2 }8 l* I2 G: Q' T
F8步过 取eax的地址 就是将写入新的quit的地方
' _/ y% t/ O0 e2 N5 X对其下硬件访问 F9运行% s5 V3 e6 q/ }9 w* k; P; y0 s
断在了4038114 x6 [ B7 _ f1 Q5 J% T
在F9几次 断在了4091c4
5 a3 l$ p: A) {( t3 u2 R( S) D在F9几次 回到了403811$ U' H6 X7 d8 e' y4 v
) l% \9 E) W* I: P. `+ ~; A) ]/ I确定就是这2处地方了
9 o' i! U" O, t+ {8 p这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用
/ b' }* L3 P$ {8 b/ I, ^6 [
0 d" w: c/ S3 t5 O; k' H004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]: H# k3 k( ^3 n: _ Y- O
004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q
# C Y, v5 r8 |. M0 @6 A004091C4 |. |85C0 |TEST EAX,EAX
# L) D; r5 R' w% \; Q( i004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出
" K* `% Z& D& |; p& t7 Q: X+ T004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
+ P- J- S. z- C4 a7 ~004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q
2 |" F4 J$ B- F1 |; F004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格
5 o: f1 S/ S9 T8 ]004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8
0 Z' L3 w/ E9 n7 m! ~004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]
5 c8 ~8 d S. _" [1 z004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX" V% ^) ]4 G9 v) X" ]+ W1 O
004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 00000000/ F# r! s* ]/ T" S
004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |# Z, }7 H$ C, T4 k' t5 x$ {- @
004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0
( e& Q8 `2 [% J' N+ Y# G004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]
- w' t9 {0 _/ ?2 Y5 e. Y H' D004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX
1 D" k; V4 Q! O* h9 `! v+ e004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
% w, \0 u+ F8 _5 o; O7 ^004091F0 |. |83C1 01 |ADD ECX,1: |, G K0 p) x3 x8 x
004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX
# P* T) ~( w/ O004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE( \% t2 Z1 Z- D6 b0 a4 B7 {( @
004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里 ?; N) d" N& v! P9 T( R9 C
004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)$ L7 w1 g6 f( ?( l/ u1 X5 q8 m
004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去
5 i( }1 `4 F: U" l; z* F00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]4 i/ H5 i7 ?4 X8 n( A
00409204 |. |51 |PUSH ECX ; /Arg2
$ D- E) n7 q6 w00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |1 a' w% {4 }2 m8 [
00409208 |. |52 |PUSH EDX ; |Arg1 压入Q! w& n. z! U: d. E, Q
00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |* T: {, Q, y* J" O9 C& S( t
0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键
* k7 n, x; g; N3 a9 g6 j00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]
1 N% v: n! W- k* b00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u
+ O6 S, b" q: i/ r) b, J" |5 E00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX8 f, i: z3 n1 _/ j" R0 ?1 g; k. x
0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
, x8 Y7 ^/ _+ x/ n* B0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度% f1 ^8 Z" \0 L% i+ d& N F
00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
5 l8 B3 O6 t- N, Q4 R5 U. i1 M; b00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],01 G: l0 g1 t2 T$ t2 a* C
00409229 |. |74 7E |JE SHORT Unpacked.004092A9
$ ?) ^6 D4 G4 {! s/ J4 J9 S1 {5 P' z0 r/ P7 I
- J! z, h" x& f2 j: S
9 A+ [* B( @6 t4 o1 h
& |; u0 }4 v5 ]) n8 f6 a, |( ^
& Y3 p: u k+ a+ `) t0 B
- r7 b2 \. ?9 }3 I+ o00404620 /$ 55 PUSH EBP
' u+ b5 T- x' i: U a' u00404621 |. 8BEC MOV EBP,ESP- y. ]! J# t- E1 O' S2 o& v8 f
00404623 |. 83EC 0C SUB ESP,0C. v/ ?/ v6 W2 s
00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX
, H- V& Z1 z/ s00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
+ n3 ` t( d% s! q- b0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]
% O, T+ c/ x* [3 [0 k, s& ?; M& w7 c0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX
. E- q ~, g. J4 c2 G( m$ B# r00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查4 C+ p9 }9 W$ ]4 G$ A# _
00404636 |. 7D 0D JGE SHORT Unpacked.00404645' }- C- u2 |3 o7 [2 d4 h8 m! k6 D: \+ N
00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]8 ^/ e8 `3 E8 u9 ^8 d6 w9 H
0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL
9 O8 q8 [- b2 v( y0 ~- e0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]6 ?2 b; r& {1 B: D6 J4 h4 Q
00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX6 `1 h1 |3 Q6 F$ D2 W) g
00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查5 d) q) W$ c& V/ Y$ \6 c4 i
0040464C |. 7D 18 JGE SHORT Unpacked.004046663 {4 p7 {5 }6 G8 Q
0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]
) O( ?1 \1 L) m- k) l' O5 \. a00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C] K2 O/ S ?' M. G, u8 m" j
00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18]: c, F6 s4 {2 O8 `4 n! A
00404658 |. 6BC0 14 IMUL EAX,EAX,14
% K' c: P6 o% l4 ^0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
& ]) l( [/ u+ v; F: a0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]
$ T$ u/ ^/ U- m; f00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
" c- w3 C7 o2 Y' E ?% x9 l00404664 |. EB 15 JMP SHORT Unpacked.0040467B8 @3 M$ S) Z; {
00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表2 N! g- `/ \' C8 u1 F
2 ~3 Y$ A2 B; @' F# T- G. H3 d+ o
; X5 ~8 Y, I" x& ~2 [# a7 l4 R' m( l* T& }% v
0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..
( u3 ?) ]# `9 c3 @/ U0054AA58 1B 00 00 00 ...
& Y) ?5 I- [" `# V8 |0 E% Z+ g1 r4 t- x% e" w
51就是字符Q+ Z! P/ y6 C; Z: _1 s
2D 02 00 001 Z% @) y9 O3 w' g
00 00 00 00% c2 G9 L. }9 W! n, [" ?6 A: y4 K
3C 02 00 00# d1 f; F- F& e) x( k% m
1B 00 00 00; \# d5 j( P3 a$ Z8 \, L& Y1 }
7 ?! l3 o$ l- c A━━┓
" ^& ^3 i7 V! t( l' V9 K ┃ Q ┃ 4 h: k) E$ F. e! t6 |
┗━━B
8 E: Z' ]/ A# T- j! A+ m" U
9 ]* \7 D. ]3 y/ [A的坐标 (22D,0)
7 s7 j$ n8 C2 D& v5 `# t" i5 q! m) LB的坐标 (23C,1B)1 m3 N. Z8 u1 ^0 P1 w
0 r i7 v4 t! B! a {" t( @ h/ P
9 p; K# t; R* m/ b6 m
5 R g" }- X( [00404669 |. 81E2 FF7F0000 AND EDX,7FFF
* F/ w% ^( @6 D0 j$ {0040466F |. 6BD2 14 IMUL EDX,EDX,14
" a" j' |6 B; B5 p$ {6 x00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]6 |1 P( F- r" A/ F+ a5 b! u; a$ Q3 X
00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]
$ ]1 \7 j2 E# b+ G00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX9 x" Q: C# O- [7 w9 Z& ~2 \
0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]- u2 ~$ l8 {" r; B5 j
0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]. S+ w; y- G$ O3 [& P1 ?4 |, m" X
00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]9 m ^$ K, x# i1 v4 N) X5 g
00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D4 |0 W% ^4 Q7 Y
00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]' u% N7 k! |/ o8 L9 o* x; Q- @6 P
00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
- s- O8 @- T' U& ]0 p0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]
4 L" b) d6 ^, q+ W0 `# i; I0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C
! Y4 v) H0 @ F3 L00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]* T0 [1 C/ F) ?6 C) X4 B' y$ l
00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]6 A1 Z* y) t; ~: I/ J
00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]
1 j; k+ M3 r% L8 X' F, i0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0% P* L! m3 E$ I
0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
5 j& S) }! H3 x004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]. l+ \/ O7 q: V2 n
004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]( h) K( G1 ?5 R) v0 r
004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B
/ A: H; }. R9 \2 v" S, k. q2 s, l3 c004046AA |. 8BE5 MOV ESP,EBP
" v4 D" b I/ o2 q8 Z3 i9 j) {% A004046AC |. 5D POP EBP
4 e5 s$ p0 ~9 a. I004046AD \. C2 0800 RETN 8
+ |% S, b( v, Z" g8 r: N; ] X3 l" t8 Y* P/ @
, g2 d5 q4 {" p2 \( G# y7 B$ `: V* `' I) M
, q3 t+ K! C" C& Y2 W$ l! U
2 ^5 e# @- m- m' R# X' f) Q, H. ^知道这些后 就可以改造了6 {+ P, w( l$ j7 f
先改字库; r$ S* B% w# [/ v- [1 J3 a
$ T" M' o9 B6 j8 b v/ w
8 L3 |0 W- w2 m( N& i
然后改成支持汉字的
* ]* y2 V6 ~+ |4 N6 J把4091d2 的 CMP EDX,0A 改成jmp
: _ s Q9 l, i7 `. g跳到我的代码上去- x# u4 r* z' B3 O! T/ S7 E
% `; h+ `* C9 g7 c8 G' b4 f! V
AND EDX,0FF 去除高位的FF
& h4 Q. D4 f0 ?2 w% MCMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的
; k; H2 _' i# W1 P2 r% \JL Unpacked.004091F8 小于 B0的就跳回去
2 }7 _- v! w& qMOV EDX,DWORD PTR SS:[EBP+8]9 G ?% D0 H8 N+ \
MOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
, O2 B! ~ Y' F. HAND EAX,0FFFF 去除汉字高位的FF
4 S& F1 q2 A3 A$ W( HMOV DWORD PTR SS:[EBP-40],EAX 存入形参+ S/ C- Y- P/ W0 g
LEA ECX,DWORD PTR SS:[EBP-28]9 E& L, x/ T! @ G e
PUSH ECX ; /Arg2+ m# d( F9 N/ K) e
MOV EDX,DWORD PTR SS:[EBP-40] ; | a& s Z/ H! F# A7 M- w
PUSH EDX ; |Arg1) ~! {$ h j9 m/ B: |
MOV ECX,DWORD PTR SS:[EBP-8] ; |
4 f Q! t( Z% @' R$ K! |CALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标& F3 H: z* } `% Q! g
MOV EAX,DWORD PTR SS:[EBP+8], B8 l9 n5 I2 X( G
ADD EAX,2 call出来后加2个字节
) b0 Y. J$ a; c6 l$ DJMP Unpacked.00409217
0 i/ V4 f" L+ j) E) Q1 Y
. F# T- S: w2 k7 u
3 b2 H4 ]. b3 o+ D, I `7 r: {5 ]" |( T3 j6 `5 t# x e# B. P
+ A2 ]' g& u# N( qPUSH EBP
/ h! J* J' Q; q0 Q8 ]MOV EBP,ESP. X/ k4 E+ j( R- X
SUB ESP,0C, y$ s4 m9 H. I1 g) J4 _
MOV DWORD PTR SS:[EBP-C],ECX
6 Z( L% s9 h6 w5 ]3 W. J" P" }MOV EAX,DWORD PTR SS:[EBP-C]9 C2 i& a) Z0 J) B5 `2 o0 b
MOV ECX,DWORD PTR DS:[EAX+14]
0 H% v1 p0 U \" o7 WMOV DWORD PTR SS:[EBP-4],ECX
+ s7 }+ \# _9 fCMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)- R. w% J+ U2 W$ j7 P# x' b
JG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片
7 K) L* u: j* O" \( YMOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)9 }" T. J, V9 @4 `
XOR EAX,EAX0 T5 L7 B5 H$ a9 d
MOV AL,CL 把汉字第一个字节 放入eax9 y; y2 ~ d3 H$ X. } B
SHR CX,8 右移8位取第二个字节
) ^9 G# q w8 b5 g1 C ]: EXOR EDX,EDX
% k, ~5 [* L1 IMOV DL,CL 把第2个字节放到edx去% c8 f% s) ?7 h6 U6 A- j0 @% ]
SUB AL,0B0 这里是我自己写的算法 不用再读取码表了 z: e/ N6 N5 D! r. @, `# |; z
SUB DL,0A14 q5 X+ d. V$ d8 {) D
IMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X160 ]8 Y% S9 D* C" K) I" c* p! V
IMUL EDX,EDX,10
3 K# H- H" R; P/ f& K% }3 ?+ qADD EAX,20 加上我图片原来的高度
& m" j2 D& H) N9 I7 U+ Z4 x. NMOV ECX,DWORD PTR SS:[EBP+C]8 B6 A: w9 E+ E1 C/ e
MOV DWORD PTR DS:[ECX],EDX/ Z4 \; p7 G! b1 K" ~! O; o0 {
MOV DWORD PTR DS:[ECX+4],EAX
" [1 C2 D$ J. Q c0 W" t; |ADD EAX,10/ Q! D4 N; H; c/ K
ADD EDX,10
- a, x T" g+ z+ y7 Z* l$ e* vMOV DWORD PTR DS:[ECX+8],EDX# q* L- s/ Q4 v8 v- A
MOV DWORD PTR DS:[ECX+C],EAX! Q# s) j4 q! Q; a! I5 g
MOV ESP,EBP1 Y5 \& w8 c$ x$ z% o
POP EBP, |; t+ k: N9 g# L6 _* n/ W
RETN 8' O5 g; w- c1 n7 |; I' S( ~. i
4 g2 O; z f7 l
: F" W+ F. d5 S6 w" X5 a3 r9 e3 a |