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

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

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

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

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

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

对于一些调用Windows标准API来进行文本输出的AVG游戏,也许可以借鉴金山词霸之类的屏幕取词技术,HOOK API 之后,在自己的函数里把截获的英文替换为中文,然后输出到屏幕上来实现汉化,而且,这样的工具一旦制作完成,对于这类的AVG游戏将是通用的。) t& v0 c2 @2 P$ s9 R
) B' Z% G6 t4 v7 b( J/ p$ R2 Q
3 g: K: o" ?9 C' z2 Q& p1 O
金山词霸”屏幕取词技术揭密(讨论稿)
( Y2 ]3 b0 C) r' Q7 e3 W这篇文章最早是发在北极星论坛的一系列帖子,那时候闻怡洋(好像他也是MVP)也在那里混
1 L9 g( R# g9 W原始的帖子我已经没有了,但不知道是谁帮我收集整理了下来(非常感谢),我用google找到了
0 \/ \7 S# w2 F+ ]?
7 Z# D& N  O  N( L6 ?% h( \这是我进金山之前写的,应该不算泄露公司技术秘密吧 2 c# }) s/ D# D( `. H" Q
而且这些现在看来似乎已经有些过时了
9 b$ I  p4 A* a0 O? 5 E- j6 v$ z: a* r5 F* x2 p! n
那时讨论的只是Win31和Win9x下的取词实现
9 D  [# P# p$ o5 \$ S  `?
# M0 D; Z. |% W2 N! s5 p0 c; v% f我到了金山之后不是负责取词模块,而是做UI,因为有个家伙比我更擅长做这种东西 & X1 K7 K' _; `) M$ V4 L! ?: d6 S
他用SoftIce调试汇编代码非常熟练,做逆向工程方面有过人的天分。 2 n0 n: @2 p6 {
? - l# l' t2 l4 ?7 B6 @# g8 \" P& B: ~( a
?
. G* H9 J* E1 e) c% k% n“亦东” 是我那时的笔名 * `# ^' u, f' T3 K+ ~) e9 k
?
$ x. x" K5 x# O! r? ; P. i8 ]/ D/ E& v2 r
“金山词霸”屏幕取词技术揭密(讨论稿) 8 |, g/ p; `% M! _; @+ d) q* I
? 2 `7 v8 i7 |& A% }+ ?/ J
主题  屏幕取词技术系列讲座(一)
: i  p6 L6 k: g% O, l作者   亦东 : R# f! p+ p% R6 O
很多人对这个问题感兴趣。
( A% `# h7 l' C: e8 @原因是这项技术让人感觉很神奇,也很有商业价值。 # P7 h. g' \% {6 W
现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。 ; l! ]( e% b+ h( I& s
但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。
1 D0 i2 D* }$ n+ N* m4 \大约每周一两次。想知道的人就常常来看看吧! ! S- w3 V& w3 q" _- {& P
一.基础知识
( X1 T9 f3 j" ]7 O6 D首先想编这种程序需要一些基础知识。
' o8 L! B" L# w+ y% P, Q$ u会用Vc++,包括16/32位。
" A$ o  W0 f6 `% v精通Windows API特别是GDI,KERNEL部分。
* \' Z- {. H) ]% n( J5 J懂汇编语言,会用softice调试程序,因为这种程序最好用softice调试。
. q$ g. |8 V* l3 s二.基本原理
: Z# z* k# f: B在Window 3.x时代,windows系统提供的字符输出函数只有很少的几个。 % `. B, p/ @$ \0 K1 h0 x9 @6 o4 s
TextOut
8 F8 I8 t9 Q. E) E9 BExtTextOut + B! \% i3 @) D" ~
DrawText
6 S; _# `8 L! t& {......
+ O9 ~1 T- m! s4 ~  \3 g4 N5 `其中DrawText最终是用ExtTextOut实现的。
9 ~1 @  c" z. @2 D所以Windows的所有字符输出都是由调用TextOut和ExtTextOut实现的。因此,如果你可以修改这两个函数的入口,让程序先调用你自己的一个函数再调用系统的字符输出,你就可以得到Windows所有输出的字符了。
6 c1 X! z( M; P; ~* Y1 G到了Windows95时代,原理基本没变,但是95比3.x要复杂。开始的时候,一些在windows3.x下编写的取词软件仍然可以是使用。但是后来出了个IE4,结果很多词典软件就因为不支持IE4而被淘汰了,但同时也给一些软件创造了机会,如金山词霸。其实IE4的问题并不复杂,只不过它的输出的是unicode字符,是用TextOutW和ExtTextOutW输出的。知道了这一点,只要也截取就可以了。不过实现方法复杂一点,以后会有详细讲解。现在又出了个IE5,结果词霸也不好用了,微软真是#^@#$%$*&^&#@#@.......... - T( p: O. a( K
我研究后找到了一种解决办法,但还有些问题,有时会取错,正在继续研究,希望大家共同探讨。
6 E3 q3 v! W; j. ~( r另外还有WindowsNT,原理也是一样,只是实现方法和95下完全不同。 / c- M/ X+ W: S* w
三.技术要点
: \  N! W7 h8 c3 a' ^0 l- ~1 \2 X要实现取词,主要要解决以下技术问题。
. P$ s* J& d' O3 [1.截取API入口,获得API的参数。 % B, z6 |% ]( }' q! n8 ^5 a
2.安全地潜入Windows内部,良好地兼容Windows的各个版本 % N6 r0 y3 n  C9 a8 i
3.计算鼠标所在的单词和字母。
3 M3 b+ j4 s) [  f. l  Z' @8 ]6 o4.如果你在Window95下,做32位程序,还涉及Windows32/16混合编程的技术。 & T8 a: J8 ]5 ?+ d- i
今天先到这里吧!最好准备一份softice for 95/98和金山词霸,让我们先来分析一下别人是怎么做的。 2 O5 v9 [$ F+ k$ B+ l% U& ]
欢迎与我联系   p' `' }* w& P' N
E-Mail:[email protected]
( J/ a9 ~5 M. P1 @/ w! L% T主题  屏幕取词技术系列讲座(二)
0 W" I8 b: A4 z; m作者   亦东
* _& z4 h9 v; }2 z( y7 ^% s很抱歉让大家久等了! 5 V) T3 Z# P4 X) f) D; T
我看了一些人的回帖,发现很多人对取词的原理还是不太清楚。
5 f* J! Q4 f' g% L+ w* j' s: m) w首先我来解释一下hook问题。词霸中的确用到了hook,而且他用了两种hook其中一种是Windows标准hook,通过SetWindowHook安装一个回调函数,它安装了一个鼠标hook,是为了可以及时响应鼠标的消息用的和取词没太大关系。
( l1 x% o& W( w8 \$ C2 F" H! t, W" T- Y另一种钩子是API钩子,这才是取词的核心技术所在。他在TextOut等函数的开头写了一个jmp语句,跳转到自己的代码里。
) O- w" w4 o. n1 ?6 ^6 p你用softice看不到这个跳转语句是因为它只在取词的一瞬间才存在,平时是没有的。 3 ]% L" V2 `0 x4 r
你可以在TextOut开头设一个读写断点
$ L! e; V  j# U4 L; w) P8 Ebpm textout
6 _7 z, X; V% D% E: d" J再取词,就会找到词霸用来写钩子的代码了。
2 O6 ?8 x! |% v' K; V; r* p8 S/**********************************
: i/ R. c$ B7 N. ~+ G$ ?! z所以我在次强调,想学这种技术一定要懂汇编语言和熟练使用softice.
; X* f" x" S! h( `- A- t2 t: z0 X* i**********************************/
) P. \  O% F/ ^" b6 O: b+ _至于从cjktl95中dump出来的未公开函数是和Windows32/16混合编程有关的,以后我会提到他们。 ; r% [/ n: A7 D9 Q; r; w
我先来讲述取词的过程,
6 B# ~" W% J4 ~# ?8 C0 判断鼠标是否在一个地方停留了一段时间 " ^/ W% U+ v+ q: C
1 取得鼠标当前位置 " v5 ?4 o' r/ y' F
2 以鼠标位置为中心生成一个矩形
  c1 r5 ~2 c/ X( ^. b2 D3 挂上API钩子 + [6 P# ^# n2 j$ i- K# g# `
4 让这个矩形产生重画消息
& b- I0 J" l+ ~/ w5 J* [0 X5 在钩子里等输出字符
, C$ p7 L8 P: G. z7 V6 计算鼠标在哪个单词上面,把这个单词保存下来 3 w/ q& d9 U5 u8 [7 ]" l
7 如果得到单词则摘掉API钩子,在一段时间后,无论是否得到单词都摘掉API钩子 2 b' m2 a" v* Y$ Q+ v
8 用单词查词库,显示解释框。
! p! G. L7 t. x) `很多步骤实现起来都有一些难度,所以在中国可以做一个完善的取词词典的人屈指可数。 & f6 X9 W& ^" b  j
其中0,1,2,7,8比较简单就不提了。
, ~. _& F8 X! F. Z- `0 K, ]9 v+ d先说如何挂钩子:
. W) u0 c! s$ {# h( f所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句,跳转到自己的代码里。 ' d/ {6 d# w/ _8 D* z, ~
步骤如下:
! ]5 [) h4 S6 }9 R( O1.取得Windows API入口,用GetProcAddress实现 * u4 ~+ J! @1 V' W
2.保存API入口的前五个字节,因为JMP是0xEA,地址是4个字节 4 {( `+ |; a+ Q* |+ Z0 t
3.写入跳转语句 0 T% O5 A0 Z9 C9 L) I
这步最复杂
: l8 h' c, `" z$ }: E1 L. l, z7 xWindows的代码段本来是不可以写的,但是Microsoft给自己留了个后门。 + q2 g/ f4 Z6 h
有一个未公开函数是AllocCsToDsAlias, : `; j5 C  M: H8 E- n& i
UINT WINAPI ALLOCCSTODSALIAS(UINT); $ _7 T. l4 Y6 V- c/ C
你可以取到这个函数的入口,把API的代码段的选择符(要是不知道什么是选择符,就先去学学保护模式编程吧)传给他,他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。 + V4 _) ]  F2 I3 S, w
这就是取词技术的最核心的东东,不止取词,连外挂中文平台全屏汉化都是使用的这种技术。现在知道为什么这么简单的几句话却很少知道了吧?因为太多的产品使用他,太多的公司靠他赚钱了。 ( \, a( E2 O1 b2 J# l! B4 c: a6 ~( e
这些公司和产品有:中文之星,四通利方,南极星,金山词霸,实达铭泰的东方快车,roboword,译典通,即时汉化专家等等等等。。。。还有至少20多家小公司。他们的具体实现虽然不同,但大致原理是相同的。
1 Y" e9 T" q1 I9 [% g3 S% Z0 L. I+ r我这些都是随手写的,也没有提纲之类的东西,以后如果有机会我会整理一下,大家先凑合着看吧!xixi...
. W! e" G% U" C/ |+ ^8 z4 Q' ?8 Z? % v% H3 E: l( G% A9 R
主题  关于屏幕取词的讨论(三) * Y5 g7 b0 B5 \) ^# ?9 w
作者   亦东 5 i% V, z: ^% f7 R+ c2 n  F

& k2 P7 t( K, W* ^1 U7 d让大家久等,很抱歉,前些时候工作忙硬盘又坏了,太不幸了。 7 S& M  A2 E7 U- v8 U3 X" S4 T$ _" {
这回来点真格的。
/ v( n- c* U' o0 _( t2 i% i3 ]7 x' s' c咱们以截取TextOut为例。 , \; \  N7 `' }! P+ t  `5 m
下面是代码:
+ J# W1 ~# ~/ G//截取TextOut
7 e* }4 y/ x0 ]! x1 F3 Q, Btypedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT); 6 o1 m7 s/ U% F+ ~& Y. _3 l& E
ALLOCCSTODSALIAS AllocCsToDsAlias;
  i( Z  K% d1 L" c: U2 l3 C) HBYTE Newvalue[5];//保存新的入口代码 4 U# v0 w' `4 B! x7 }
BYTE Oldvalue[5];//API原来的入口代码
( g6 I3 E; a9 K+ ?, p9 V7 tunsigned char * Address=NULL;//可写的API入口地址 ; v& n, l- x" r6 t
UINT DsSelector=NULL;//指向API入口的可写的选择符
2 k* z; @9 q% A1 `8 ~WORD OffSetEntry=NULL;//API的偏移量
  w  e3 [% k" W; e+ D5 ?1 |BOOL bHookAlready = FALSE; //是否挂钩子的标志
5 ]! O+ f" \# C; xBOOL InitHook()
+ ]' c2 ~) w2 b' g{ " q8 J  f7 }! R% F8 a( G7 q+ `
HMODULE hKernel,hGdi; 7 P( u3 `4 [7 o
hKernel = GetModuleHandle("Kernel"); 7 p. K- A5 Q0 I9 H; p9 Y0 H
if(hKernel==NULL) " G7 U1 E2 K  k. E/ l) u
return FALSE;
# k- j+ Y- b2 Q$ a) o. P  I) HAllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//这是未公开的API所以要这样取地址 ) n+ R" H! E) ~' N! j
if(AllocCsToDsAlias==NULL)
  n) u/ P3 E3 z5 z- ~( lreturn FALSE;
3 F; F! O0 j. m6 W8 W; ^hGdi = GetModuleHandle("Gdi"); # `, l5 N2 j* J3 U
if(hmGdi==NULL) + z! @/ A" ]- s- U. |4 w1 d
return FALSE;
( E  n/ P$ [: N6 F+ g" s: XFARPROC Entry = GetProcAddress(hGdi,"TextOut"); % j! f0 q. Y) n( N7 C
if(Entry==NULL)
+ x, k" W# y1 a0 creturn FALSE; - w& i( f% N! a* m, M+ ~
OffSetEntry = (WORD)(FP_OFF(Entry));//取得API代码段的选择符
. S/ H; o- N0 j) N/ DDsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配一个等同的可写的选择符
5 R1 Y! G6 _5 k  P* P; _6 T, }% A; _$ l. BAddress = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址 % B! d7 r; z- a1 G
Newvalue[0]=0xEA; " M9 O+ M# ^8 [3 z% u! H- W- h9 l
*((DWORD*)(Newvalue+1)) = (DWORD)MyTextOut; 8 o. K) I3 `; p" c
Oldvalue[0]=Address[0]; : K7 _/ e: `9 c9 ?- B2 I# v
*((DWORD*)(Oldvalue+1)) = *((DWORD*)(Address+1)); ; \* U8 o" G% l& Y# ]/ n
} 4 |4 a4 g5 X8 c
BOOL ClearHook() & j( R5 R3 _6 c4 ]9 p% d7 k: D" K
{
- D9 I# [4 u) N# w& p( n3 o- `if(bHookAlready)
1 {+ W  Z; \7 cHookOff(); / ]0 }8 u" I1 W& p' R
FreeSelector(DsSelector);
, e# b$ R+ V7 p* z/ e6 C- R0 a8 u}
( J9 |7 M9 t1 K( [& rBOOL HookOn() + [/ b0 e- Y) ~7 {3 T
{ 0 w) j3 [4 D( R2 d! E, V
if(!bHookAlready){
- O. p2 u9 E* M" Z6 x: Ifor(int i=0;i<5;i++){
8 d, N4 r. X% q( o' N! P' |Address=Newvalue; ( y1 @+ n0 a" J+ j( f. Q# G+ u1 B
}
2 E. Y) c1 ]; a* e$ B& {bHookAlready=TRUE; / s9 [, s4 s1 {+ a4 x9 W; i2 p
}
! O( G& t$ z+ y2 E} 2 b4 ^7 i0 i2 E  G5 X  f( P
BOOL HookOff() ( e/ r# o$ k) P
{ # b( r. I' Z% h6 h, o  o7 j
if(bHookAlready){ 7 T2 q, n) J5 X& K+ M) u
for(int i=0;i<5;i++){ % {  f2 F4 m( w6 Y
Address=Oldvalue;
% v* _$ o7 J$ l1 U$ K% [( Q! H- B7 q}
7 h' [  _* o4 I# z# KbHookAlready=FALSE;
8 F  B8 x  F  @$ O8 B} " i+ {. O( b9 A4 L
}
3 V; B8 w7 k% |$ }# J//钩子函数,一定要和API有相同的参数和声明 4 m- G$ g/ f9 O
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString) 2 ?+ J( R/ ]; `$ M: ?
{ 3 d8 w- w! e4 ^; @1 y5 p# G6 x3 \
BOOL ret; & p& f% W/ l* @3 V2 f# H1 ~! z$ _4 V9 h
HookOff(); - \6 \4 |9 H9 F: O
ret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//调原来的TextOut
2 d7 R6 @! \2 u4 CHookOn(); 4 I8 D  E: i6 A7 u7 ~; p; b9 d) h
return ret; 9 [2 Z9 X5 Y0 v# ^, \/ l# Q6 ~
}
5 z8 g' M8 L) n, E+ Z上面的代码是一个最简单的挂API钩子的例子,我要提醒大家的是,这段代码是我凭记忆写的,我以前的代码丢了,我没有编译测试过
( |2 w/ d$ @/ l6 K% T% c1 P3 G; ?因为我没有VC++1.52.所以代码可能会有错。 ' y* n3 g7 K" ?; D3 z& U. Y  ?
建议使用Borland c++,按16位编译。
/ _8 G- {4 Q/ H( w如果用VC++1.52,则要改个选项 9 w1 m- {. _2 N4 f, M
在VC++1.52的Option里,有个内存模式的设置,选大模式,和"DS!=SS DS Load on Function entry.",切记,否则会系统崩溃。
3 s( Y4 O3 i8 q$ Q有什么不明白的可以给我写信 * S" r% D5 S9 i. ?
[email protected]
) R2 E. ?9 }) UWednesday, June 23, 2004 7:11 PM ' L- {. G& Z. ^7 I, ^

" N, L* q! p- `% r, e
分享到:  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日,是全球华人共同的冒险解谜类游戏家园。我们致力于提供各类冒险游戏资讯供大家学习交流。本站所有资源均不用于商业用途。

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