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

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

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

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

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

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

对于一些调用Windows标准API来进行文本输出的AVG游戏,也许可以借鉴金山词霸之类的屏幕取词技术,HOOK API 之后,在自己的函数里把截获的英文替换为中文,然后输出到屏幕上来实现汉化,而且,这样的工具一旦制作完成,对于这类的AVG游戏将是通用的。
5 s+ Q. Z( g) |8 N" b# c4 J! e- N$ Y$ n- r0 S; H8 V( |
, K5 G# \& N' V1 g7 M
金山词霸”屏幕取词技术揭密(讨论稿)
( B6 S1 ]1 L9 z& v$ y这篇文章最早是发在北极星论坛的一系列帖子,那时候闻怡洋(好像他也是MVP)也在那里混
2 O" t) s4 \7 B. T1 k5 D0 M* K原始的帖子我已经没有了,但不知道是谁帮我收集整理了下来(非常感谢),我用google找到了
8 v7 B% _8 _6 {" K9 A" B? " D! w* A: W" i7 V) D; L
这是我进金山之前写的,应该不算泄露公司技术秘密吧
8 }( O0 X8 `% Y% b+ q# H% X而且这些现在看来似乎已经有些过时了
% J2 U2 l* n1 @3 _! b? ; }7 F) O5 E" J( C1 T7 Q  @* s
那时讨论的只是Win31和Win9x下的取词实现 . f. w$ m* D' C9 I. U3 y3 a0 a
?
0 `) E; c3 n  {8 d9 a1 ?我到了金山之后不是负责取词模块,而是做UI,因为有个家伙比我更擅长做这种东西
4 x% x" J' F' j他用SoftIce调试汇编代码非常熟练,做逆向工程方面有过人的天分。
  M5 z# [& X5 }- ^: A. n?
6 ^: {+ Z* g" i- R4 O! p?
% t* N4 d6 B- I) J! l% s$ H/ b: I8 _“亦东” 是我那时的笔名
  T$ C: d3 ?* T? 4 A' D0 G7 U$ G5 r! c" R, J
?
+ i+ ^6 b' j0 x8 \“金山词霸”屏幕取词技术揭密(讨论稿) 0 C' O- B# F& s1 d% G
?
6 G3 m+ `* t; U4 f+ J/ Q7 i+ w主题  屏幕取词技术系列讲座(一)   O3 L  r% Q" o8 W: g
作者   亦东 ( k; b1 a' u2 x7 b
很多人对这个问题感兴趣。 , H( M6 R. z1 x7 m" _
原因是这项技术让人感觉很神奇,也很有商业价值。 4 w5 d9 O1 ?) M0 Y+ k9 S
现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。
( T8 E0 R+ n5 d4 p但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。 6 m! T( [+ ~! n5 Q1 ?
大约每周一两次。想知道的人就常常来看看吧! ' N; Q: P! Z" t- C
一.基础知识
0 Q9 w7 G6 U2 F; L# a首先想编这种程序需要一些基础知识。
! l1 f# x% H' A& M1 S9 w会用Vc++,包括16/32位。
$ b% S& X% G* ?2 W2 j2 {精通Windows API特别是GDI,KERNEL部分。   V1 l( U1 B# ?( ^! g/ ^) L
懂汇编语言,会用softice调试程序,因为这种程序最好用softice调试。 6 L. H0 l& }# k$ ~
二.基本原理 * S5 {& C+ l0 o# u2 F4 o. g+ P; I" P
在Window 3.x时代,windows系统提供的字符输出函数只有很少的几个。 + p& e4 i: z) q
TextOut 5 x) f- c/ w, ~+ M7 `3 ]
ExtTextOut
1 K6 q' T+ h7 Y8 z6 j0 L! @DrawText : [% @( n0 {6 m0 O9 x. W' x( O
...... * O0 R( M4 u. |  e, f
其中DrawText最终是用ExtTextOut实现的。
! i. P/ F3 y, U所以Windows的所有字符输出都是由调用TextOut和ExtTextOut实现的。因此,如果你可以修改这两个函数的入口,让程序先调用你自己的一个函数再调用系统的字符输出,你就可以得到Windows所有输出的字符了。 ; j6 b/ ], q- S8 s2 P3 C
到了Windows95时代,原理基本没变,但是95比3.x要复杂。开始的时候,一些在windows3.x下编写的取词软件仍然可以是使用。但是后来出了个IE4,结果很多词典软件就因为不支持IE4而被淘汰了,但同时也给一些软件创造了机会,如金山词霸。其实IE4的问题并不复杂,只不过它的输出的是unicode字符,是用TextOutW和ExtTextOutW输出的。知道了这一点,只要也截取就可以了。不过实现方法复杂一点,以后会有详细讲解。现在又出了个IE5,结果词霸也不好用了,微软真是#^@#$%$*&^&#@#@..........
% o. i+ ]- \9 C+ v我研究后找到了一种解决办法,但还有些问题,有时会取错,正在继续研究,希望大家共同探讨。 # j3 J/ m$ W) F6 b. D3 e/ n
另外还有WindowsNT,原理也是一样,只是实现方法和95下完全不同。
1 T4 W  {* [0 D/ E3 F3 F三.技术要点
+ Y0 z% A" `2 o0 O" p2 z( Q要实现取词,主要要解决以下技术问题。
/ D/ K; p7 b3 I+ G' q; r- r1.截取API入口,获得API的参数。 : v2 E4 T2 p+ u! Y+ F
2.安全地潜入Windows内部,良好地兼容Windows的各个版本
' j, R  c- l; _/ C3.计算鼠标所在的单词和字母。
3 L" s- a/ n1 d4.如果你在Window95下,做32位程序,还涉及Windows32/16混合编程的技术。
' E2 z2 i2 c1 i6 A! \' X今天先到这里吧!最好准备一份softice for 95/98和金山词霸,让我们先来分析一下别人是怎么做的。
) K+ s* h/ p6 F欢迎与我联系
$ \  q" o8 L6 a: j8 {1 j& aE-Mail:[email protected]
5 Q( b3 y5 ]; p8 M主题  屏幕取词技术系列讲座(二) ) B9 @# g  `) M* i; ?3 S+ t
作者   亦东
4 X2 H5 |" P# ^8 Q% G" S很抱歉让大家久等了! ' R; d) X* s3 M- O" Q% {& J; [0 W
我看了一些人的回帖,发现很多人对取词的原理还是不太清楚。   M& `* j6 S! ]
首先我来解释一下hook问题。词霸中的确用到了hook,而且他用了两种hook其中一种是Windows标准hook,通过SetWindowHook安装一个回调函数,它安装了一个鼠标hook,是为了可以及时响应鼠标的消息用的和取词没太大关系。 ! [5 C/ _6 E2 u
另一种钩子是API钩子,这才是取词的核心技术所在。他在TextOut等函数的开头写了一个jmp语句,跳转到自己的代码里。
) g8 u6 t5 {* x- x- ]9 c你用softice看不到这个跳转语句是因为它只在取词的一瞬间才存在,平时是没有的。 # B3 ]1 P- o9 L! f4 f; \4 e
你可以在TextOut开头设一个读写断点   p; y3 d$ `! e* B
bpm textout
; F5 p- h7 o9 p# \' o再取词,就会找到词霸用来写钩子的代码了。
! w8 Z# i8 b3 @8 r/**********************************
! O4 N) T, H: d# [4 h所以我在次强调,想学这种技术一定要懂汇编语言和熟练使用softice.
6 o- k) J1 q# K7 A**********************************/ 6 ?+ G: P  k6 P; L4 v
至于从cjktl95中dump出来的未公开函数是和Windows32/16混合编程有关的,以后我会提到他们。
( n1 X# y' w4 J2 s2 ^+ {我先来讲述取词的过程,
& j0 j9 S7 n- V/ ]+ a$ g  p0 判断鼠标是否在一个地方停留了一段时间
- x. H" b3 }' f: I+ y/ f9 x1 取得鼠标当前位置   t4 e- e# P. u5 w
2 以鼠标位置为中心生成一个矩形 / q+ D$ B! {( Y1 t5 D
3 挂上API钩子 , n' S) a) w' R" Q0 P3 B
4 让这个矩形产生重画消息 - S* ~7 x% R8 \/ j: p: n
5 在钩子里等输出字符
- a2 v7 n$ P/ F0 p6 计算鼠标在哪个单词上面,把这个单词保存下来 ; ~& Z9 {0 V+ \5 e9 b" ]
7 如果得到单词则摘掉API钩子,在一段时间后,无论是否得到单词都摘掉API钩子
; q& d5 r- w, Y) @9 F8 用单词查词库,显示解释框。 9 a$ j) Z' ^2 Y; q- t6 h
很多步骤实现起来都有一些难度,所以在中国可以做一个完善的取词词典的人屈指可数。
( p' T! x+ ?6 U) |其中0,1,2,7,8比较简单就不提了。 % Z, L, g% v. g9 e2 q' K
先说如何挂钩子:
0 m" x: A: Z# ]/ l" ~, z9 f" T所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句,跳转到自己的代码里。 + O( }5 n5 K  ~- @
步骤如下:
& F) E  t! ^" U2 D$ p$ F1.取得Windows API入口,用GetProcAddress实现
  K; ]6 g6 i: L2 z2.保存API入口的前五个字节,因为JMP是0xEA,地址是4个字节 , x/ K6 q+ E6 K, d
