上次在这翻资料的时候正好看到某人写的虚拟村庄3的汉化教程 手头上正好也有这游戏 所以决定小试下汉化
! Z/ _. D" e4 a( }5 n: Y, R8 B) L
在汉化前我们需要分析下游戏是如何显示字符串的 是贴图还是点阵
* b: M* n* W- s' ~" o, ?这里是用bitblt贴上去的 ; _% n F' M# U4 M% E) X% u( u
知道是贴图就要寻找相应的字库和字符串
f$ G w+ S6 X# v字库在image文件夹就能找到 % ~. s) D- z+ O( b/ X
字符串被压在了EXE里(脱壳后exe增大了3MB)
4 H- D7 e: @8 N/ V* P- F" W! k2 n9 g. [& ]) K) ?& H2 w
下面就是分析游戏是如何显示字符串的
- J" g. T7 ?8 q! F: p" p用OD载入脱壳后的EXE文件 找到某个字符串下硬件访问 我这里断的是Quit* T" F, `/ {* l: `& {9 e1 }
3 |4 l: e$ u1 B
按F9启动游戏: K: F& H: {* r& e% v
然后断在4F24D2处
. y5 N" N3 N2 c. e {6 F6 W( `" f这里是读取字符串用的
# z* d3 E& K J5 ^, ? n
* E( z- L7 z6 U" z分析下& j$ U8 R5 ?* O' G) I/ f8 S
004F24D0 |> 8B01 /MOV EAX,DWORD PTR DS:[ECX] 这里是读取4字节 quit
: y o% |$ U8 b004F24D2 |. |BA FFFEFE7E |MOV EDX,7EFEFEFF 这里一块主要分析字符串是否已经到\0) [9 x' O6 ~/ t+ U" g
004F24D7 |. |03D0 |ADD EDX,EAX
* a+ P/ G* e6 T004F24D9 |. |83F0 FF |XOR EAX,FFFFFFFF5 p. |( P2 [* O6 V7 L) y
004F24DC |. |33C2 |XOR EAX,EDX$ T8 s1 T5 E% M5 K& ?
004F24DE |. |83C1 04 |ADD ECX,4 读取后面4个字节/ n. ^) z' O1 ~# o6 } z
004F24E1 |. |A9 00010181 |TEST EAX,81010100 - d/ Z9 l: M( E: D* B3 H
004F24E6 |.^ 74 E8 |JE SHORT Unpacked.004F24D0 如果是4个00(也就是81010100)就跳出
! c/ b' n+ w+ `9 x; X
. Q8 `8 p1 n+ C! u7 _' B7 [ k8 H这里要注意一下- l$ z# B4 T' j$ r2 }; _1 P
因为是4字节一取 \0处一定要是00 00 00 00
8 p% A, `1 j- h, [4 C7 v如果要把Quit; q1 L( t5 x. K' B/ ~
51 75 69 74 00 00 00 00 Quit....Quit....
- m5 p9 E# N$ R6 U+ q& k" j+ ?翻译成"退出吧" 就需要在加4字节
/ e6 F" [% p/ l* f: N地方不够用咋办 幸好ecx是个指针 MOV EAX,DWORD PTR DS:[ECX]- s' Q g, Y6 S9 _ k
只要把ecx里的地址改成你的就OK了& T2 o. l* D3 b3 D3 \. Y
: V% ~; b3 x/ K. E继续找处理图片的地方
" }* U$ u, q. a0 |1 e3 h0 J4 F" s一路跟下来到了1 ]; C" `/ T. ?5 @7 g4 V
6 N W! Q M8 S7 B/ u6 W% U; r$ b. Y004F398C |. 83E6 F0 AND ESI,FFFFFFF0
5 f6 {, @. h2 [1 R004F398F |> 56 PUSH ESI ; /HeapSize = 5, P; |/ Y, W: j/ R; X3 F
004F3990 |. 6A 00 PUSH 0 ; |Flags = 0! ~+ f# {4 d0 n
004F3992 |. FF35 3C7B7B00 PUSH DWORD PTR DS:[7B7B3C] ; |hHeap = 00D60000
3 K5 u3 ]) K. O: h% w) N004F3998 |. FF15 14815100 CALL DWORD PTR DS:[<&kernel32.HeapAlloc>>; \HeapAlloc
3 l& V3 j' Q% n% T004F399E |> 5E POP ESI- x& V9 y7 g$ i
* T9 I- y, T, g5 F
发现HeapAlloc 这个是用来分配内存的! ?; v, y0 T/ E9 a4 M
F8步过 取eax的地址 就是将写入新的quit的地方 ^$ \- d/ |2 E5 t6 k# J* w% }
对其下硬件访问 F9运行+ B! R M1 ~* b
断在了403811
! }, h& V; o* W/ E在F9几次 断在了4091c4 ~8 y/ V) G, u. P! t8 M, N
在F9几次 回到了4038112 y8 m+ A9 a3 d, d: r7 Z% b
8 U' i( M+ j* i0 x
确定就是这2处地方了
1 b* N% G& v) V7 n% |( I这里我选择了4091c4 因为在跟踪403811后发现这里的数据没用, v# f! z! {( V0 l0 c% x
6 b x+ K- [0 Y8 i7 e
004091BE |> 8B55 08 /MOV EDX,DWORD PTR SS:[EBP+8]& d: ]# U1 T6 ?* N9 ~+ O7 m
004091C1 |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 取第一个字节Q
& ]$ u( r% M$ c) Q& m004091C4 |. |85C0 |TEST EAX,EAX 8 b7 ?) h4 P: H
004091C6 |. |0F84 E2000000 |JE Unpacked.004092AE 字符串到达\0就跳出' Y5 w1 h% V" |6 \
004091CC |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]* j: N( ?( V. r* b+ i; p3 N2 O
004091CF |. |0FBE11 |MOVSX EDX,BYTE PTR DS:[ECX] 取第一个字节Q" _1 W" P1 J' B9 E1 y! v
004091D2 |. |83FA 0A |CMP EDX,0A 判断是否是空格" P* d, p; f+ V+ e" P
004091D5 |. |75 21 |JNZ SHORT Unpacked.004091F8
3 w. F v- P1 A7 _6 p* m5 x004091D7 |. |8B45 E8 |MOV EAX,DWORD PTR SS:[EBP-18]8 v% a2 T3 S* U4 [
004091DA |. |8945 0C |MOV DWORD PTR SS:[EBP+C],EAX
/ {2 |# J, |7 |# s, p% d6 x) C004091DD |. |6A 00 |PUSH 0 ; /Arg1 = 00000000& W3 M# R0 M$ i1 }, p
004091DF |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |! K" V+ ]# y. o9 Z# c
004091E2 |. |E8 C9A7FFFF |CALL Unpacked.004039B0 ; \Unpacked.004039B0% ~7 @0 A7 h* [$ s/ [
004091E7 |. |0345 10 |ADD EAX,DWORD PTR SS:[EBP+10]: k8 i) D# t k" @. q* g, A J2 @
004091EA |. |8945 10 |MOV DWORD PTR SS:[EBP+10],EAX% |5 [0 O1 w2 U) o
004091ED |. |8B4D 08 |MOV ECX,DWORD PTR SS:[EBP+8]
& j$ x+ L9 x) ?9 w004091F0 |. |83C1 01 |ADD ECX,1
; ?. m8 ~, k) E! r+ r004091F3 |. |894D 08 |MOV DWORD PTR SS:[EBP+8],ECX
' A1 d. F v$ j004091F6 |.^ EB C6 |JMP SHORT Unpacked.004091BE' }4 n, m, @6 I! w
004091F8 |> |8B55 08 |MOV EDX,DWORD PTR SS:[EBP+8] 不是的空格跳到这里- i# V o- Q( s% z0 d& M0 k
004091FB |. |0FBE02 |MOVSX EAX,BYTE PTR DS:[EDX] 把Q给EAX (实现双字节 就要让他取WORD 这里需要改造)
. x) W" p' k7 p Q8 D$ L0 L( H004091FE |. |8945 C0 |MOV DWORD PTR SS:[EBP-40],EAX 把Q 放到参数中去- b% t- x( @& p6 z4 `, L" e, q5 ^
00409201 |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]
! K+ m6 g- `& N) ~& |00409204 |. |51 |PUSH ECX ; /Arg2
1 Z4 ^7 ?- ^6 x5 O/ o/ v00409205 |. |8B55 C0 |MOV EDX,DWORD PTR SS:[EBP-40] ; |
8 B- Z$ N* |, Y6 R3 S1 g5 j! [5 G) c00409208 |. |52 |PUSH EDX ; |Arg1 压入Q/ F. P! f1 g' C7 M
00409209 |. |8B4D F8 |MOV ECX,DWORD PTR SS:[EBP-8] ; |+ I; k B8 U7 v0 l: {
0040920C |. |E8 0FB4FFFF |CALL Unpacked.00404620 ; \Unpacked.00404620 这里是关键6 z7 C8 f/ h8 G$ Y+ m6 }
00409211 |. |8B45 08 |MOV EAX,DWORD PTR SS:[EBP+8]
' t1 _3 H. Q$ K00409214 |. |83C0 01 |ADD EAX,1 读完Q后读u q/ F6 U/ R7 q6 S1 K" T, d
00409217 |. |8945 08 |MOV DWORD PTR SS:[EBP+8],EAX Q( w5 y. L( r0 A5 \/ h& R. g6 \8 p
0040921A |. |8D4D D8 |LEA ECX,DWORD PTR SS:[EBP-28]; m: O& `5 @6 C7 u! C0 x' c. ^4 g
0040921D |. |E8 5E8BFFFF |CALL Unpacked.00401D80 这里存入了画Q需要的长度
* W8 b) F$ }1 T" w5 @00409222 |. |8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
4 e7 K7 `. ~* E00409225 |. |837D D4 00 |CMP DWORD PTR SS:[EBP-2C],0% u+ U# V/ l! t2 i8 j
00409229 |. |74 7E |JE SHORT Unpacked.004092A9
) L3 v( ~" A1 ^% h: @- R: E
f+ p: s2 O- a* o4 ~/ ?" t6 Q. ~% ~3 n* u; F1 n
; H- `8 H( \, Q5 ~5 B0 n
5 W4 j* o8 m9 H
! s0 Q. X+ q0 O! w
9 k& B2 a# Z$ R% y00404620 /$ 55 PUSH EBP z. }( n" R2 R, r7 d
00404621 |. 8BEC MOV EBP,ESP( ?: [ `: X8 S# o4 E+ j
00404623 |. 83EC 0C SUB ESP,0C' {6 L& w }! X$ X
00404626 |. 894D F4 MOV DWORD PTR SS:[EBP-C],ECX
0 D' e5 d5 s8 D3 E; K+ A0 v00404629 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
3 P( [: M. f: e6 ~4 @2 r1 Z& o0040462C |. 8B48 14 MOV ECX,DWORD PTR DS:[EAX+14]
. i3 `3 M0 g6 F5 A+ J: K0040462F |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX
2 T9 g. _2 G5 K00404632 |. 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 这里就是边界检查
% ^- a/ U5 N7 `, E0 D00404636 |. 7D 0D JGE SHORT Unpacked.00404645
) w+ j8 T/ G1 R* O$ o00404638 |. 8A55 08 MOV DL,BYTE PTR SS:[EBP+8]2 H4 E& x& S4 i. W& O* @
0040463B |. 8855 FB MOV BYTE PTR SS:[EBP-5],DL, F* P V N( G, Y
0040463E |. 0FB645 FB MOVZX EAX,BYTE PTR SS:[EBP-5]
2 ^% m/ v6 s7 m3 d8 P00404642 |. 8945 08 MOV DWORD PTR SS:[EBP+8],EAX
/ V6 b! F( Q1 L' ?& r) ^00404645 |> 817D 08 00010>CMP DWORD PTR SS:[EBP+8],100 边界检查, q" O; H, k. Q, b0 Q* Q5 b
0040464C |. 7D 18 JGE SHORT Unpacked.00404666
6 o |4 s4 s# n5 Q5 Z, u8 O$ ~0040464E |. 8B4D 08 MOV ECX,DWORD PTR SS:[EBP+8]/ {" Z9 R3 X7 a2 P- n
00404651 |. 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]# \* A; o0 _9 `$ v; i
00404654 |. 8B448A 18 MOV EAX,DWORD PTR DS:[EDX+ECX*4+18], |6 G7 h4 g! t) {( ?3 P# O o
00404658 |. 6BC0 14 IMUL EAX,EAX,14
* H. S" T) d n5 X5 N9 Y2 l3 R' Q0040465B |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C]
+ P) n0 o8 K) y) N4 F- i! \. J0 t0040465E |. 0341 14 ADD EAX,DWORD PTR DS:[ECX+14]
, a5 t+ @# M) I7 o6 U; y' _00404661 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
9 C4 I* I9 i1 Q8 x" t+ d00404664 |. EB 15 JMP SHORT Unpacked.0040467B/ w1 L% K3 W0 b# x7 f+ z, ]
00404666 |> 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8] 这里一块是算Q的码表
, W. s0 C5 a1 C* Z, w" Y" T' Z, Q5 y* [" \
5 \; y/ v( S" y. _. x
" k3 V r# y+ B: T$ ]
0054AA48 51 00 00 00 2D 02 00 00 00 00 00 00 3C 02 00 00 Q...-......<..- b1 [/ d7 v. o8 i6 Z1 b
0054AA58 1B 00 00 00 ...
; z/ [" M0 {0 Z+ E8 E4 F T0 U% Z, D! q* [
5 B& M' h k# l+ z# L, \! B51就是字符Q
# J* S: Q# l3 A( g! i* S) V: I2D 02 00 00
* q0 o. J9 l9 M6 a- l% _00 00 00 00# L- p7 i7 s! X/ q- T8 g
3C 02 00 00$ O3 {1 W3 P9 Q3 d1 t$ C }
1B 00 00 00
# }; H% j. E- Y) C. h4 D
0 f1 c1 C3 Y: t( t' J+ e, u2 ~ A━━┓
/ g, t& \& o% | C. y' R2 t; }3 E ┃ Q ┃
) m8 _$ A% j3 u( c- @8 o ┗━━B) ^- t2 a& d% p9 f/ R* S% `
, V7 Q3 G* } v) Z3 p7 k8 rA的坐标 (22D,0)
# v8 j* M# |& @" f, v2 p% W4 U, ]B的坐标 (23C,1B): u6 l$ Q* Y/ N& A4 f0 X2 d
+ D W2 g I5 F4 i# _) v% T" g) W U, x, O( H
0 t7 n; u1 Q4 I2 F% D
00404669 |. 81E2 FF7F0000 AND EDX,7FFF
- d3 a P l. C7 ]0040466F |. 6BD2 14 IMUL EDX,EDX,144 H9 N* @; s3 n
00404672 |. 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
& a% A& P3 i1 S# b) l6 d00404675 |. 0350 14 ADD EDX,DWORD PTR DS:[EAX+14]
# {+ ^2 d& L) s. H00404678 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX7 K, B; ]; A" D
0040467B |> 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
. }& \3 t( Q! R/ z/ a0040467E |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]( Y3 Y+ n- L/ ^' u
00404681 |. 8B42 04 MOV EAX,DWORD PTR DS:[EDX+4]
+ W2 i1 u3 R& `. j: Z00404684 |. 8901 MOV DWORD PTR DS:[ECX],EAX 存入22D. G8 ^* Q5 e G4 w& N8 [
00404686 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
/ g) q9 w# Y+ I) Y+ ~6 |# c- J00404689 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]3 W# M* F5 B. [" [" w( n
0040468C |. 8B42 0C MOV EAX,DWORD PTR DS:[EDX+C]/ Y# F: G, `& _+ ?
0040468F |. 8941 08 MOV DWORD PTR DS:[ECX+8],EAX 存入23C- ~- Z, j/ d) ?& b: h; |4 C
00404692 |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
) R3 v: L; ~9 [% M1 F9 c* I e, }' u00404695 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]% m+ h* \6 O2 K# |+ l$ q7 `( E
00404698 |. 8B42 08 MOV EAX,DWORD PTR DS:[EDX+8]
, u/ V7 u1 I- \+ ~ u7 |3 l0040469B |. 8941 04 MOV DWORD PTR DS:[ECX+4],EAX 存入0
4 B! x$ c1 m' I V- D, i0040469E |. 8B4D 0C MOV ECX,DWORD PTR SS:[EBP+C]
: F' O( H9 s% k6 H7 f004046A1 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
% Y g/ `, ~4 b4 j004046A4 |. 8B42 10 MOV EAX,DWORD PTR DS:[EDX+10]0 d+ \4 t- J5 c, l4 O
004046A7 |. 8941 0C MOV DWORD PTR DS:[ECX+C],EAX 存入1B' e" ^& ^, |# v9 L- Y
004046AA |. 8BE5 MOV ESP,EBP1 _7 h3 r( N8 j' e0 t
004046AC |. 5D POP EBP: \ x& U" k4 @: g# D
004046AD \. C2 0800 RETN 8
: K! @4 j# ? t' m4 S
% t: {& X( g- K# w/ J+ R! k2 z$ w! H
* I, _' Q7 C: b1 g% e
) S M" u/ W. c% ?# d8 r3 x3 [8 ]5 O- H0 B0 u' q" n
知道这些后 就可以改造了
4 `9 L$ \7 L- d: n- @! H! z先改字库
# l" ~$ G0 J6 E1 i6 @: T
; [7 f+ Q; ^1 a& g# j; B( v% i
9 h% ]4 Y h9 d9 i% G8 g( s然后改成支持汉字的4 Q$ i3 H3 {9 z5 ~0 h5 r d
把4091d2 的 CMP EDX,0A 改成jmp& p' \4 p9 R. Y4 L4 _" b/ C0 k
跳到我的代码上去! g' q( X i: ^9 U7 g+ x
: P3 ~' I- d8 A# `# B6 V7 mAND EDX,0FF 去除高位的FF
+ ~+ j3 ]" x6 c( fCMP EDX,0B0 比较是否汉字 跟比较80一样 因为我的字库是从 啊(B0 A1) 开始的
$ {, x" J, i7 a7 p7 FJL Unpacked.004091F8 小于 B0的就跳回去8 `, }7 T4 Q' j2 T
MOV EDX,DWORD PTR SS:[EBP+8]! \* B5 L% ~& R$ k/ c8 D$ H3 [
MOVSX EAX,WORD PTR DS:[EDX] 取2个字节 (支持汉字)
4 T+ c1 h4 t6 g4 t3 hAND EAX,0FFFF 去除汉字高位的FF. g2 T$ J: J6 E( y- [) W3 m4 O
MOV DWORD PTR SS:[EBP-40],EAX 存入形参' d6 h2 E; O- o- d. x
LEA ECX,DWORD PTR SS:[EBP-28]
+ v4 [9 h" S5 gPUSH ECX ; /Arg2
/ n. J7 k& _0 R8 UMOV EDX,DWORD PTR SS:[EBP-40] ; |5 E7 n% k9 `9 h* Q8 P
PUSH EDX ; |Arg1' |8 C- J3 A j" M6 n: x+ h
MOV ECX,DWORD PTR SS:[EBP-8] ; | E* Z' H$ ~8 X# X, h
CALL Unpacked.007CFF3C 这里是读取并存入 字符串X Y坐标
) P, f( {2 T+ {' y+ JMOV EAX,DWORD PTR SS:[EBP+8]% h/ d, b! E, z1 L6 y
ADD EAX,2 call出来后加2个字节! d" A" T6 M9 c% H* U5 r4 x
JMP Unpacked.004092171 a. p4 U/ e% F/ N- R# D
+ x, `: Q4 u' R% ~8 ?2 C; z/ w) K" t% x
- Q8 t# _/ v* L3 h4 q2 I8 B% ]7 s4 }2 ~# U9 L4 k, v9 B
PUSH EBP/ q( u A. N# U8 F6 G% g8 v
MOV EBP,ESP
4 _5 B: q5 W9 h, A1 V* N+ v9 RSUB ESP,0C
5 e1 a3 m5 I3 r4 XMOV DWORD PTR SS:[EBP-C],ECX
7 g& [) F. Y* u# _) YMOV EAX,DWORD PTR SS:[EBP-C]! s' R, U3 _& T' }; u% _
MOV ECX,DWORD PTR DS:[EAX+14]
1 v7 N- K8 K4 ]1 q- BMOV DWORD PTR SS:[EBP-4],ECX
& U# C C9 E9 W; f( ZCMP DWORD PTR SS:[EBP+8],0F9D7 边界检查 我这里最后个文字是 座(D7 F9)
4 U, o4 Y& w# S' ^- L" ?, w% N3 HJG SHORT Unpacked.007CFF8E 超出就绘制一个空白图片3 C/ U$ z" U% _; x9 a' q
MOV ECX,DWORD PTR SS:[EBP+8] 从形参读取汉字 把汉字 放入ecx (我这里代码写的乱)
" ]) D( p* }( `4 @XOR EAX,EAX0 D/ v- }5 ?6 S& @# H7 p. ^5 e
MOV AL,CL 把汉字第一个字节 放入eax' c5 Q2 R/ T, C/ M0 s7 {
SHR CX,8 右移8位取第二个字节/ x6 D9 u/ C( d0 p
XOR EDX,EDX l* D* B$ n* Y. {2 H0 ]* ]
MOV DL,CL 把第2个字节放到edx去
" e9 P: {* f: G& W/ {SUB AL,0B0 这里是我自己写的算法 不用再读取码表了
( F3 _% E! z; Y' P3 e5 _SUB DL,0A11 X \9 }0 m3 G+ f
IMUL EAX,EAX,10 我用的 宋体16 一个汉字是16X16, H" r& E+ t9 _- W* E- e4 A
IMUL EDX,EDX,10
+ w/ k; {* E( h% W# ?( DADD EAX,20 加上我图片原来的高度
w7 r4 m" G$ t* I; g. C$ c! v* uMOV ECX,DWORD PTR SS:[EBP+C] N( O, j- m! ?6 f; s* ?
MOV DWORD PTR DS:[ECX],EDX
; r9 u* b, |' c! d; |MOV DWORD PTR DS:[ECX+4],EAX3 L C" { ]! @0 i/ k# ]2 f
ADD EAX,10
2 F' v4 W9 T8 N- m* t" \ADD EDX,10
* f4 L+ W* T/ b9 {MOV DWORD PTR DS:[ECX+8],EDX
/ @4 O& Z) M! r3 e$ M& PMOV DWORD PTR DS:[ECX+C],EAX* h- E& O( K3 H; r1 \
MOV ESP,EBP& `+ ]6 U; C0 ^$ f7 z2 \2 Q
POP EBP5 n/ M2 s2 d: l
RETN 8
3 K8 F/ J+ f* ]! Q$ f# |9 i& G2 @6 K% E7 o
4 i2 R: i- S c1 w5 p7 Q. i |