设为首页收藏本站官方微博

【汉化资料】从花瓣区位看破解CreateFontA区位

[复制链接]
查看: 4339|回复: 4
打印 上一主题 下一主题

【汉化资料】从花瓣区位看破解CreateFontA区位

跳转到指定楼层
楼主
发表于 2008-12-23 13:45 | 只看该作者 回帖奖励 |正序浏览 |阅读模式

【汉化资料】从花瓣区位看破解CreateFontA区位

首先,分析游戏的文字显示方式。玩游戏的时候最怕打开游戏看到一片乱码,现在汉化游戏却是最希望打开游戏看到一片乱码,因为一般在非日文环境下能正常显示日文的游戏都使用的字符集限定,为汉化增加了不少的难度…… . A/ b# R& @' z
一起采取Textout方式的年代,日文、繁体中文十个有九个是乱码,于是有了Apploc和NL,后来比尔大叔推出了CreateFontA函数……CreateFontA能够直接定义程序使用和操作系统不同的字符集,于是乱码没有了,而汉化者噩梦开始了,因为汉化后的中文内容在经过日文字符集的编译后变为日语状态下的乱码。
1 y( j1 {$ P0 d& `以下是比尔大叔的MSDN Library对CreateFontA给出的函数原形
" N+ e! {  w* S- p( J5 d. @/ m! x1 |3 F' E% }
HFONT CreateFont( # F- {3 k/ j7 Z& V
int nHeight, // height of font $ W- _' b+ X* s+ O1 F
int nWidth, // average character width
9 x- u  F' \& D! o: x3 X! }3 {3 n: eint nEscapement, // angle of escapement
* L; \% h: x) W8 M3 s1 _. s& }int nOrientation, // base-line orientation angle
% M8 S& C/ @" Rint fnWeight, // font weight 3 i! n" G- M0 v* e2 V0 q
DWORD fdwItalic, // italic attribute option
! H; x( `& L' gDWORD fdwUnderline, // underline attribute option
! p5 X+ G0 t* b! k) FDWORD fdwStrikeOut, // strikeout attribute option * k* r& L- x6 v  G: P; r( E2 i
DWORD fdwCharSet, // character set identifier
# O4 t' {  W) b8 K4 N5 SDWORD fdwOutputPrecision, // output precision & L& l. _4 ~. j
DWORD fdwClipPrecision, // clipping precision
5 ]1 Q( D: ?  c: F! ]DWORD fdwQuality, // output quality
' a8 y8 `9 F+ X( a& IDWORD fdwPitchAndFamily, // pitch and family
8 C7 O' V( V" O. d. D  ALPCTSTR lpszFace // typeface name
# G% Z9 O% n; z2 d, d* M  X( m$ W);
* Z# j) k/ d5 Y4 ?7 F
+ S# y3 R+ I7 n8 u9 Q. y/ L+ qHFONT CreateFontIndirect( ; I, o5 o/ [3 ~: C+ t' O
CONST LOGFONT* lplf // characteristics
! P7 C3 c9 A' @  v0 w);
0 u8 }5 q) C8 W, N7 x/ e其中 LOGFONT的声明如下: 4 ?# u; [0 R% I2 k0 @! t

