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

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

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

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

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

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

对于一些调用Windows标准API来进行文本输出的AVG游戏,也许可以借鉴金山词霸之类的屏幕取词技术,HOOK API 之后,在自己的函数里把截获的英文替换为中文,然后输出到屏幕上来实现汉化,而且,这样的工具一旦制作完成,对于这类的AVG游戏将是通用的。
! w0 [  |( ^$ J* Q$ f
' Q9 I' \% G  |4 ]5 T/ H9 X, @& W8 K, p0 y. H* x# I
金山词霸”屏幕取词技术揭密(讨论稿)
- o& ^0 s' R6 \: h0 Q" c这篇文章最早是发在北极星论坛的一系列帖子,那时候闻怡洋(好像他也是MVP)也在那里混 % j8 z' [3 c8 p5 A0 \* C
原始的帖子我已经没有了,但不知道是谁帮我收集整理了下来(非常感谢),我用google找到了 6 J$ F+ U' S5 f% D
?
8 ~7 V; |& G8 X. E这是我进金山之前写的,应该不算泄露公司技术秘密吧 ! s2 n/ ?6 q" `, z( Q
而且这些现在看来似乎已经有些过时了 ( l* o3 _# ~- \& i9 h# E$ B
? , Y- t0 H. p9 ]+ B
那时讨论的只是Win31和Win9x下的取词实现
: a$ Z! Z9 z* E* S+ F. f?
3 I+ Y1 D' g& ~' ~我到了金山之后不是负责取词模块,而是做UI,因为有个家伙比我更擅长做这种东西
; J8 T5 [; \5 E2 T他用SoftIce调试汇编代码非常熟练,做逆向工程方面有过人的天分。 6 F! u7 D  c$ s" |' E
?
4 S- z, I2 z# ~: k  u, _7 v: d?
" u4 w2 R% s& @% P- l+ f“亦东” 是我那时的笔名
7 r8 v$ t- M- K* j1 n/ i?
. z: l$ g: a$ F9 }? 0 p4 L/ I9 R: o% m
“金山词霸”屏幕取词技术揭密(讨论稿)
4 S  z$ }9 o" E: G& o& j3 P2 j?
  T1 K! j9 W6 w; s( I主题  屏幕取词技术系列讲座(一) ; C- g( ]+ S% I! m" a/ S# z4 s: K1 t
作者   亦东
+ N( n( A# Z! o% E2 T( t! I, l很多人对这个问题感兴趣。 6 y/ [' H# r1 }2 F) C
原因是这项技术让人感觉很神奇,也很有商业价值。
, u+ T; X  F# n1 V% J5 _现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。 $ d5 ^2 K0 ]' a. R
但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。 9 r% i/ L  [) v7 D
大约每周一两次。想知道的人就常常来看看吧! ( k* M* ?, k5 c
一.基础知识
% P, ?7 B" e& i0 @4 u首先想编这种程序需要一些基础知识。 1 Q; Q  J. V1 L* f! x
会用Vc++,包括16/32位。
# |# _7 p! S: z* u3 S. T; x精通Windows API特别是GDI,KERNEL部分。
2 s- Y: ?" e; G6 V$ a" ?  T6 r6 P懂汇编语言,会用softice调试程序,因为这种程序最好用softice调试。 ' B- D: ~5 c4 _% e# f# J
二.基本原理
) D6 ~3 B8 o0 y" G5 Q在Window 3.x时代,windows系统提供的字符输出函数只有很少的几个。 * o! B5 u9 q. A
TextOut + c; z, U% r1 U0 k. n, |
ExtTextOut
( h4 d/ G+ \7 g- y' i4 R' E  PDrawText + e% \7 S) s" A$ r) S* Z" ?
......
' [9 C6 j9 F6 D2 x+ Y; Q9 l其中DrawText最终是用ExtTextOut实现的。
9 e1 ?; ~' w! n9 e: M$ i8 c所以Windows的所有字符输出都是由调用TextOut和ExtTextOut实现的。因此,如果你可以修改这两个函数的入口,让程序先调用你自己的一个函数再调用系统的字符输出,你就可以得到Windows所有输出的字符了。
$ ?& z* m7 U2 t5 `* D到了Windows95时代,原理基本没变,但是95比3.x要复杂。开始的时候,一些在windows3.x下编写的取词软件仍然可以是使用。但是后来出了个IE4,结果很多词典软件就因为不支持IE4而被淘汰了,但同时也给一些软件创造了机会,如金山词霸。其实IE4的问题并不复杂,只不过它的输出的是unicode字符,是用TextOutW和ExtTextOutW输出的。知道了这一点,只要也截取就可以了。不过实现方法复杂一点,以后会有详细讲解。现在又出了个IE5,结果词霸也不好用了,微软真是#^@#$%$*&^&#@#@..........
+ _& P9 A. T' E, v5 u6 r我研究后找到了一种解决办法,但还有些问题,有时会取错,正在继续研究,希望大家共同探讨。 ! Z# ~! o. \% t7 F
另外还有WindowsNT,原理也是一样,只是实现方法和95下完全不同。 * b) ~9 V5 B( C* @1 K4 U( X" s
三.技术要点
; q2 u/ H/ M1 O3 u9 L% j要实现取词,主要要解决以下技术问题。 , z2 Y+ k0 f/ W+ ^# j0 p. Q8 X& P
1.截取API入口,获得API的参数。
& v% Y0 L: `, b( n, I" G8 p1 ^2.安全地潜入Windows内部,良好地兼容Windows的各个版本
) y, L. Y- h+ M0 T- E1 y/ W8 r, S3.计算鼠标所在的单词和字母。 ; b4 J3 z* V) u% v% Z8 [2 o4 H; Q3 Q. a0 W
4.如果你在Window95下,做32位程序,还涉及Windows32/16混合编程的技术。
" t( U8 g9 \5 f! |- F) |今天先到这里吧!最好准备一份softice for 95/98和金山词霸,让我们先来分析一下别人是怎么做的。
0 z! B" S8 h* [9 P' u( q- g欢迎与我联系 - b3 d! ^/ V4 z( O3 i/ Q! a
E-Mail:[email protected] 2 [' w- m9 i! H+ \- M  y
主题  屏幕取词技术系列讲座(二)
% v/ [# B/ [) N! g9 y2 Q+ ]. p作者   亦东
* Y6 o( M6 ?9 s很抱歉让大家久等了!
- D" E( O  d" E4 V我看了一些人的回帖,发现很多人对取词的原理还是不太清楚。 0 D+ F, W: {# j8 w0 p" Z* }
首先我来解释一下hook问题。词霸中的确用到了hook,而且他用了两种hook其中一种是Windows标准hook,通过SetWindowHook安装一个回调函数,它安装了一个鼠标hook,是为了可以及时响应鼠标的消息用的和取词没太大关系。 - o# ~- H3 [, E4 b" c! t7 ^
另一种钩子是API钩子,这才是取词的核心技术所在。他在TextOut等函数的开头写了一个jmp语句,跳转到自己的代码里。
0 Z6 k; O& s3 [8 A你用softice看不到这个跳转语句是因为它只在取词的一瞬间才存在,平时是没有的。
- d* f8 g- {% N& x" n  w" [8 ]0 \& Q你可以在TextOut开头设一个读写断点
' @3 c% m$ T: l  |$ v" W( }bpm textout
9 r$ v7 f% a2 F  i" s" O; D3 j# v再取词,就会找到词霸用来写钩子的代码了。 7 i6 |0 i; f5 Y" _1 ?3 `
/********************************** 5 n" x3 [4 t% Y% m0 n, e* Q6 Y; m9 A* K
所以我在次强调,想学这种技术一定要懂汇编语言和熟练使用softice.
4 [: s! {- \* v; f: [% f6 d+ }4 b$ k**********************************/ * J: t) F; [4 I
至于从cjktl95中dump出来的未公开函数是和Windows32/16混合编程有关的,以后我会提到他们。 ! r9 N( r: h& h6 |
我先来讲述取词的过程,
6 ?: M+ o3 ]7 d0 判断鼠标是否在一个地方停留了一段时间
0 B7 a4 N6 K2 o, o0 g, y8 i1 取得鼠标当前位置 % o9 F( o9 P: |( r
2 以鼠标位置为中心生成一个矩形 3 }3 P" |% x5 K# ?) w5 h7 q3 S: j7 V5 I
3 挂上API钩子
5 y  E- ?: z, y& J" Z4 让这个矩形产生重画消息 ; L+ w1 P. u9 c
5 在钩子里等输出字符 % ^# ~. m5 M3 z4 A* u4 J
6 计算鼠标在哪个单词上面,把这个单词保存下来
" r' w2 ^' Q; w9 k8 ^7 如果得到单词则摘掉API钩子,在一段时间后,无论是否得到单词都摘掉API钩子
- Y% @! s2 h( R! l: R8 用单词查词库,显示解释框。
6 F* c4 N; z$ _! f很多步骤实现起来都有一些难度,所以在中国可以做一个完善的取词词典的人屈指可数。
: C, _6 `, H& S! m8 I) e其中0,1,2,7,8比较简单就不提了。 1 i5 B) }5 D& s% k2 D3 m( K
先说如何挂钩子: 4 _6 L3 z# J2 B& D3 d  D& m
所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句,跳转到自己的代码里。 $ D! l' t8 V6 {' c4 C0 u
步骤如下:
1 q% H$ ^& g5 j% _# @1.取得Windows API入口,用GetProcAddress实现 2 O- ]+ s4 d' G
2.保存API入口的前五个字节,因为JMP是0xEA,地址是4个字节
0 @+ a* }$ N( k: b* {# ^" @3.写入跳转语句
$ p  x$ Q) g2 a8 x9 D" ?1 `: F( K/ q这步最复杂
& T$ P3 }0 Z4 e) w& X; c  r. j7 b# YWindows的代码段本来是不可以写的,但是Microsoft给自己留了个后门。 0 M% o) L5 a. z; v* ?* J7 _
有一个未公开函数是AllocCsToDsAlias,
. A: ]6 ^9 l9 ^+ ]. m4 \: C+ sUINT WINAPI ALLOCCSTODSALIAS(UINT);
+ d& R+ i$ ~/ T' F你可以取到这个函数的入口,把API的代码段的选择符(要是不知道什么是选择符,就先去学学保护模式编程吧)传给他,他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。 * G+ {! d6 x. `
这就是取词技术的最核心的东东,不止取词,连外挂中文平台全屏汉化都是使用的这种技术。现在知道为什么这么简单的几句话却很少知道了吧?因为太多的产品使用他,太多的公司靠他赚钱了。 " m; H4 |( y1 P/ y
这些公司和产品有:中文之星,四通利方,南极星,金山词霸,实达铭泰的东方快车,roboword,译典通,即时汉化专家等等等等。。。。还有至少20多家小公司。他们的具体实现虽然不同,但大致原理是相同的。
! q% U) x" I* S2 ~8 |3 S1 v  k我这些都是随手写的,也没有提纲之类的东西,以后如果有机会我会整理一下,大家先凑合着看吧!xixi... * P! k4 K+ q7 P; h$ S4 @. \, T- a
? % n! p- {# O2 Q! W+ S8 [. |
主题  关于屏幕取词的讨论(三) ; h3 L2 u7 J6 z2 [
作者   亦东
0 _# ]9 v/ Q: V0 F
, M9 ^+ A+ ^: v' R8 F% `9 ?让大家久等,很抱歉,前些时候工作忙硬盘又坏了,太不幸了。 : l6 I- {: Z, }9 n- `$ P" l
这回来点真格的。
! f2 i3 \, i1 C0 h7 a. A咱们以截取TextOut为例。
! s5 @' h0 R* j4 K% Y( l下面是代码: # j6 t: j: \3 {. ?# J$ X  B5 y
//截取TextOut
: `( ~! ]" \- g' ntypedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT);
3 A3 s! W5 \. p* {) TALLOCCSTODSALIAS AllocCsToDsAlias; ' v1 }. @1 k, ^& W0 z% x5 T
BYTE Newvalue[5];//保存新的入口代码
0 D4 U- ]$ s  g0 v; s: oBYTE Oldvalue[5];//API原来的入口代码
+ X0 V3 r* w8 O# n/ Y: V! g: F8 vunsigned char * Address=NULL;//可写的API入口地址
1 p5 l6 N9 v+ zUINT DsSelector=NULL;//指向API入口的可写的选择符 ; g- Z, k1 a- R7 M
WORD OffSetEntry=NULL;//API的偏移量
/ h. t. F: ]7 o3 a4 BBOOL bHookAlready = FALSE; //是否挂钩子的标志
' b7 G& r- ^- ~8 h$ z4 A6 O( uBOOL InitHook() / e! R$ s8 m' I1 u9 f
{ 9 |; a+ [3 X  U" n2 o! ^3 u
HMODULE hKernel,hGdi; 2 a. }+ I  q* S9 J
hKernel = GetModuleHandle("Kernel"); 6 f6 O! K$ r. w8 {4 x
if(hKernel==NULL) 3 N  _* _& g$ z. H- F' R
return FALSE; / E# E( U5 c# o+ `2 C! C
AllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//这是未公开的API所以要这样取地址
' }6 @. h4 Y: w8 A) k6 Cif(AllocCsToDsAlias==NULL) 2 v1 j8 x/ ?7 [, s
return FALSE;
: i5 y. @. d* k  z# Y% L$ shGdi = GetModuleHandle("Gdi"); ' m! x. L+ \/ R
if(hmGdi==NULL)
6 k6 l- }% g2 g: C1 {4 k, dreturn FALSE; 2 S$ K8 X! _" C: B8 r# q
FARPROC Entry = GetProcAddress(hGdi,"TextOut");
9 d+ f+ `/ M" Y2 t$ l: Wif(Entry==NULL)
6 i; s& U5 x& ^; N' ereturn FALSE;
5 p9 o' B+ o0 |& Q' {2 _1 h- kOffSetEntry = (WORD)(FP_OFF(Entry));//取得API代码段的选择符
8 k# m$ {! H4 JDsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配一个等同的可写的选择符 ) j! U3 D/ E. _- c( a, m
Address = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址
5 g: {- d% r6 g7 ~/ p9 lNewvalue[0]=0xEA; ) E# b( R7 H. ^3 v9 }/ z
*((DWORD*)(Newvalue+1)) = (DWORD)MyTextOut;
5 y( @7 H" J, b, \; e- D, K! _Oldvalue[0]=Address[0];
$ B  {2 k9 N: c8 n) D2 q! @*((DWORD*)(Oldvalue+1)) = *((DWORD*)(Address+1)); # o- Z: H1 G4 |" R
}
' \1 q) f* V" z- h1 D# i0 {* pBOOL ClearHook() ! F  _. R7 Q& L& b8 u
{
4 R: k  I" I: {* T/ Pif(bHookAlready) 0 S# b9 g  n+ V  ?. O
HookOff(); 1 ?( N2 Q1 H/ z: Z! R9 q# q' Z' ]4 C
FreeSelector(DsSelector); " q* ?/ S9 l; H. \4 s. M0 E. f2 A
}
8 U1 @; |( z4 H1 N" R: Q+ P, p) ^BOOL HookOn()
* B( g2 `7 `% b! z{
% z/ R0 S3 _6 K, }1 }if(!bHookAlready){
6 c, g2 ^9 d3 X, f5 |& U2 c6 Gfor(int i=0;i<5;i++){ ! ~2 S6 ^$ p0 v3 U
Address=Newvalue;
- B, c4 R! h# J$ \6 q7 v* ]} " `% d5 Z& V% `4 G3 I. `
bHookAlready=TRUE; ' d! e8 x+ D' L: Z$ {) V. [
} ( a7 R7 o) L# v
} 7 O. h, `2 V( r
BOOL HookOff()
: ~% n. W. C( _: F) k{
9 g6 F3 @1 \4 p3 Q4 [if(bHookAlready){
: ^, O. Q" n) Dfor(int i=0;i<5;i++){
; t+ W1 \! b7 Y& o' b) }5 EAddress=Oldvalue; # [4 x7 {8 _1 \3 z) G6 Z/ ^
}
8 G0 l) s2 _7 I9 x4 z  ]bHookAlready=FALSE; # [$ C2 z5 x: b& D) M2 K" T5 ~, ]
}
6 t  @# B+ ]; u7 {7 b. T}
" V  i0 d$ r( m9 u& o( i: n, ^//钩子函数,一定要和API有相同的参数和声明 ; b/ t, d) k" |# Y
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString) : Z3 s; a2 Y# P4 k3 o% W# W
{
" v. a/ m, [' NBOOL ret; 1 L& N2 S* q# H2 b
HookOff();
0 K. C% J; K0 T3 X0 Q3 m, c0 K: E% H" Eret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//调原来的TextOut $ P/ y* J9 {" i# D  c; t& R! s0 Z
HookOn();
0 o: ?2 V) p7 f8 g8 Kreturn ret;
+ o: O. y* f  t. o( i& U/ u* [* y$ {( b}
; M$ H! [5 [8 V6 a4 @上面的代码是一个最简单的挂API钩子的例子,我要提醒大家的是,这段代码是我凭记忆写的,我以前的代码丢了,我没有编译测试过
8 U, T" V. I# m: ?' N; F1 N5 o因为我没有VC++1.52.所以代码可能会有错。 2 O( i: M5 @9 N! ?( Y
建议使用Borland c++,按16位编译。
2 g, t. b6 D+ t如果用VC++1.52,则要改个选项
+ S; y8 l# \1 z# _0 K1 }" H; a4 {在VC++1.52的Option里,有个内存模式的设置,选大模式,和"DS!=SS DS Load on Function entry.",切记,否则会系统崩溃。 " M1 s9 V! F/ i' q/ S- K
有什么不明白的可以给我写信 # ^+ Q% r2 t+ Y& }/ e" p
[email protected] 3 M% e9 |& T! \/ |0 _( X4 R3 Q
Wednesday, June 23, 2004 7:11 PM   g+ a* O; v7 Q: [7 D

9 g# o" i; C  m% v# q
分享到:  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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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