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

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

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

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

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

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

对于一些调用Windows标准API来进行文本输出的AVG游戏,也许可以借鉴金山词霸之类的屏幕取词技术,HOOK API 之后,在自己的函数里把截获的英文替换为中文,然后输出到屏幕上来实现汉化,而且,这样的工具一旦制作完成,对于这类的AVG游戏将是通用的。
7 z  R. g3 z, E6 A. ~* d2 I8 k
; V- ?; M- l3 {
: w0 J# g1 U  i2 v金山词霸”屏幕取词技术揭密(讨论稿) 3 r- t1 j+ [" w$ T3 s
这篇文章最早是发在北极星论坛的一系列帖子,那时候闻怡洋(好像他也是MVP)也在那里混
/ K- \. M/ u1 s" w' e; E原始的帖子我已经没有了,但不知道是谁帮我收集整理了下来(非常感谢),我用google找到了 1 O# E% i2 V% Q  T
?
5 {$ Y/ k* r! e/ r$ \  w这是我进金山之前写的,应该不算泄露公司技术秘密吧 $ |8 r* m. Q, Q8 _. n9 c
而且这些现在看来似乎已经有些过时了 " ]% ^& ~% P1 {7 H- H  t9 Z
?
4 L: v5 v& n' I2 L那时讨论的只是Win31和Win9x下的取词实现
( g2 O1 c7 o7 g8 @( S5 K% P? 6 g1 d" Z% e2 I: u& Y# T% h' |) G
我到了金山之后不是负责取词模块,而是做UI,因为有个家伙比我更擅长做这种东西
3 I  M" i+ v) w- r. P他用SoftIce调试汇编代码非常熟练,做逆向工程方面有过人的天分。
1 }4 C7 i/ v  J4 _0 L5 F- U* g( c? 4 ?1 j- _+ x0 J* U: S3 @9 T! D# O
? # S3 F* \2 l) q/ l
“亦东” 是我那时的笔名
8 g' ^  x4 u: Z7 q! v7 q( W? 3 z8 x+ x2 J3 C* q6 Z
?
% N; _8 Q2 h% {/ ]$ }“金山词霸”屏幕取词技术揭密(讨论稿)
6 v; u3 E: }% `6 u2 y; V2 }? . C$ O# B5 c9 [  F
主题  屏幕取词技术系列讲座(一) 9 y( N7 i! k" C% S
作者   亦东
4 ~7 g- `4 g3 q! X" s. @* }9 q很多人对这个问题感兴趣。
! ?  n+ K8 [% D8 i/ g$ c# L& n9 P原因是这项技术让人感觉很神奇,也很有商业价值。 2 b) a9 @5 ?8 ~& L
现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。 : D) a; X- s/ W2 @
但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。 ; c! K' l! ?1 j, ]8 n( ^0 a
大约每周一两次。想知道的人就常常来看看吧!
6 {2 b- A$ L. Q5 |8 @一.基础知识 6 ~/ j. n2 D/ g9 D
首先想编这种程序需要一些基础知识。 : c# T5 y: m; `  H
会用Vc++,包括16/32位。
! m& d* I6 a8 o: y, n精通Windows API特别是GDI,KERNEL部分。 % O; M+ W, V# E, q' L8 i
懂汇编语言,会用softice调试程序,因为这种程序最好用softice调试。
8 b6 Q- j7 K3 ?4 [二.基本原理 8 ~" v" Q  K: l+ i# U
在Window 3.x时代,windows系统提供的字符输出函数只有很少的几个。
$ W8 o4 m% Z6 B* e3 S1 kTextOut
# T3 B% ^6 c- o; Q# J  WExtTextOut
2 H: V$ Y  ?7 }( W8 E& ?: D" ?DrawText
, F0 t3 b. r& `) b* G4 J...... / L# F- z# J" O4 `8 n. N# F
其中DrawText最终是用ExtTextOut实现的。 5 w3 z  O# }; @) p8 ]  v
所以Windows的所有字符输出都是由调用TextOut和ExtTextOut实现的。因此,如果你可以修改这两个函数的入口,让程序先调用你自己的一个函数再调用系统的字符输出,你就可以得到Windows所有输出的字符了。 0 o% ~: ^2 ?# B4 x" G6 m- |5 e" E
到了Windows95时代,原理基本没变,但是95比3.x要复杂。开始的时候,一些在windows3.x下编写的取词软件仍然可以是使用。但是后来出了个IE4,结果很多词典软件就因为不支持IE4而被淘汰了,但同时也给一些软件创造了机会,如金山词霸。其实IE4的问题并不复杂,只不过它的输出的是unicode字符,是用TextOutW和ExtTextOutW输出的。知道了这一点,只要也截取就可以了。不过实现方法复杂一点,以后会有详细讲解。现在又出了个IE5,结果词霸也不好用了,微软真是#^@#$%$*&^&#@#@.......... 3 s. C6 T; I/ D# y* E
我研究后找到了一种解决办法,但还有些问题,有时会取错,正在继续研究,希望大家共同探讨。 " G, r" k6 P& I0 l
另外还有WindowsNT,原理也是一样,只是实现方法和95下完全不同。 * v5 m+ r& O+ p% Z" u2 b' I
三.技术要点 6 B1 E7 B% p' r. Y
要实现取词,主要要解决以下技术问题。 ) e- v9 E, s# A0 v
1.截取API入口,获得API的参数。
5 C! C: ^5 v6 f- `" f- q3 E2.安全地潜入Windows内部,良好地兼容Windows的各个版本 4 c' N+ }* `; d& |8 q
3.计算鼠标所在的单词和字母。 * v/ }# p) t0 x3 g8 _- V' h" R
4.如果你在Window95下,做32位程序,还涉及Windows32/16混合编程的技术。
+ ~$ h, h, i5 i今天先到这里吧!最好准备一份softice for 95/98和金山词霸,让我们先来分析一下别人是怎么做的。
. ?/ f8 c/ q+ ^9 {! k欢迎与我联系
8 F$ l# s3 E0 P" DE-Mail:[email protected] ) J: N* l9 p4 b4 o/ c, Q7 Q
主题  屏幕取词技术系列讲座(二)
; b9 W/ H; s2 y" ?9 D& @/ e作者   亦东
( `$ ^! n2 L7 g; k5 Y9 \0 a$ T很抱歉让大家久等了!
/ M) H$ N- |. q( R我看了一些人的回帖,发现很多人对取词的原理还是不太清楚。 + Q  W( W( k: h3 Q
首先我来解释一下hook问题。词霸中的确用到了hook,而且他用了两种hook其中一种是Windows标准hook,通过SetWindowHook安装一个回调函数,它安装了一个鼠标hook,是为了可以及时响应鼠标的消息用的和取词没太大关系。
" L( V2 i. s" \! Z' E& f/ }' G另一种钩子是API钩子,这才是取词的核心技术所在。他在TextOut等函数的开头写了一个jmp语句,跳转到自己的代码里。
. D: n- M7 W2 t4 p/ Y你用softice看不到这个跳转语句是因为它只在取词的一瞬间才存在,平时是没有的。
/ ?# P: r! K4 Y7 n你可以在TextOut开头设一个读写断点
) B6 F0 e* D. \, _bpm textout
% V: q* y* C. o1 e; @  D: D再取词,就会找到词霸用来写钩子的代码了。
% s) ?' X* C9 p& u9 }  n! m* Y; |% q4 }* H3 i/**********************************
6 E! J$ r  Y% L* D/ Y9 @+ y) z所以我在次强调,想学这种技术一定要懂汇编语言和熟练使用softice. 5 Z0 F. H! E" s4 D2 \
**********************************/ ; \) h" N1 c  t6 ?9 T  J1 \
至于从cjktl95中dump出来的未公开函数是和Windows32/16混合编程有关的,以后我会提到他们。
7 a" J+ L, R$ W, p3 E; \我先来讲述取词的过程,
+ e# o4 z' C. l- I. }9 Y0 判断鼠标是否在一个地方停留了一段时间   ~: d* o, A, X5 Q
1 取得鼠标当前位置 5 _( v, z) J5 E7 R
2 以鼠标位置为中心生成一个矩形
$ V8 W) Q, q# N- C1 U& ?  c4 \! ~3 挂上API钩子 . ^- |+ H/ O# e/ w
4 让这个矩形产生重画消息 : P5 M0 a  Z* T$ T2 I
5 在钩子里等输出字符 8 C, y, K& I. i$ A# h4 v3 i9 L
6 计算鼠标在哪个单词上面,把这个单词保存下来 * o0 s4 b7 A6 [5 N/ m7 @
7 如果得到单词则摘掉API钩子,在一段时间后,无论是否得到单词都摘掉API钩子
: b( |2 T6 Z( ?5 A/ q9 N8 用单词查词库,显示解释框。 " v' b/ ?- Q; m) J# _) `; d: ]
很多步骤实现起来都有一些难度,所以在中国可以做一个完善的取词词典的人屈指可数。 ! x' Z  Q* z+ ?
其中0,1,2,7,8比较简单就不提了。 & A: I, \, i7 }- @; }, ]; }
先说如何挂钩子:   H) E% d4 H" R9 g+ d  e
所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句,跳转到自己的代码里。 2 Y, [, E6 k4 u- ~  @4 f3 m
步骤如下: , R3 A  h3 E) w6 w4 w* v
1.取得Windows API入口,用GetProcAddress实现
9 B/ ^* n: H) S" L( `2.保存API入口的前五个字节,因为JMP是0xEA,地址是4个字节
1 h) I3 H# J5 ?- w1 n: [  d3.写入跳转语句
& t6 X6 c5 T; [0 O) @$ }这步最复杂
% u- Z* K* L  O7 ]3 \Windows的代码段本来是不可以写的,但是Microsoft给自己留了个后门。 , S8 ]# _' X2 D
有一个未公开函数是AllocCsToDsAlias, % }: J$ i5 z! O# H& r$ S  t7 V
UINT WINAPI ALLOCCSTODSALIAS(UINT); 8 q2 W' r' S6 h$ N& d4 `( N; Q
你可以取到这个函数的入口,把API的代码段的选择符(要是不知道什么是选择符,就先去学学保护模式编程吧)传给他,他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。
- o+ t% c* `5 D7 y& K这就是取词技术的最核心的东东,不止取词,连外挂中文平台全屏汉化都是使用的这种技术。现在知道为什么这么简单的几句话却很少知道了吧?因为太多的产品使用他,太多的公司靠他赚钱了。 * K3 x: O8 }0 M/ x/ \8 T( r2 j% }
这些公司和产品有:中文之星,四通利方,南极星,金山词霸,实达铭泰的东方快车,roboword,译典通,即时汉化专家等等等等。。。。还有至少20多家小公司。他们的具体实现虽然不同,但大致原理是相同的。
$ f! @) m: ]6 e# c我这些都是随手写的,也没有提纲之类的东西,以后如果有机会我会整理一下,大家先凑合着看吧!xixi...
0 w8 m  X7 c3 j! l( w? 3 X7 z3 v% E9 t/ i9 o: F
主题  关于屏幕取词的讨论(三)
1 E" E5 L* C% T8 L作者   亦东 ( \' K0 V' }6 _% q4 F- I0 {

+ J' @& ?7 Z, @. @让大家久等,很抱歉,前些时候工作忙硬盘又坏了,太不幸了。
8 O4 X- |  b; C0 o$ |% j这回来点真格的。
5 B, N* i+ l  k$ J; \咱们以截取TextOut为例。 6 C' F' c5 \6 Y
下面是代码: . b- e6 }  c- t* C3 {+ H5 l) R9 W
//截取TextOut 0 \: m$ f! j1 f
typedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT);
. A9 u! I. s7 M; GALLOCCSTODSALIAS AllocCsToDsAlias;
- X: k/ d* c  q/ }, }/ SBYTE Newvalue[5];//保存新的入口代码
# P) P# c& R  R. e8 n* i9 v! F0 fBYTE Oldvalue[5];//API原来的入口代码 + f0 q' A6 C( f' N: e3 Z% Z7 }/ I
unsigned char * Address=NULL;//可写的API入口地址
% y+ H0 I! F3 }: n5 p" Y  NUINT DsSelector=NULL;//指向API入口的可写的选择符 8 {$ W3 f# z$ c; q/ f( V
WORD OffSetEntry=NULL;//API的偏移量 ! L, I& J  d3 ~$ g0 A) S
BOOL bHookAlready = FALSE; //是否挂钩子的标志 ) f4 M' q8 L( i: Q
BOOL InitHook() ) g2 {2 j3 y, O9 o6 V
{
$ O2 J, f( b) QHMODULE hKernel,hGdi;
! ?) [. f" x7 j) }/ A) L7 ehKernel = GetModuleHandle("Kernel");
  d$ N8 [; f: k9 K6 i2 {( f1 Z4 Eif(hKernel==NULL) 1 ^5 f4 d( A0 i) j3 R" ]
return FALSE; : t' V3 Y+ y6 w" ~7 x
AllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//这是未公开的API所以要这样取地址
0 y" E, e# x" c" h2 Rif(AllocCsToDsAlias==NULL) ; S" ^; M' _% M1 B
return FALSE; % |) c0 @' o6 ]% X
hGdi = GetModuleHandle("Gdi"); " p' U, c, t, h- x/ m5 \
if(hmGdi==NULL)
! c7 o$ \* M( Treturn FALSE; 0 g1 P# H5 J* A) X
FARPROC Entry = GetProcAddress(hGdi,"TextOut"); 0 E5 {# e' G, [( p# |7 `- G
if(Entry==NULL)
+ [- v% B5 v8 B, f8 V& hreturn FALSE; 7 ?$ W/ Y  C" U) W2 q8 F7 p/ z
OffSetEntry = (WORD)(FP_OFF(Entry));//取得API代码段的选择符 $ q  ^+ K' _* C
DsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配一个等同的可写的选择符
; @: R+ s. _) k. S. LAddress = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址 * h7 C7 \  p  g0 m; S
Newvalue[0]=0xEA;
/ v: J8 ?! o. n8 f) U) B" M  ~4 b*((DWORD*)(Newvalue+1)) = (DWORD)MyTextOut; # ?: L' {: f  x2 C# S! _* v/ Y- V
Oldvalue[0]=Address[0]; # g6 @$ o/ ?4 x# E$ W. ^) v. C
*((DWORD*)(Oldvalue+1)) = *((DWORD*)(Address+1));
: M* e5 D! C3 b} 2 R1 l; _, W7 X0 ?" t, A& G
BOOL ClearHook()
- e3 ?" l# U# m1 C- j7 u  j1 s4 x! m{ 2 O7 g8 T& ]2 D! D) n
if(bHookAlready) 9 k8 P9 F: t' ~0 E5 k
HookOff(); $ f  {- z* ]' e6 K  ?" J
FreeSelector(DsSelector);
9 q% C  u% ^0 }& Y7 C+ a} 0 u& W5 T) `) l/ x3 Y1 g/ [6 a
BOOL HookOn()
' q0 X: `! P5 `3 s4 e4 G: J{
2 f5 e+ w5 p- w, I5 {if(!bHookAlready){
& P+ k  F& U5 i* {! p) Jfor(int i=0;i<5;i++){ ) t1 k$ l* w  `- l7 u' x
Address=Newvalue;
' A, {. U6 d" _% x0 m} $ k" j2 t' e' L4 ^* G
bHookAlready=TRUE;
$ T1 M8 ~0 [, l} * B- O* ]9 `* T9 W
} 7 |; i6 Y* e+ v5 Q( ?
BOOL HookOff()
. X, z' J' b- ~; b. r' u  r9 g{
- y' l$ k. j) d! M' k6 [if(bHookAlready){
# F7 ]+ a6 t  mfor(int i=0;i<5;i++){
" @8 k1 \9 {! o4 @! tAddress=Oldvalue; 8 {$ T6 P( H% }. {  o( c% @9 v
} 0 P" K! a" I0 v
bHookAlready=FALSE; 3 E/ T' k4 e& v8 [3 [, a5 j
} ! X/ O/ {! o# D4 O: I/ _
} # I1 \7 q2 W/ l; J6 t
//钩子函数,一定要和API有相同的参数和声明 % n& u( W) s. o3 ~; S; [- g
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString)
+ k: y+ I5 E( x3 _" x' R% Z, H{ 4 O" {8 L+ J! o
BOOL ret;
) X! v' C1 w+ f) Q& YHookOff();
7 S( o* g' z0 m; L5 m, Kret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//调原来的TextOut
# f# x/ C, k/ @  [1 n7 SHookOn(); 8 K1 o- h8 A; X7 o! M* E$ w
return ret;
' T/ N  T, }+ d- M}
8 o% S3 r$ g! `2 V6 c) a8 w7 k上面的代码是一个最简单的挂API钩子的例子,我要提醒大家的是,这段代码是我凭记忆写的,我以前的代码丢了,我没有编译测试过 4 M: T- i, g5 M: P
因为我没有VC++1.52.所以代码可能会有错。
) L/ u4 T9 Z8 b# I, W建议使用Borland c++,按16位编译。 ; X  R- T& G& a( |- q
如果用VC++1.52,则要改个选项 % y$ d/ u: N+ m3 V' J
在VC++1.52的Option里,有个内存模式的设置,选大模式,和"DS!=SS DS Load on Function entry.",切记,否则会系统崩溃。 , `8 \' {6 }8 t
有什么不明白的可以给我写信 . C2 d2 a: a6 [4 s  Y* R
[email protected]
) ?. V. P( K0 f) C7 ?Wednesday, June 23, 2004 7:11 PM
* \5 `7 b) D" o% P. V& k  v; }7 C
; F1 {$ y3 e. I; k# o
分享到:  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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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