9 L9 B; o  I7 ?( n; ?, P% G. htypedef struct tagLOGFONT {
% t$ Z, z0 r! r3 xLONG lfHeight;
! y4 I; [3 v9 pLONG lfWidth; 8 u+ N$ D6 f+ w8 C" A
LONG lfEscapement;
* \" I# v' h. P9 P3 |# W/ y( lLONG lfOrientation; ; P; d: V5 ~. H( L' y1 o+ @
LONG lfWeight; % h! A4 s+ B5 i( j; c1 `
BYTE lfItalic; ' [; S# Q: |2 g, c! r3 k/ e+ C
BYTE lfUnderline; ) G7 p9 f  \) {
BYTE lfStrikeOut; - i- B2 q; l2 M% Y
BYTE lfCharSet;
$ g3 D7 M: {8 @4 DBYTE lfOutPrecision;
' S: q- `% G& _/ PBYTE lfClipPrecision; 5 {; G! m* K) b6 `
BYTE lfQuality;
* J4 ]' v1 E0 W. O9 eBYTE lfPitchAndFamily; # r7 o  A! ~" {, Q( J# J" E8 ]3 A
TCHAR lfFaceName[LF_FACESIZE];
, r2 f2 N  e8 {5 U7 d& J2 j} LOGFONT, *PLOGFONT;
7 E3 @" O, N) C% m4 e" M. u: W==================分割线=================
$ d6 h/ F7 _# G. O要改变程序支持的字符集,就要改变程序调用上面两个函数时的fdwCharSet或lfCharSet的值
) {5 Y% H$ ]( l# a* B+ o$ H其中各字符集所对应的值如下:
  N4 W, |, C+ N( k7 [
& V' v; Q3 E$ T字符集 值(十进制) 6 y9 z& i2 W$ J# J+ p. b7 @
ANSI_CHARSET 0 $ P3 {8 A! @) |2 F5 \# p
DEFAULT_CHARSET 1 $ O# t5 Z6 |9 J$ A3 w
SYMBOL_CHARSET 2
8 h* S. B0 z, C. W4 @! b& QMAC_CHARSET 77 + v& G; L8 \0 O; L) C6 }  H
SHIFTJI_CHARSET 128 % C! c) z5 S9 O+ k, y
HANGEUL_CHARSET 129 ) v! J9 r$ o( X1 Y! |2 S
HANGUL_CHARSET 129 9 D5 F4 }9 @3 h7 o- r
JOHAB_CHARSET 130 1 x! y: e' b2 E/ u  x
GB2312_CHARSET 134
6 t6 J+ `+ u' {6 k6 n% P8 [CHINESEBIG5_CHARSET 136 - I6 m! e( Q* e5 s
GREEK_CHARSET 161 ( z  C! x" T& u/ m$ u: w* E% f8 \6 c
TURKISH_CHARSET 162 7 k; T" v+ y9 T9 [5 C; z
VIETNAMESE_CHARSET 163 " F1 P& ~1 }( B# y: G
HEBREW_CHARSET 177
* o1 u' _! Q( C( J$ gARABIC_CHARSET 178   |! j% S! y& J1 ~7 n& _" b
BALTIC_CHARSET 186
' y- ^2 Z6 ^0 N  [4 QRUSSIAN_CHARSET 204
7 ^& [( {0 Y8 j& c7 D" q, \" DTHAI_CHARSET 222
- E, m3 y" |( [& C/ _# V2 ZEASTEUROPE_CHARSET 238
8 a% O0 c! [  m9 B1 AOEM_CHARSET 255 9 R) L+ [. K5 E/ J. g  S
可以看到简体中文是86(Hex),日语是80(Hex),繁体中文是88(Hex) ) Q& h: R  Q$ M- T8 n$ a
我们要做的就是找到游戏中定义调用字符集的部位,将80改为86,这样就能让程序正确的显示中文。
3 }1 {5 U8 K' m, t  r5 M( _; m" i用pexplorer打开HANABIRA.exe,进入反编汇模式,查找Font字符串,我们发现调用CreateFontA函数的位置是唯一的: " q5 G( B! a) T% F! f- n2 q, w
于是在以下代码我们停下来 2 [* t5 k* v5 H% ~5 q
L0041E365: ) V1 L* I5 L3 s6 [* E
mov edi,[esp+14h] % I, K  ]$ Q) C. W( n' A
mov ebx,[esp+28h] 9 d0 ~0 `) s* L  A
mov ebp,[esp+24h] 8 a& y; `& `8 J3 o
mov eax,[esp+20h] 9 |9 H' h" z  u7 s
mov ecx,[esp+1Ch]
: G( Y0 d0 p# t! V& Ipush edi
" C! W3 n2 ?$ D* `8 }mov edx,[esp+30h] % j7 W8 T4 u  Y# O0 U, P7 _
push 00000031h ( J" f5 D' q0 a7 Q7 t2 Z5 c
push 00000002h * g6 @# ~; O) t/ a; u
push 00000000h
) J1 w' V$ V) [6 A$ Xpush 00000000h
9 h8 n  B9 v2 M2 \, v# b  s, m8 `push 00000080h & z9 O- `! V2 N& S
push ebx
- x0 z9 o2 k( v5 R6 @2 H- Mpush ebp
' v( \/ F( A* u( j2 Z6 a8 jpush eax 1 x* ]  q: t; G; e; G- _$ ?. T3 H
mov eax,[esp+3Ch] 4 r6 K: N. N$ P: j  J" W8 g' J
push ecx 5 v! S5 u; Z9 {6 M. |
push 00000000h : R: M5 m, ~# C2 B9 d6 I1 K7 L. Z
push edx % |/ H- Q6 U9 W. b, p% v
push 00000000h 9 b( t. K8 e0 d0 H. ~( q0 T
push eax
6 l! U  |' q) [* q6 J! X; k' r' O/ jcall [GDI32.dll!CreateFontA] 9 N5 f- R9 M; j, w. p' u* s
lea ecx,[esi+28h] # [' i. L' ?9 F9 N& m6 e
mov [esi+00000138h],eax 6 ?3 ^* _1 k2 J0 C) l' V7 u6 W* K
mov eax,edi 9 e1 ]8 P9 i  Z) @9 o7 a
sub ecx,edi : Q* ]$ T. ]2 Y0 ^4 U& Z
lea esp,[esp+00h]
6 V9 p  y8 S& g" M6 Y2 f) H% h注意这就是调用GDI32.dll中的CreateFontA函数了,我们在这个堆栈中寻找将80这个值传递给CreateFontA的部位。 ( @) h$ ^6 c4 s& n  a; N: v
push 00000080h 6 X  Q* j# W9 Z% n3 \6 l& \
就是这里将80值压入传递中
4 L/ N: _' r& Y$ Z8 RPE中标记了这段赋值的Hex数值,用UE打开文件,找到
+ R8 W; c- k5 W' h. T/ A5 E7 [68800005355 : X% i# A4 p) X. s
将其改为
  L1 y$ E7 C) o8 y1 k4 ]68860005355
! ]* Y4 ^0 ?7 t9 a保存之。   T, |; S9 U, P6 a( r! A- r  Y+ b
这样PE中看到这段压入就成了 $ F1 {( p6 A1 ^+ }/ u5 q+ |$ q
push 00000086h 0 G5 l- n& n7 m" ~- J& W
初战告破,运行游戏,你会看到日文全变成了乱码,说明程序已经在使用中文的字符集了 * u" f" ~3 B2 f9 z( g: p
修改游戏脚本,加入几个中文看看……
! R. ~. b- Y8 ]$ ]- O& |为什么我添加的中文全部是“□”? ; o! m  L, {; Q8 @
这就是需要解决的第二关卡,字符集边界检查。 * U8 B: f6 E2 U. ^
1 N. ~$ ~! f* X$ a0 ]
既然已经设定了字符集,为什么还要边界检查呢?这是为了防止当游戏文本中含有某些非法的字符串时产生缓存溢出。于是在字形传递到GDI32.dll描绘字体准备显示在屏幕之前对其进行检查,发现超出了设定的缓存大小就将其拦截下来,于是屏幕上就显示出一个“□”。我们知道,由于日文的字符比中文少得多,所以这个缓存也小的多,换言之就是边界太窄。
: F& d/ G% K0 o3 S  Y. H边界检查的例子:
/ m3 ?% H# w2 n9 p' I9 _9 Dcmp al,80 9 D/ o/ _$ |& H+ o+ R/ A# f
jbe xxxxxxxx ; \7 [! M( h  _% @: h$ L
cmp al,09F 1 W& k& h, T* b5 U; r: ?" u
jb xxxxxxxx
1 C0 `6 Z& o/ B. ]5 kcmp al,0E0
; J8 _  T! q: J3 I4 }jb xxxxxxxx
9 O$ v0 b; h+ K* Scmp al,0FC 8 b9 z" i, F% V
ja xxxxxxxx 9 s# i' K- ^9 v. h) {8 I

2 V% c+ l$ L- L2 q即看字符是否在80-9F(前两位)和e0-fc(后两位)之间
$ }# {! B( k2 m( {* z1 o1 ]=================================== 8 I$ d/ o8 Z. n
如何具体查找程序的字符集边界呢?常用的方法是下断,用OllyDbg载入游戏主程序运行,一步一步断下去,在出现一堆“□”的时候停住,然后转到ASM模式查看停在哪里。 . C! C$ Y7 c; Y  c, S
L0043BA00: 5 H, c9 x, ?# g+ |/ ^8 E# O- C" G
cmp al,80h 7 q' p' M* z6 @6 P$ B5 Y5 k
jc L0043BA08
6 C  J" R+ r# K) A  @3 c- g% ]* d, Scmp al,9Fh
9 a9 D% k6 o6 }; N0 Qjbe L0043BA10
- h8 u; E+ o- e2 j0 |. E" ?L0043BA08: : z* D% T/ o) d2 B
cmp al,E0h
, M7 ^0 W2 X& M1 v, C9 C/ Ljc L0043BA30
# R7 C8 b2 Z3 q" X6 u1 }cmp al,FFh
" g* y2 n+ T4 ^. ^ja L0043BA30
9 c1 d, p; K. B花瓣的上边界为80~FF,而下边界为E0~FF,好,开始动手
8 Z, ~& J. t. b% R! O1 t, _% @% q  E这里我们将上边界改为80~FF,而下边界范围足够宽广,就不用改了。 * z0 c* B: r" [. m# x! J
这里为什么使用80~FF而不改为20~FF呢?因为我们需要让游戏文本中原本的日语空格(8140)不显示为乱码,于是8140刚好在边界外,就不会被送去CreateFontA进行显示,就会显示为日语的缺字码,一个空白——而它刚好就是空白,在显示上两者没有任何区别。 : a  t7 l  q: `
如法炮制,打开EU修改之,保存测试,OK,正常显示了& w' f5 L% b5 `# X( E* Y8 |

- ]. o9 D$ p( d4 W$ U原文
+ `+ k& ?8 b7 R' }6 \http://blog.potatoneko.cn/
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

5#
 楼主| 发表于 2008-12-26 06:34 | 只看该作者
引用第3楼zhang336970于2008-12-26 01:07发表的 : ) y& ?! f" ?8 V6 R* p4 y
'' 我尝试制作一个整合版,就是在英文版的基础上加入日文版的exe,dll。结果发现无法进入游戏画面,看来还是要找到完整的日文正式版才行。''
1 \. \7 n  v5 `0 O1 m8 e
: ]3 `6 u# g% F0 s) j# _你没有试下用E版的改代码行不行?把E版的字符集和边界限制都改下,这样做后续问题会少。
2 {+ j& k. H) R( ~总感觉缝缝补补的会出问题。

* H/ P2 p6 ^7 M  J: x  u; i" B' K( Y: A
我也是这样想的。但是难度极大,寻找和修改都非常困难,E版的边界检查没有日文的边界检查那么明显。还要加代码修改双字节的问题。以我现在的功力,短期内估计不会有进展。
回复 支持 反对

使用道具 举报

地板
发表于 2008-12-26 01:07 | 只看该作者
'' 我尝试制作一个整合版,就是在英文版的基础上加入日文版的exe,dll。结果发现无法进入游戏画面,看来还是要找到完整的日文正式版才行。''
3 ?' V3 {, F: P; Q
; u- K/ M/ v& l% n9 p. P你没有试下用E版的改代码行不行?把E版的字符集和边界限制都改下,这样做后续问题会少。5 c) r2 ~/ ^+ I9 p
总感觉缝缝补补的会出问题。
回复 支持 反对

使用道具 举报

板凳
 楼主| 发表于 2008-12-24 19:59 | 只看该作者
引用第1楼zhang336970于2008-12-24 16:59发表的  :, q% O7 R4 W7 k! O
学习!!
8 I  o& \' A- M
2 h1 K: G8 w! |* c6 l" OSyberia2的汉化原理估计和这段代码类似吧。

1 B$ N: E# a/ G. g* J  p# _不错,的确从中受到不少启发。
回复 支持 反对

使用道具 举报

沙发
发表于 2008-12-24 16:59 | 只看该作者
学习!!) A/ c0 j, S9 @- @( q4 Z) |
. q( }, x0 P) m1 |( @+ c6 r* F
Syberia2的汉化原理估计和这段代码类似吧。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

冒险解谜游戏中文网 ChinaAVG

官方微博官方微信号小黑屋 微信玩家群  

(C) ChinaAVG 2004 - 2019 All Right Reserved. Powered by Discuz! X3.2
辽ICP备11008827号 | 桂公网安备 45010702000051号

冒险,与你同在。 冒险解谜游戏中文网ChinaAVG诞生于2004年9月9日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

快速回复 返回顶部 返回列表