3.写入跳转语句   O; d; w& r3 G9 J& J( _
这步最复杂 , Z0 w! Y$ J8 X' U3 P  D3 f$ s
Windows的代码段本来是不可以写的,但是Microsoft给自己留了个后门。
& t4 [) _8 d/ B1 X# |  E2 F有一个未公开函数是AllocCsToDsAlias, 8 y8 ?& ?# n9 c7 k4 m
UINT WINAPI ALLOCCSTODSALIAS(UINT); 8 [& x( e; Z5 b: d
你可以取到这个函数的入口,把API的代码段的选择符(要是不知道什么是选择符,就先去学学保护模式编程吧)传给他,他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。 1 @+ ?8 N5 g7 I
这就是取词技术的最核心的东东,不止取词,连外挂中文平台全屏汉化都是使用的这种技术。现在知道为什么这么简单的几句话却很少知道了吧?因为太多的产品使用他,太多的公司靠他赚钱了。
! Q4 D% B; @4 V: l( h3 S这些公司和产品有:中文之星,四通利方,南极星,金山词霸,实达铭泰的东方快车,roboword,译典通,即时汉化专家等等等等。。。。还有至少20多家小公司。他们的具体实现虽然不同,但大致原理是相同的。
" H) g) h1 a$ ?$ o' P我这些都是随手写的,也没有提纲之类的东西,以后如果有机会我会整理一下,大家先凑合着看吧!xixi... , G% I/ W7 ~- B4 Q7 h1 h0 a$ t2 B/ A' Y
?
$ E* \; S* F+ M5 ~) Q! G- @主题  关于屏幕取词的讨论(三) ' C/ g, |: p- q) K$ g0 Q, _
作者   亦东
3 J8 \* v, N+ `" K& u  _3 y5 \: \8 t! Q+ _5 `! I. u
让大家久等,很抱歉,前些时候工作忙硬盘又坏了,太不幸了。 4 S7 m" Q+ w5 H; `- u. \7 Z6 x
这回来点真格的。
: `5 ^! W3 n0 n咱们以截取TextOut为例。
1 X$ g: ]8 _+ \& ~下面是代码:
2 Z( x, g: D, _+ [//截取TextOut - t. M5 I, R3 d1 ?! f
typedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT); ! @. \* t. M  ^2 @
ALLOCCSTODSALIAS AllocCsToDsAlias;
8 j$ e5 V, d4 V* nBYTE Newvalue[5];//保存新的入口代码
* r2 g& T; B% d$ }+ n& P0 _BYTE Oldvalue[5];//API原来的入口代码 % m( g" H. s+ K8 w' Y0 p
unsigned char * Address=NULL;//可写的API入口地址
5 D& {& n! o4 c; w& J; \" U1 MUINT DsSelector=NULL;//指向API入口的可写的选择符 ( h# e" }) A' p+ b$ Z! x9 t
WORD OffSetEntry=NULL;//API的偏移量 " o& T# g! l6 O4 ]5 |
BOOL bHookAlready = FALSE; //是否挂钩子的标志 ( u2 Q) i: D5 Y+ E2 ^3 |
BOOL InitHook() ( x' m( {* Q: }# q2 w
{
2 o6 B8 ~4 ^+ p0 D2 U# y  iHMODULE hKernel,hGdi; . c$ b, h3 Q% s) z- v! v2 e
hKernel = GetModuleHandle("Kernel");
+ Y8 G( }; {. K+ }" `: nif(hKernel==NULL)
, w, ?9 j1 U' y5 A+ U( B! E3 sreturn FALSE; " I6 l- J, g; `0 ~$ |. |# Y
AllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//这是未公开的API所以要这样取地址
0 Z$ w, e8 f) M" l4 b$ x4 o+ }% yif(AllocCsToDsAlias==NULL)
1 |5 q) s6 a, E9 U+ z/ P- mreturn FALSE;   D! Z- ]7 p! a2 {
hGdi = GetModuleHandle("Gdi");
8 L# h, M: s! e: {  Aif(hmGdi==NULL) 8 d$ [9 q5 Q9 X; W" j/ v
return FALSE;
- H3 `8 ~9 _8 j( s1 O( H! Q" p5 pFARPROC Entry = GetProcAddress(hGdi,"TextOut");
/ G1 B! M! A( ], r; v- t/ u/ g  Dif(Entry==NULL) ( M8 u$ O! D3 g$ J* z( M: Z
return FALSE; 8 }; |- O% [. F5 J) G$ c3 t
OffSetEntry = (WORD)(FP_OFF(Entry));//取得API代码段的选择符 4 s" X$ x8 X$ g  t4 ]9 S
DsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配一个等同的可写的选择符
, I8 ^0 ]- B7 SAddress = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址 - {$ |' ?1 t8 {, L1 Z( B/ i& Y
Newvalue[0]=0xEA; 7 m3 n: \! P& R1 Z+ `
*((DWORD*)(Newvalue+1)) = (DWORD)MyTextOut; # l, R+ X6 r0 E$ l( I
Oldvalue[0]=Address[0];
6 f2 f5 V* h0 y7 v4 {  a*((DWORD*)(Oldvalue+1)) = *((DWORD*)(Address+1));
" B/ J: o  ^: y}
! M( G% ~1 Q$ W6 U0 fBOOL ClearHook()
6 i* c3 v$ k, V; `% U{ 8 n- x; Q, @8 `7 {% B$ @9 p6 n5 w0 z& x
if(bHookAlready)
/ U4 w  E3 N  \$ o* O/ OHookOff();
) I" a3 I) \# vFreeSelector(DsSelector); : U0 L- [) p  \0 v& U9 d- m/ ~
} ) R, g& b  \& e, j1 {* M& H
BOOL HookOn() , x% k' P9 Z$ w- i/ R/ y! A
{   ^1 I% _! e4 p  ]4 P4 {
if(!bHookAlready){ + d' a) A% W% o6 S; V
for(int i=0;i<5;i++){ 6 j; N: m  g) E% Q% x* E0 d
Address=Newvalue; " p& m  L2 e; }- T
}
( q4 h8 d' d. E8 _. bbHookAlready=TRUE;
" R! k8 z9 h4 B+ ~9 R} # w; Q, |5 U2 d; ^
} # m. W. W) ^7 _  U) |  @
BOOL HookOff() 5 P, ^( _) }' ?1 }1 v/ c
{ 2 C# r6 z; ?7 P. x4 m# X, b
if(bHookAlready){
& O4 V9 f6 {9 @8 T: K4 jfor(int i=0;i<5;i++){
7 y- o: @& J, jAddress=Oldvalue;
6 T5 ]! V' @* D2 I# U/ s/ ^} 7 O  ]/ T, H2 P: G# ^8 s
bHookAlready=FALSE; 9 l, ]3 V9 m# ~" A5 {0 ~, j& S6 c' x
} , K, e* s: N- w- O2 P" ?/ `
} " l& Q6 Q/ q8 v2 e  v$ T& ^
//钩子函数,一定要和API有相同的参数和声明 % y( z* Y. i& ~1 O! [
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString)
( }8 |3 @6 t8 Z{
) j; J0 J7 j, Q; g1 vBOOL ret; 3 c! P8 Z/ y! A
HookOff();
+ C: g; ~$ p( T- @( @ret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//调原来的TextOut 1 k* q1 Y0 v- C/ H" O5 v8 P- r
HookOn();
: B- c9 ]: P' @% d1 _9 H9 }! sreturn ret; " M2 p% o8 C/ r4 [8 o  V
} 8 U% _- x8 R5 S! Q1 o0 Q
上面的代码是一个最简单的挂API钩子的例子,我要提醒大家的是,这段代码是我凭记忆写的,我以前的代码丢了,我没有编译测试过
* K8 C; v: v! c& ^: m  @1 G5 h因为我没有VC++1.52.所以代码可能会有错。 8 C& m; D" t7 e
建议使用Borland c++,按16位编译。
& `4 M8 _9 q9 S; U2 r6 x* u' W: U如果用VC++1.52,则要改个选项 , C; s: w; k+ a' {) k! ]
在VC++1.52的Option里,有个内存模式的设置,选大模式,和"DS!=SS DS Load on Function entry.",切记,否则会系统崩溃。
2 K0 E5 t/ m9 m' ^* D+ C有什么不明白的可以给我写信 . h' }0 g) [  p
[email protected]
* Y" ?) W& D% w- i4 qWednesday, June 23, 2004 7:11 PM ' s, n% \+ r! s% ]4 i9 ~$ E

( J- g# C  C  c. L! @( Q: D0 c
分享到:  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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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