冒险解谜游戏中文网 ChinaAVG

标题: 【汉化资料】金山词霸”屏幕取词技术揭密 [打印本页]

作者: shane007    时间: 2008-11-9 09:53
标题: 【汉化资料】金山词霸”屏幕取词技术揭密
对于一些调用Windows标准API来进行文本输出的AVG游戏,也许可以借鉴金山词霸之类的屏幕取词技术,HOOK API 之后,在自己的函数里把截获的英文替换为中文,然后输出到屏幕上来实现汉化,而且,这样的工具一旦制作完成,对于这类的AVG游戏将是通用的。- U" |! X; ^! ^/ ?" d6 H0 Q4 y

0 W0 m6 }5 [3 o9 s% m4 ?. i
5 S& t* i* S% g: J金山词霸”屏幕取词技术揭密(讨论稿) 4 \1 D1 w! _# i; c$ N
这篇文章最早是发在北极星论坛的一系列帖子,那时候闻怡洋(好像他也是MVP)也在那里混 0 D7 F7 d4 K  @" c" z5 F7 I: \
原始的帖子我已经没有了,但不知道是谁帮我收集整理了下来(非常感谢),我用google找到了
0 R; V! s- _1 `4 g?
. A- F/ H: F/ ~- ~- H7 M" g这是我进金山之前写的,应该不算泄露公司技术秘密吧
+ q' K% K! c6 B1 T而且这些现在看来似乎已经有些过时了
- ?$ v, S# U- H& I8 }% \) B; _? 4 n  {; W* [* C& P2 V5 B
那时讨论的只是Win31和Win9x下的取词实现
4 r1 t! }4 x" F1 ]- T? 9 F- H% m$ {1 e+ y  U
我到了金山之后不是负责取词模块,而是做UI,因为有个家伙比我更擅长做这种东西
. ^3 u4 v! n, \& U  P他用SoftIce调试汇编代码非常熟练,做逆向工程方面有过人的天分。
+ U0 R9 ?+ E& \) {3 Z5 X?
2 U! i9 x4 i( Z" }  Z# W? / N" B& \3 o8 E4 `+ J0 r" v8 U. U
“亦东” 是我那时的笔名 * A0 b7 W$ s( r, a1 e" _0 ?
? + z$ g& G- I, u7 B8 j
? 6 n, q$ w+ M0 s0 f! I+ \" p. ^
“金山词霸”屏幕取词技术揭密(讨论稿)
8 o( ~( n9 D" Y. w- `& B) W/ V?
" o" U' ^2 R: Y主题  屏幕取词技术系列讲座(一)
: [6 l; _; _& x作者   亦东
3 v! L7 R5 U' C4 T6 N! B0 z很多人对这个问题感兴趣。 " x# v) V- |5 }1 E' B$ J0 s
原因是这项技术让人感觉很神奇,也很有商业价值。
4 u$ m1 L+ U: M5 g2 q+ z' `7 K现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。
5 H  h! i+ g; R但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。
% ]& y1 S1 q$ s2 ?大约每周一两次。想知道的人就常常来看看吧!
$ B1 z& `0 u* J  f" t一.基础知识
6 n+ ]  T" G4 u首先想编这种程序需要一些基础知识。 * N; P+ T* |5 v, z" k6 T) [$ [8 K
会用Vc++,包括16/32位。
8 A/ W. U$ p) o9 N- N% y精通Windows API特别是GDI,KERNEL部分。 * u2 {1 u$ }3 C7 R4 ?7 {
懂汇编语言,会用softice调试程序,因为这种程序最好用softice调试。
, c3 p4 q0 G2 S0 E* ~二.基本原理 5 `: `5 @4 T5 H( \( L3 p% ?4 }
在Window 3.x时代,windows系统提供的字符输出函数只有很少的几个。 ( v# z1 I  q) q: i+ b( q" {3 W# q: X
TextOut " L' B$ b8 w* l$ C: W6 H+ K
ExtTextOut
5 S8 e, _! t( y4 [# m) ODrawText
: o! W4 [/ W  A4 g1 `/ ~" O...... 0 D, X* p$ C4 D6 X! R& K" O' {7 e
其中DrawText最终是用ExtTextOut实现的。
" \' n2 F* C( ~& C) a所以Windows的所有字符输出都是由调用TextOut和ExtTextOut实现的。因此,如果你可以修改这两个函数的入口,让程序先调用你自己的一个函数再调用系统的字符输出,你就可以得到Windows所有输出的字符了。
+ a) ]9 p/ D3 c) q: x  u到了Windows95时代,原理基本没变,但是95比3.x要复杂。开始的时候,一些在windows3.x下编写的取词软件仍然可以是使用。但是后来出了个IE4,结果很多词典软件就因为不支持IE4而被淘汰了,但同时也给一些软件创造了机会,如金山词霸。其实IE4的问题并不复杂,只不过它的输出的是unicode字符,是用TextOutW和ExtTextOutW输出的。知道了这一点,只要也截取就可以了。不过实现方法复杂一点,以后会有详细讲解。现在又出了个IE5,结果词霸也不好用了,微软真是#^@#$%$*&^&#@#@..........
' t4 `/ _! `. j7 b; _. _我研究后找到了一种解决办法,但还有些问题,有时会取错,正在继续研究,希望大家共同探讨。 5 O8 [8 X/ {6 j) A0 a6 u) \8 q8 S
另外还有WindowsNT,原理也是一样,只是实现方法和95下完全不同。 ! {: Q, I8 V( C# k& \
三.技术要点 8 p! [7 U3 t( V& n9 ?* R0 A
要实现取词,主要要解决以下技术问题。 7 ^4 U3 y' `- q
1.截取API入口,获得API的参数。
& C$ j( ]! R# ?% E3 F  o4 g2.安全地潜入Windows内部,良好地兼容Windows的各个版本 0 Z/ F8 s2 P" X  p" T9 t1 E: p9 g
3.计算鼠标所在的单词和字母。 0 X; i( m* W) e/ ~! n/ K+ s
4.如果你在Window95下,做32位程序,还涉及Windows32/16混合编程的技术。
- Z! k+ X% o! x- ~今天先到这里吧!最好准备一份softice for 95/98和金山词霸,让我们先来分析一下别人是怎么做的。 6 `  G  c3 ~5 u0 S2 ^8 Q
欢迎与我联系
# X# M3 s& ?1 ~+ _% n1 v6 ~E-Mail:[email protected]
# d, r( m( B* z1 `  f9 V' h; w主题  屏幕取词技术系列讲座(二) 6 u4 ^# q% c3 E+ Z7 f' i) j' W
作者   亦东 0 ^$ Y* ]+ e6 u7 j) q% e5 r
很抱歉让大家久等了! ! y& D  o7 X) _' ?$ B2 S
我看了一些人的回帖,发现很多人对取词的原理还是不太清楚。 4 v/ H  V# P% G1 F, \& d2 c8 m
首先我来解释一下hook问题。词霸中的确用到了hook,而且他用了两种hook其中一种是Windows标准hook,通过SetWindowHook安装一个回调函数,它安装了一个鼠标hook,是为了可以及时响应鼠标的消息用的和取词没太大关系。 - c+ g% ^% Y% c& d3 Y# K# k1 n: j, ^' y
另一种钩子是API钩子,这才是取词的核心技术所在。他在TextOut等函数的开头写了一个jmp语句,跳转到自己的代码里。
2 [8 S9 ?1 w2 S. N你用softice看不到这个跳转语句是因为它只在取词的一瞬间才存在,平时是没有的。
3 u) W& q( i  g1 U& g' p* }0 Z你可以在TextOut开头设一个读写断点 + y8 B$ u+ `/ z6 d, I+ \: e) h
bpm textout & s0 ~! k& {5 L: @9 v/ h
再取词,就会找到词霸用来写钩子的代码了。
# k, A& v# R, F* w/********************************** $ l, s. K( l7 X. g7 A. \5 v
所以我在次强调,想学这种技术一定要懂汇编语言和熟练使用softice.
  o. ?2 p( a2 F9 _% v( j" V**********************************/ $ |! Y  Q: \4 d' m$ Z% H
至于从cjktl95中dump出来的未公开函数是和Windows32/16混合编程有关的,以后我会提到他们。
" T% V5 N2 r% m/ f7 G4 N& B我先来讲述取词的过程, + o3 w) q8 X$ T# ^( l
0 判断鼠标是否在一个地方停留了一段时间
2 e5 y; P( g+ k& u3 ^6 J1 u1 取得鼠标当前位置
* r' o9 e2 R" v# H2 以鼠标位置为中心生成一个矩形 ; v! @' h) i7 ]/ y: Z' u/ M2 V2 D
3 挂上API钩子
$ y( I3 J% t9 K1 J4 让这个矩形产生重画消息
$ q5 m# B/ I  K% y! b0 M, |4 \8 k5 在钩子里等输出字符
# p0 K+ y4 j" y6 计算鼠标在哪个单词上面,把这个单词保存下来 2 V; t& K/ R% s+ J$ Q: T4 a) _0 M
7 如果得到单词则摘掉API钩子,在一段时间后,无论是否得到单词都摘掉API钩子 ! q" ]1 W( b- R. D4 f' p( n
8 用单词查词库,显示解释框。
+ F; V! T& i* x" \$ y/ s/ G5 T很多步骤实现起来都有一些难度,所以在中国可以做一个完善的取词词典的人屈指可数。 6 ]+ V6 v& F9 T# B8 i1 C
其中0,1,2,7,8比较简单就不提了。
( O9 S/ [% V0 T1 Q5 k( ]/ g1 Y先说如何挂钩子:
8 m/ I2 Z, s9 ?# M: m2 y2 D所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句,跳转到自己的代码里。 7 N; E/ r3 l1 A* J/ c" w
步骤如下: ; m. {0 P$ O. J/ _0 ^. f# T
1.取得Windows API入口,用GetProcAddress实现   u2 s5 t: i* E& E
2.保存API入口的前五个字节,因为JMP是0xEA,地址是4个字节
9 Z! y& c6 s* @% ]' S. d4 q1 P) y3.写入跳转语句 2 G9 R& s2 q! d5 |' T3 ?$ p
这步最复杂
  h: K+ p; M3 SWindows的代码段本来是不可以写的,但是Microsoft给自己留了个后门。 7 F7 Q% u2 A) I' f$ C) W
有一个未公开函数是AllocCsToDsAlias,
9 q8 r( G9 R2 hUINT WINAPI ALLOCCSTODSALIAS(UINT);
* M* m$ j1 ]4 C% m* P- E1 @你可以取到这个函数的入口,把API的代码段的选择符(要是不知道什么是选择符,就先去学学保护模式编程吧)传给他,他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。
$ A6 E, t2 g5 ]7 u3 g( r这就是取词技术的最核心的东东,不止取词,连外挂中文平台全屏汉化都是使用的这种技术。现在知道为什么这么简单的几句话却很少知道了吧?因为太多的产品使用他,太多的公司靠他赚钱了。
6 J( u- P& K8 E1 b! v. S# ~这些公司和产品有:中文之星,四通利方,南极星,金山词霸,实达铭泰的东方快车,roboword,译典通,即时汉化专家等等等等。。。。还有至少20多家小公司。他们的具体实现虽然不同,但大致原理是相同的。
- H, v+ `- N: P* j, ]' ^我这些都是随手写的,也没有提纲之类的东西,以后如果有机会我会整理一下,大家先凑合着看吧!xixi... 4 ^+ M! S8 }, ^
?
0 x) p9 U8 P: l- x1 i# j6 J主题  关于屏幕取词的讨论(三)
% E# p' N+ m0 k! f2 [0 c作者   亦东 " J, }7 \7 \; {; n/ t# `
& X2 z5 b. H0 C; a# B: ~
让大家久等,很抱歉,前些时候工作忙硬盘又坏了,太不幸了。 * c/ [2 D' C0 Y7 B& C! v! ~
这回来点真格的。
) R* u- c0 Y$ ?* h0 R咱们以截取TextOut为例。
9 v  E0 Q3 g, h0 V下面是代码: % ^6 i6 q: C/ z, T/ \) q& }# f
//截取TextOut
; u4 p! n6 s/ Q& m0 U- r0 }typedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT); 9 C3 Q! D+ A& L  G9 z6 m
ALLOCCSTODSALIAS AllocCsToDsAlias; ' Q9 I* d( f! G' Z- O4 }+ ]9 j
BYTE Newvalue[5];//保存新的入口代码 ' S5 f2 Y! `9 n& R! H+ M% ]
BYTE Oldvalue[5];//API原来的入口代码 - p; j: ?. ?3 b
unsigned char * Address=NULL;//可写的API入口地址
1 d. F; g$ R6 p( _7 p# EUINT DsSelector=NULL;//指向API入口的可写的选择符
- f' M! U9 w, s4 pWORD OffSetEntry=NULL;//API的偏移量
* U& D- \" C$ D9 j- C& c/ u: }BOOL bHookAlready = FALSE; //是否挂钩子的标志
- `7 R3 f. ]3 P4 k/ f9 GBOOL InitHook()
3 i- g9 u2 `$ K  F- Y{
8 ?# O. ?$ p( g1 p) k$ ?% H/ eHMODULE hKernel,hGdi;
2 C" c) @$ M! d% a; x' g3 A4 E5 ihKernel = GetModuleHandle("Kernel"); % I3 [' h9 h* I3 a6 h+ G
if(hKernel==NULL) 1 t$ h4 J% e, u4 J) }  F
return FALSE;
( C- [$ i& s- P$ @* Q1 |4 a. }, JAllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//这是未公开的API所以要这样取地址 / c& K$ f1 g0 @! G# A7 S
if(AllocCsToDsAlias==NULL)
+ M% r% ~1 _3 P) _( t9 |% Q( Jreturn FALSE; 6 r1 X$ @, J: b& H7 Q
hGdi = GetModuleHandle("Gdi"); / x( _0 |( h+ s
if(hmGdi==NULL) 1 X1 s7 v' |8 h1 z0 I! e
return FALSE; : G. J( x" b. b0 @3 V
FARPROC Entry = GetProcAddress(hGdi,"TextOut"); 4 s: Q+ P# _* x( G9 L1 q. j7 O- a
if(Entry==NULL)
) m- m1 Y* u% p  Qreturn FALSE;
1 L$ r5 W3 S! gOffSetEntry = (WORD)(FP_OFF(Entry));//取得API代码段的选择符
: n! y6 t8 J* }( p! {DsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配一个等同的可写的选择符   V2 q+ u, D2 m4 [; \* {, y/ T" z
Address = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址 9 n7 _4 j* F2 b
Newvalue[0]=0xEA; ' C, _2 A4 x  f/ Z7 Q% Y$ n. F
*((DWORD*)(Newvalue+1)) = (DWORD)MyTextOut;   i1 }' F6 }3 {8 w5 D- K6 G
Oldvalue[0]=Address[0]; - ~( |8 j: o" _6 x4 l6 [
*((DWORD*)(Oldvalue+1)) = *((DWORD*)(Address+1));
( i" A3 P0 U8 E} ) o: u( I1 ^* N- }% ^5 v4 w
BOOL ClearHook()
8 i3 d, s, s% l/ Q8 H{
7 X7 |, L0 J6 O6 vif(bHookAlready) 3 N9 [9 |* b; n( W" V
HookOff();
, s# t0 y2 n& [& l3 [, EFreeSelector(DsSelector); & J& W. D9 _3 S% N0 T4 `) l
}
! C$ O8 [" b  R& Z- ~+ xBOOL HookOn()
0 i6 `1 T9 O, o% Z; ]- n' {9 N5 z{
, K4 E& L7 h1 Y1 Dif(!bHookAlready){
0 z: ?! D9 j+ ?8 x( V- Zfor(int i=0;i<5;i++){
; Q3 n# x2 c: j! pAddress=Newvalue; 8 O' k7 v5 P0 t  I5 T7 o
}
! `, p, u, i9 U5 ], h' ^/ \bHookAlready=TRUE; ! M$ @3 Q/ U0 W7 M3 B
} 7 T0 H% h# Q1 z8 Y) s! H
}
4 [# [" v/ r4 d' ]) c5 XBOOL HookOff() 5 p+ \4 A/ u/ a; D2 k
{ * Z" R% H3 D' v7 ]; l
if(bHookAlready){ - f; r, I* s" q; H
for(int i=0;i<5;i++){ ; q4 r% i3 S9 r- ]: X0 o4 h9 f
Address=Oldvalue; : ]) s. X1 `  O& m8 h2 {
}
' b: s  d( m/ @- i5 RbHookAlready=FALSE; ' n* u5 ?8 W7 a4 E9 N2 }$ v  N
} 8 q4 y/ J' n( y3 Z6 D6 `+ b0 {$ @. A
}
8 s% b' O$ V8 C//钩子函数,一定要和API有相同的参数和声明 ; L% `1 \6 v, S( n; q
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString) # K$ o% i/ Q5 {) w5 M0 `8 q2 b/ i
{ 8 w# K3 J" r7 \1 L8 n" o' Y9 v+ w
BOOL ret; 3 p( I" t" y7 s% P, A: I
HookOff(); 3 L) z$ F7 p# H& j: y, i
ret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//调原来的TextOut 6 _$ W, b0 P! x" B
HookOn();
* @% h% w+ [) c$ E& greturn ret; 8 d6 I; O( ~) l' p! ^6 y
} 7 S. R) K; o8 T6 A
上面的代码是一个最简单的挂API钩子的例子,我要提醒大家的是,这段代码是我凭记忆写的,我以前的代码丢了,我没有编译测试过 ) b0 @5 b* r. B5 o3 o
因为我没有VC++1.52.所以代码可能会有错。 3 d" {, \" Q1 b' A/ R( g
建议使用Borland c++,按16位编译。 ) c+ C) r" ]; w' d8 t
如果用VC++1.52,则要改个选项
4 f1 r' i, t% `0 o1 `在VC++1.52的Option里,有个内存模式的设置,选大模式,和"DS!=SS DS Load on Function entry.",切记,否则会系统崩溃。 1 U: ?; ?* s% P
有什么不明白的可以给我写信
7 B3 }& p; \. c& T. b[email protected] : ~. j8 j& I* R" p0 L
Wednesday, June 23, 2004 7:11 PM $ d0 S) _+ ?1 `1 q/ N6 r
! x+ I$ a7 ^# m  a

作者: liunan7080    时间: 2008-11-9 09:57
[s:6] 高深莫测




欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://chinaavg.com/) Powered by Discuz! X3.2