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

【汉化资料】金山词霸”屏幕取词技术揭密

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

【汉化资料】金山词霸”屏幕取词技术揭密

跳转到指定楼层
楼主
发表于 2008-11-9 09:53 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

【汉化资料】金山词霸”屏幕取词技术揭密

对于一些调用Windows标准API来进行文本输出的AVG游戏,也许可以借鉴金山词霸之类的屏幕取词技术,HOOK API 之后,在自己的函数里把截获的英文替换为中文,然后输出到屏幕上来实现汉化,而且,这样的工具一旦制作完成,对于这类的AVG游戏将是通用的。
1 z  }0 z- o; V9 U) F  i+ E; s8 Y; _# G/ g3 s$ c& Q

: s9 X/ F9 s  N$ f( X8 r# q金山词霸”屏幕取词技术揭密(讨论稿) + `7 i  _5 Q0 \# i# o7 i: ^8 m
这篇文章最早是发在北极星论坛的一系列帖子,那时候闻怡洋(好像他也是MVP)也在那里混
- A& D1 \5 ?3 I" K$ L1 [原始的帖子我已经没有了,但不知道是谁帮我收集整理了下来(非常感谢),我用google找到了 4 A* D+ b. ]8 V) l) n3 w$ B
?
1 ?1 \# Z( T2 h, q4 z3 W这是我进金山之前写的,应该不算泄露公司技术秘密吧
( [  E+ i% d- y1 \, @8 T1 [& ]$ w5 q而且这些现在看来似乎已经有些过时了 # z: b5 w7 y0 K4 I( g
? ( [9 V6 |3 H" F5 m
那时讨论的只是Win31和Win9x下的取词实现 % H/ S# e* K0 b1 _% h( d9 M$ x
? 8 H+ b7 `2 i& Q
我到了金山之后不是负责取词模块,而是做UI,因为有个家伙比我更擅长做这种东西 % A& X1 U( l) t# B+ |* \/ G
他用SoftIce调试汇编代码非常熟练,做逆向工程方面有过人的天分。
4 m8 N2 Z. w+ e% N4 [?
- t. Q5 V9 i6 \; y4 H* @; _; y9 g! R5 W? % ^7 n3 I& w7 I% k
“亦东” 是我那时的笔名 , I3 y- \3 ]% d7 d# p+ h+ T
?
& p. `; {* _# d* L: M0 s7 F  L?
% `' |1 M" e; R" p2 S“金山词霸”屏幕取词技术揭密(讨论稿)
. Q: h4 t- E+ E# v- o5 i$ K? % V8 l2 T  @5 ]2 D& V6 e
主题  屏幕取词技术系列讲座(一) 3 Q' x& X4 ^% h) G9 k, S0 {
作者   亦东 0 ^4 _3 N; ^, ?9 X2 b& e
很多人对这个问题感兴趣。
1 X  [/ w5 p7 a原因是这项技术让人感觉很神奇,也很有商业价值。 . N" g8 E" Z1 m0 d9 d/ h6 N, v
现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。
! J# _$ W  x4 K! X但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。 6 U6 c& Y" m3 w: h* E* ?; O
大约每周一两次。想知道的人就常常来看看吧!
. V" I0 k$ E5 R' E' r) c一.基础知识
( l+ e) M5 X) R* Y# O: x$ w! n& K首先想编这种程序需要一些基础知识。 4 Y  a- j5 [# ]# x$ `  q1 c; q, ~
会用Vc++,包括16/32位。
8 v+ j) r1 I" e! S精通Windows API特别是GDI,KERNEL部分。
$ h4 `+ B7 R8 ~& I懂汇编语言,会用softice调试程序,因为这种程序最好用softice调试。
, A( _" A8 J2 b$ t9 L% _( C二.基本原理
1 `* Z. G* R( _在Window 3.x时代,windows系统提供的字符输出函数只有很少的几个。
$ g& r% g9 n( d9 ^, u8 A) _TextOut ! }2 `9 T/ O8 O( ]- F
ExtTextOut ; z5 }4 }, Y) b
DrawText
+ f; Y2 A8 U, S: n8 [/ {# E......
) V% J: H) F4 d# @其中DrawText最终是用ExtTextOut实现的。 ' ~- F" z+ _) W8 Q' [" M, R2 P
所以Windows的所有字符输出都是由调用TextOut和ExtTextOut实现的。因此,如果你可以修改这两个函数的入口,让程序先调用你自己的一个函数再调用系统的字符输出,你就可以得到Windows所有输出的字符了。
$ Y7 @1 p# J/ r: D3 g到了Windows95时代,原理基本没变,但是95比3.x要复杂。开始的时候,一些在windows3.x下编写的取词软件仍然可以是使用。但是后来出了个IE4,结果很多词典软件就因为不支持IE4而被淘汰了,但同时也给一些软件创造了机会,如金山词霸。其实IE4的问题并不复杂,只不过它的输出的是unicode字符,是用TextOutW和ExtTextOutW输出的。知道了这一点,只要也截取就可以了。不过实现方法复杂一点,以后会有详细讲解。现在又出了个IE5,结果词霸也不好用了,微软真是#^@#$%$*&^&#@#@.......... 7 Q2 e6 x! ?% Y+ @  o
我研究后找到了一种解决办法,但还有些问题,有时会取错,正在继续研究,希望大家共同探讨。 * U. a. z  D) b; R6 J
另外还有WindowsNT,原理也是一样,只是实现方法和95下完全不同。
4 d$ N1 K; ?! _' r6 E$ s三.技术要点
$ G  c4 n8 a8 o; e' v) X$ i要实现取词,主要要解决以下技术问题。
7 t5 O+ Y" g! m) C8 {1.截取API入口,获得API的参数。 9 k* y- Z5 O  G4 C7 Y+ Z
2.安全地潜入Windows内部,良好地兼容Windows的各个版本
) r' i2 M. m1 N2 W- I3.计算鼠标所在的单词和字母。
3 O  T% K, b! J4.如果你在Window95下,做32位程序,还涉及Windows32/16混合编程的技术。
7 Q2 x% c" Q/ Q1 S  L今天先到这里吧!最好准备一份softice for 95/98和金山词霸,让我们先来分析一下别人是怎么做的。 ' N: d7 _1 A$ ^. Y- u
欢迎与我联系   o/ f) U) J+ l7 \, p
E-Mail:[email protected]
. O  x! o3 @' ~6 t2 d( k主题  屏幕取词技术系列讲座(二) 6 f8 `* n: B4 x2 s
作者   亦东 8 O: q  R1 {# k! V9 R0 K3 }' w0 L
很抱歉让大家久等了! : B, |5 P8 f3 r
我看了一些人的回帖,发现很多人对取词的原理还是不太清楚。
: m  e$ k7 o  j9 M: x7 e+ D首先我来解释一下hook问题。词霸中的确用到了hook,而且他用了两种hook其中一种是Windows标准hook,通过SetWindowHook安装一个回调函数,它安装了一个鼠标hook,是为了可以及时响应鼠标的消息用的和取词没太大关系。
" B9 c7 ~' t2 K  r* r* H0 a另一种钩子是API钩子,这才是取词的核心技术所在。他在TextOut等函数的开头写了一个jmp语句,跳转到自己的代码里。
) w# k' T# n  J# K3 w+ ^! ^你用softice看不到这个跳转语句是因为它只在取词的一瞬间才存在,平时是没有的。 - K4 T' ?" `" u6 A8 p4 O: n" ?
你可以在TextOut开头设一个读写断点
, e: K# e% Y1 j/ c, o7 g) qbpm textout
) C/ u7 O' H9 m8 a0 t再取词,就会找到词霸用来写钩子的代码了。 3 A( u. v; G3 ~1 B6 v( P  K
/**********************************
; f1 ?4 Z6 n' k; [8 h4 F. H" h所以我在次强调,想学这种技术一定要懂汇编语言和熟练使用softice.
- {4 E5 \0 D- C; V. \/ t**********************************/
  o" T- _  i- f0 K, W- t至于从cjktl95中dump出来的未公开函数是和Windows32/16混合编程有关的,以后我会提到他们。 / |8 Q+ }) W4 I( {8 }
我先来讲述取词的过程,
2 C' S3 q; I+ X# b0 判断鼠标是否在一个地方停留了一段时间 ! j2 p; G4 R' H9 H$ ^! d
1 取得鼠标当前位置 9 j" b- g3 F! J# ^6 O# V1 O
2 以鼠标位置为中心生成一个矩形
4 B; T( A) {- `9 B0 W' }3 @3 挂上API钩子 ' k, x' @( A( }+ Y
4 让这个矩形产生重画消息
8 `6 ]5 t" p6 |: |5 在钩子里等输出字符 * t6 Y& P1 M3 ]" N, s3 ^( f
6 计算鼠标在哪个单词上面,把这个单词保存下来
0 p4 J. ?' n. v% I( {7 如果得到单词则摘掉API钩子,在一段时间后,无论是否得到单词都摘掉API钩子 / N" C" ]. W9 i) y# J+ ]; g- q# `
8 用单词查词库,显示解释框。 5 F. c* U" W! Q6 I6 b
很多步骤实现起来都有一些难度,所以在中国可以做一个完善的取词词典的人屈指可数。
5 {! i: X, j' f6 a" v其中0,1,2,7,8比较简单就不提了。
" a  W  J- z( O5 A# C先说如何挂钩子: ( V5 x' |- g3 o1 X2 B% j
所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句,跳转到自己的代码里。
' ?" r; R' V, \2 a; L步骤如下:
6 I. M+ w6 t: {0 ?1.取得Windows API入口,用GetProcAddress实现
: e7 P9 q+ F0 f- N( ~2.保存API入口的前五个字节,因为JMP是0xEA,地址是4个字节 ! U" D( \+ G1 ]6 _# ~; n. E
3.写入跳转语句 + q# ?) v0 l# Y! x2 f: D. w
这步最复杂 ! z5 |: [4 R% a  p
Windows的代码段本来是不可以写的,但是Microsoft给自己留了个后门。
2 x1 `# P% n8 o& I5 B8 O有一个未公开函数是AllocCsToDsAlias, & Z' ~& L! T0 ~4 {" T1 D
UINT WINAPI ALLOCCSTODSALIAS(UINT); 7 X3 v  I) Y! P% e7 Z0 ^; _
你可以取到这个函数的入口,把API的代码段的选择符(要是不知道什么是选择符,就先去学学保护模式编程吧)传给他,他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。 3 Y" {% y: x. R% s8 T8 P( Q
这就是取词技术的最核心的东东,不止取词,连外挂中文平台全屏汉化都是使用的这种技术。现在知道为什么这么简单的几句话却很少知道了吧?因为太多的产品使用他,太多的公司靠他赚钱了。 $ p6 @! _$ u& P: R$ S5 U" u
这些公司和产品有:中文之星,四通利方,南极星,金山词霸,实达铭泰的东方快车,roboword,译典通,即时汉化专家等等等等。。。。还有至少20多家小公司。他们的具体实现虽然不同,但大致原理是相同的。 5 G6 i1 S& q0 h4 `4 s9 I1 ^
我这些都是随手写的,也没有提纲之类的东西,以后如果有机会我会整理一下,大家先凑合着看吧!xixi...
% N( B: i* S+ U3 Z? % \/ b% q3 M: Y; I" p; u
主题  关于屏幕取词的讨论(三)
6 R& `& s  h( O5 a" ]作者   亦东
" H/ K+ h, Y5 ?  p5 B0 L0 S1 Z& S# R/ v. E3 S8 U& J
让大家久等,很抱歉,前些时候工作忙硬盘又坏了,太不幸了。 0 {5 ^6 H* k0 F$ M( I: F1 s) h
这回来点真格的。 & _; ^- L; N& j: }: w, @
咱们以截取TextOut为例。 ' i( J* T1 i( ~5 M* V
下面是代码:
2 Y7 t. h& d) Y. p//截取TextOut
4 M  F5 C8 s" o/ Otypedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT); * T. d% I' ^; x
ALLOCCSTODSALIAS AllocCsToDsAlias;
, Y; M  Q( }8 u& uBYTE Newvalue[5];//保存新的入口代码
0 Y' S0 Z) f  M, t% DBYTE Oldvalue[5];//API原来的入口代码
, K: h3 s5 n6 u% m& Tunsigned char * Address=NULL;//可写的API入口地址 9 w; W- ^6 _. q) C( a2 T1 x& x
UINT DsSelector=NULL;//指向API入口的可写的选择符
- o/ T- L3 f& J* \  UWORD OffSetEntry=NULL;//API的偏移量 % X# P5 a$ H" Q# v2 E/ I
BOOL bHookAlready = FALSE; //是否挂钩子的标志
* N% i- S) b2 f" fBOOL InitHook()
2 Z, l0 H; f# R5 y$ S{ 6 J" O) Z* o  s+ n
HMODULE hKernel,hGdi;
4 n  H! V$ u' }: u! xhKernel = GetModuleHandle("Kernel");
5 p7 L' S; v; J1 `if(hKernel==NULL)
: R  \! G" }. u; m, N  n6 Dreturn FALSE; % L8 m& R3 ^: |2 X9 M% C
AllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//这是未公开的API所以要这样取地址 % F9 d6 s! }  W- \
if(AllocCsToDsAlias==NULL)
1 z! x& c/ h4 N4 k* Creturn FALSE; 3 Z' }7 t& \) _1 h. G8 k: B
hGdi = GetModuleHandle("Gdi");
' c% P& [$ B0 r; Y4 Uif(hmGdi==NULL)
/ o9 n, r1 p& Jreturn FALSE; 6 i" T' U$ B$ {
FARPROC Entry = GetProcAddress(hGdi,"TextOut"); / O& P: f' d! S3 V. U  }
if(Entry==NULL) 1 ?' \' \+ I& q3 {) y; L
return FALSE;
6 z2 Y2 ]% i7 U9 U2 m! u$ `OffSetEntry = (WORD)(FP_OFF(Entry));//取得API代码段的选择符 ' Y/ j! d+ I4 R- |3 X: g- L% t
DsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配一个等同的可写的选择符 # a! h# e# U% k3 g1 ~& P) W
Address = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址
, J1 I$ v8 C" E4 k7 @, aNewvalue[0]=0xEA;
' r4 L# ^1 R$ I+ h. O  s+ L*((DWORD*)(Newvalue+1)) = (DWORD)MyTextOut;
3 G6 M9 M; U% B% N1 OOldvalue[0]=Address[0]; 4 [$ b8 n: T, f! j
*((DWORD*)(Oldvalue+1)) = *((DWORD*)(Address+1));
- H  L9 }% x" R} ' \5 Q+ [5 R/ W% O
BOOL ClearHook()
: C& U& S! \0 e{
& k- l( Q+ ^' h/ [% O. {if(bHookAlready)
$ i4 C' R5 J( g2 {1 G$ `HookOff();
* p6 S0 `& j1 i/ r- YFreeSelector(DsSelector); & |3 G! F* n! B+ X9 q7 V
}
. D! U/ h, N4 I2 x1 n! bBOOL HookOn()
/ I% E. d4 j$ e/ Z* Z5 d{ 3 v$ z% @2 @; z! \' l
if(!bHookAlready){ ! u/ o5 H& F4 a1 ^
for(int i=0;i<5;i++){ : \8 N- o3 a6 U% Z$ r/ z3 _8 I* x
Address=Newvalue;
! l: n1 K: g/ r9 g9 L+ A}   N  G5 X5 l4 k! D* k
bHookAlready=TRUE;
' g5 y! A3 g0 ^" J0 {( w  d}
+ S! L* G+ u; {% J} , R: [. @; V0 v# A$ K0 A9 ]0 q6 r
BOOL HookOff()
5 P& [5 {' k) a" _! M6 w7 g{ ( u; Z* V, Z0 \/ r! T( x
if(bHookAlready){
% K! _& x. ]+ [( n& b" f% xfor(int i=0;i<5;i++){
$ k1 y$ [5 K# X0 XAddress=Oldvalue; # b/ y- Z; E0 ]
} $ ]$ J4 }, }6 v" D8 i
bHookAlready=FALSE;
9 X+ {, q; z1 `3 g} + z( e  r4 z. ]7 Q( }
}
2 [" h& Q# G7 A, E2 a7 i//钩子函数,一定要和API有相同的参数和声明 6 P2 ?6 o) t9 z) H, {# e
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString)
" z% R2 I$ v2 p4 \9 t' O$ b{ 6 Q7 ]( N, C) g) ?$ T5 W
BOOL ret; & ^1 _: Q1 L3 m4 U+ i
HookOff();   K% w5 H8 A' e2 k7 C
ret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//调原来的TextOut
# }  }" x# i2 D1 U& WHookOn(); 9 W2 S' x2 E1 j. u% s* M4 W
return ret;
3 N3 ]# l$ ~5 Q4 g3 X9 I% ~}
7 a/ W( _0 u4 I# H( i$ S5 `9 T+ W上面的代码是一个最简单的挂API钩子的例子,我要提醒大家的是,这段代码是我凭记忆写的,我以前的代码丢了,我没有编译测试过
: M' m( [7 X$ r5 `9 L因为我没有VC++1.52.所以代码可能会有错。
/ R6 |' b  o# v) \建议使用Borland c++,按16位编译。 3 S1 J% b7 D- M- M" I. W- `
如果用VC++1.52,则要改个选项 2 p1 r$ c+ z8 c! x9 j, Z. ]1 F% L
在VC++1.52的Option里,有个内存模式的设置,选大模式,和"DS!=SS DS Load on Function entry.",切记,否则会系统崩溃。
8 l$ E" a& B4 r' v有什么不明白的可以给我写信
7 X2 m4 i4 q: `  }; w[email protected] + O( {% t9 S9 Y9 P1 X! A% F: |% i
Wednesday, June 23, 2004 7:11 PM ' z+ R/ ]2 K  [+ K  b
% S9 F! A9 l8 J6 H' O! V+ S
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 很美好很美好 很差劲很差劲
回复

使用道具 举报

沙发
发表于 2008-11-9 09:57 | 只看该作者
[s:6] 高深莫测
回复 支持 反对

使用道具 举报

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

本版积分规则

冒险解谜游戏中文网 ChinaAVG

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

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

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

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