冒险解谜游戏中文网 ChinaAVG
标题:
【汉化资料】金山词霸”屏幕取词技术揭密
[打印本页]
作者:
shane007
时间:
2008-11-9 09:53
标题:
【汉化资料】金山词霸”屏幕取词技术揭密
对于一些调用Windows标准API来进行文本输出的AVG游戏,也许可以借鉴金山词霸之类的屏幕取词技术,HOOK API 之后,在自己的函数里把截获的英文替换为中文,然后输出到屏幕上来实现汉化,而且,这样的工具一旦制作完成,对于这类的AVG游戏将是通用的。
9 [ D; M2 s) H
: ^' c* r: V0 M1 |5 i
8 e$ c' c# X+ E8 s' c2 Z5 a
金山词霸”屏幕取词技术揭密(讨论稿)
) t. y3 t6 N _9 g( K. n
这篇文章最早是发在北极星论坛的一系列帖子,那时候闻怡洋(好像他也是MVP)也在那里混
/ D$ h6 j1 f8 d
原始的帖子我已经没有了,但不知道是谁帮我收集整理了下来(非常感谢),我用google找到了
0 I9 ?1 K8 L$ c1 y0 u& W
?
$ u' ]7 k k9 S/ G! [2 `
这是我进金山之前写的,应该不算泄露公司技术秘密吧
* B" I7 v Y8 w) @4 L* ]
而且这些现在看来似乎已经有些过时了
+ v3 g8 r9 H# t& m1 B
?
! R* A2 o7 N' Q& Q. l
那时讨论的只是Win31和Win9x下的取词实现
N7 o N2 h) J' I7 F0 R
?
) T( a/ z# Z: H3 n* H
我到了金山之后不是负责取词模块,而是做UI,因为有个家伙比我更擅长做这种东西
. ^# U [, p( M3 z# l1 b( q7 l
他用SoftIce调试汇编代码非常熟练,做逆向工程方面有过人的天分。
, M, U- j' e% I
?
% V: V3 @& ], C* s6 b/ Q
?
( x1 g0 ]* c9 ?/ V: s0 n) d
“亦东” 是我那时的笔名
+ q; p. ?( j" i* ], t d( C
?
0 h- D& m; s' |0 S# q* r
?
" j/ D3 c) D5 j% Z( ^) g# y
“金山词霸”屏幕取词技术揭密(讨论稿)
; _- N2 Z6 U# R& ]4 S
?
, N0 G u; M: J
主题 屏幕取词技术系列讲座(一)
( j5 {6 h1 W+ |$ z+ N* [! g; r8 K" q! A" F
作者 亦东
& L ?4 A, O- D$ ~6 x0 S
很多人对这个问题感兴趣。
/ ~( w) u8 J# M2 }: Q% n
原因是这项技术让人感觉很神奇,也很有商业价值。
) F1 Q$ ]9 _5 }) G) ?: e# N$ j
现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。
8 f) Z ^0 H* B# E
但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。
2 G! q: r/ x0 f( U( J
大约每周一两次。想知道的人就常常来看看吧!
U2 |$ l+ Q: X |! y3 L" G2 K
一.基础知识
, q, q9 S% V" U( \5 x
首先想编这种程序需要一些基础知识。
# @) X v3 F+ v: `& N
会用Vc++,包括16/32位。
6 J+ v* R9 h: x# `! ^
精通Windows API特别是GDI,KERNEL部分。
% T$ U$ e8 B% f/ V1 v
懂汇编语言,会用softice调试程序,因为这种程序最好用softice调试。
2 }: x- [: p) @8 m
二.基本原理
" q8 g# x5 b) u$ k
在Window 3.x时代,windows系统提供的字符输出函数只有很少的几个。
: W' ^6 w, b$ Q- R
TextOut
% _0 A! B0 x2 D7 d! P8 e
ExtTextOut
- t: E }( Q; r/ s9 J
DrawText
) E3 A0 V, t, l9 R) U% m* D- F$ g
......
' D0 `8 a% x- c
其中DrawText最终是用ExtTextOut实现的。
: H# h( e: y8 _7 D# I" A+ K
所以Windows的所有字符输出都是由调用TextOut和ExtTextOut实现的。因此,如果你可以修改这两个函数的入口,让程序先调用你自己的一个函数再调用系统的字符输出,你就可以得到Windows所有输出的字符了。
+ @' Z7 i: r4 M( c' u3 w V
到了Windows95时代,原理基本没变,但是95比3.x要复杂。开始的时候,一些在windows3.x下编写的取词软件仍然可以是使用。但是后来出了个IE4,结果很多词典软件就因为不支持IE4而被淘汰了,但同时也给一些软件创造了机会,如金山词霸。其实IE4的问题并不复杂,只不过它的输出的是unicode字符,是用TextOutW和ExtTextOutW输出的。知道了这一点,只要也截取就可以了。不过实现方法复杂一点,以后会有详细讲解。现在又出了个IE5,结果词霸也不好用了,微软真是#^@#$%$*&^&#@#@..........
) B" V$ \2 l, I7 Q! H' t
我研究后找到了一种解决办法,但还有些问题,有时会取错,正在继续研究,希望大家共同探讨。
' }6 r5 _3 {5 B; I3 g0 O
另外还有WindowsNT,原理也是一样,只是实现方法和95下完全不同。
5 h, F- z* s! O$ |" i
三.技术要点
1 ^' W! [8 ^3 h2 [, N6 C* c
要实现取词,主要要解决以下技术问题。
* e+ E' S( q7 n4 f
1.截取API入口,获得API的参数。
6 [) S# F H2 l3 }& w
2.安全地潜入Windows内部,良好地兼容Windows的各个版本
$ t1 z, s, o0 c3 K. }6 D* m2 F
3.计算鼠标所在的单词和字母。
+ G* v& t4 S1 _
4.如果你在Window95下,做32位程序,还涉及Windows32/16混合编程的技术。
0 b# R% b* o. l6 C
今天先到这里吧!最好准备一份softice for 95/98和金山词霸,让我们先来分析一下别人是怎么做的。
, k, T4 M9 c9 v" l% D: B
欢迎与我联系
3 S0 ?/ T9 B' [$ n( {1 h
E-Mail:
[email protected]
+ V7 }$ n# {* k% I9 I
主题 屏幕取词技术系列讲座(二)
$ x/ R k3 z) t0 B
作者 亦东
7 J! a3 z) s) D- T5 F& \: x0 Y
很抱歉让大家久等了!
( Z4 N; e4 ~% R: Q3 |7 s/ L! Y$ T
我看了一些人的回帖,发现很多人对取词的原理还是不太清楚。
/ k1 o' W. L' l$ S9 K; Z0 Q0 d8 p
首先我来解释一下hook问题。词霸中的确用到了hook,而且他用了两种hook其中一种是Windows标准hook,通过SetWindowHook安装一个回调函数,它安装了一个鼠标hook,是为了可以及时响应鼠标的消息用的和取词没太大关系。
& w; k" M2 A$ b# X1 M
另一种钩子是API钩子,这才是取词的核心技术所在。他在TextOut等函数的开头写了一个jmp语句,跳转到自己的代码里。
; e' V. z r: A
你用softice看不到这个跳转语句是因为它只在取词的一瞬间才存在,平时是没有的。
: u1 b- Z# U4 C* W+ r, V
你可以在TextOut开头设一个读写断点
r7 B8 S7 Z1 K t" T. v3 b2 }
bpm textout
a) p* {- ?) K5 Z' l! i
再取词,就会找到词霸用来写钩子的代码了。
9 E% E$ A. Q' X; T
/**********************************
4 a+ m. m8 U8 A2 ~# f
所以我在次强调,想学这种技术一定要懂汇编语言和熟练使用softice.
3 C# {) |. P- O, o9 J) g
**********************************/
' c: C, Q& ]- Z8 B; a* ^
至于从cjktl95中dump出来的未公开函数是和Windows32/16混合编程有关的,以后我会提到他们。
6 s' B8 G9 F1 r0 {
我先来讲述取词的过程,
. t6 F4 g. D* V& y f1 E) W
0 判断鼠标是否在一个地方停留了一段时间
- n. S" L) l* r6 G
1 取得鼠标当前位置
! X2 u, w) y U7 B
2 以鼠标位置为中心生成一个矩形
$ N8 P' Y; x) _# D) C+ T2 T" m
3 挂上API钩子
$ M" Z" D& R3 x# E) A* @5 L. |
4 让这个矩形产生重画消息
3 s+ B, C+ T- t% ^. G7 h
5 在钩子里等输出字符
: L3 d- ^6 a; N- P+ N
6 计算鼠标在哪个单词上面,把这个单词保存下来
; Y; a- j6 b6 v- a2 T) v# ?( q; R
7 如果得到单词则摘掉API钩子,在一段时间后,无论是否得到单词都摘掉API钩子
. A, I" u! d6 E8 K" P
8 用单词查词库,显示解释框。
' W) ~4 O& Z7 ?* q# W. A
很多步骤实现起来都有一些难度,所以在中国可以做一个完善的取词词典的人屈指可数。
* T) |0 U# A# ?
其中0,1,2,7,8比较简单就不提了。
' S8 v9 |- ^ q: q! m
先说如何挂钩子:
$ A. W0 }* l) \" G$ ^1 k
所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句,跳转到自己的代码里。
+ J( k6 q/ O+ @. p; v; T5 Y: t
步骤如下:
6 T0 y! Z+ U7 w8 L- j* G
1.取得Windows API入口,用GetProcAddress实现
J5 h' N2 k4 ?8 i$ U4 }% `
2.保存API入口的前五个字节,因为JMP是0xEA,地址是4个字节
3 Z2 c3 J& l' x, H& @7 L, N
3.写入跳转语句
" t. ?' H. Z) j' b1 l
这步最复杂
$ B% B7 Z8 B/ a
Windows的代码段本来是不可以写的,但是Microsoft给自己留了个后门。
7 O: a1 j1 t+ o* H
有一个未公开函数是AllocCsToDsAlias,
, v, J! u+ g- Z) p5 z. v$ }
UINT WINAPI ALLOCCSTODSALIAS(UINT);
1 Y3 n$ I% G6 y5 }. p5 _* R. ?
你可以取到这个函数的入口,把API的代码段的选择符(要是不知道什么是选择符,就先去学学保护模式编程吧)传给他,他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。
8 ^% u' J( M! V0 J( x, l; `
这就是取词技术的最核心的东东,不止取词,连外挂中文平台全屏汉化都是使用的这种技术。现在知道为什么这么简单的几句话却很少知道了吧?因为太多的产品使用他,太多的公司靠他赚钱了。
" h. V: f1 d; N
这些公司和产品有:中文之星,四通利方,南极星,金山词霸,实达铭泰的东方快车,roboword,译典通,即时汉化专家等等等等。。。。还有至少20多家小公司。他们的具体实现虽然不同,但大致原理是相同的。
( s" Z( y9 z) I$ }5 D
我这些都是随手写的,也没有提纲之类的东西,以后如果有机会我会整理一下,大家先凑合着看吧!xixi...
% P) ?) R$ s$ `- Y5 ~1 x6 u/ D
?
+ M8 r# e, ~. u+ c1 I2 F
主题 关于屏幕取词的讨论(三)
- v8 D; ^/ y+ [( z: l* L; {- l2 e3 S
作者 亦东
& H1 v5 A% u5 P- H! n
! X, k3 A" [+ D4 P7 |# b5 Y. {
让大家久等,很抱歉,前些时候工作忙硬盘又坏了,太不幸了。
& {! }0 U5 q: I' X% V/ m
这回来点真格的。
I4 R' ^) c- X* e1 `
咱们以截取TextOut为例。
2 H8 S e) M( H) k- L
下面是代码:
7 E/ ^2 c7 q/ m+ ^- o
//截取TextOut
9 ~" S7 K* C2 t1 E, u( i4 f3 `
typedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT);
4 l4 P* E9 l$ Z" V/ P3 R- Q2 p+ T
ALLOCCSTODSALIAS AllocCsToDsAlias;
/ S$ l+ x7 n+ A7 t/ @
BYTE Newvalue[5];//保存新的入口代码
3 P- k k" M* k' T, x
BYTE Oldvalue[5];//API原来的入口代码
" w7 g8 ?& n5 {6 L
unsigned char * Address=NULL;//可写的API入口地址
& S8 x }& I0 q; R! G
UINT DsSelector=NULL;//指向API入口的可写的选择符
8 `# b5 w' g; Q" V# Q
WORD OffSetEntry=NULL;//API的偏移量
! n2 D9 `- M9 G
BOOL bHookAlready = FALSE; //是否挂钩子的标志
1 `6 g6 I8 z3 i; Q n2 D
BOOL InitHook()
: R+ p+ A+ L1 |; n, `2 A
{
+ x/ a3 b7 t4 a) R [+ I
HMODULE hKernel,hGdi;
4 W+ v. T. d! l b( \: N; `
hKernel = GetModuleHandle("Kernel");
8 q$ j8 e; E, o: ^
if(hKernel==NULL)
( x8 i# V& v: Q! d2 f" u" X2 y
return FALSE;
4 |3 l8 k* S% O4 r0 r
AllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//这是未公开的API所以要这样取地址
2 a2 u+ D- W0 g7 l5 A: Q0 a8 K% [, q
if(AllocCsToDsAlias==NULL)
e9 E* e3 x% d# T1 _
return FALSE;
) S1 T" ~7 x; |6 M2 y7 `' [5 f
hGdi = GetModuleHandle("Gdi");
+ r% F) c E, I9 H. o
if(hmGdi==NULL)
. S. J- N. v* z/ s S# t
return FALSE;
5 |5 A- p4 q9 |3 t; _
FARPROC Entry = GetProcAddress(hGdi,"TextOut");
- N1 F5 x q8 H5 |# \ C K
if(Entry==NULL)
7 {, l' @' ^# Y
return FALSE;
8 o$ A/ F( A" j. b" J% r* N9 P
OffSetEntry = (WORD)(FP_OFF(Entry));//取得API代码段的选择符
1 _7 Z5 j" ]+ r1 v/ \' s9 J3 V- m
DsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配一个等同的可写的选择符
6 ` N0 T) _7 \5 M! ]1 a# ~
Address = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址
7 k2 [2 w7 S- ] x1 G- @
Newvalue[0]=0xEA;
" \$ |. j! M% u% ~" v
*((DWORD*)(Newvalue+1)) = (DWORD)MyTextOut;
8 z$ X9 t& d/ h' @' p7 }" d
Oldvalue[0]=Address[0];
2 P. Z/ Y$ R: U) D! p( p
*((DWORD*)(Oldvalue+1)) = *((DWORD*)(Address+1));
! U+ L5 \* q1 s* c
}
1 i% ^0 t9 |8 t+ s. D0 s
BOOL ClearHook()
A+ S: g1 v [ x0 W5 G" h
{
* ]' e% e! Q9 b% M, V- ~
if(bHookAlready)
. h' H5 q: x; Q- k5 A0 |
HookOff();
4 U/ j! c3 G5 G) j
FreeSelector(DsSelector);
~- G# n+ R+ ?2 N
}
I$ Q, Y. s* d+ d* D# Z0 E! {
BOOL HookOn()
4 @0 g; L+ j1 @) c
{
+ z6 N7 i( e' a& Y I
if(!bHookAlready){
! m% ^6 Q J, ]3 E3 c
for(int i=0;i<5;i++){
6 q2 e( W7 N. X: S: l/ l. P' S
Address
=Newvalue
;
9 R7 q; v) g, y5 @- h' b+ o) f
}
; t+ A, C- a# W, [( H2 U- Y
bHookAlready=TRUE;
% f. c" C& z/ g4 V/ K- }
}
5 e, c8 ?5 _% ^: [! o8 [$ @
}
2 U6 C4 F$ u% e v
BOOL HookOff()
' I/ K o8 E. \/ q
{
" l A* \' P; k1 I5 J$ A9 I
if(bHookAlready){
/ ]4 @9 s) G) @+ U9 {) S9 K! L
for(int i=0;i<5;i++){
( o9 I5 O; Y2 o$ I" [9 P
Address
=Oldvalue
;
% {0 A4 e; o' e) Y; p
}
7 R% x( m# m0 ?& F- P
bHookAlready=FALSE;
, Q7 U" t) F0 Z2 A
}
6 i5 h2 n* Q& |3 T* U Y" r
}
3 ~+ Z! Q* _9 U$ X* _# B
//钩子函数,一定要和API有相同的参数和声明
1 f3 i2 V; t3 t' l' ^. O
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString)
; J: i! T$ {! k) C
{
& ?/ D. W4 M& s' D p7 s: j. [
BOOL ret;
) B: I0 {1 g4 y- m
HookOff();
9 d$ Y( K7 W' [7 |
ret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//调原来的TextOut
5 a- u% i' `5 U, e4 h
HookOn();
5 P. j% g0 T$ P' x& S1 l/ g6 y
return ret;
/ T) s9 A" x$ Y2 c9 L# p$ G
}
: k1 S- E3 v1 ]5 Q2 T
上面的代码是一个最简单的挂API钩子的例子,我要提醒大家的是,这段代码是我凭记忆写的,我以前的代码丢了,我没有编译测试过
$ E7 R% S& z$ h. d9 M! P
因为我没有VC++1.52.所以代码可能会有错。
3 B. P. R2 E( ^$ }) f# \
建议使用Borland c++,按16位编译。
! Y& N" X3 D2 f% t3 L2 x6 f
如果用VC++1.52,则要改个选项
, D$ ~1 d. \; D1 z' b. W/ Y2 T/ x9 U
在VC++1.52的Option里,有个内存模式的设置,选大模式,和"DS!=SS DS Load on Function entry.",切记,否则会系统崩溃。
6 z9 W- O& z/ \ j" ^2 \- }# h
有什么不明白的可以给我写信
. O4 K0 Y9 M2 q/ @$ x
[email protected]
. O2 X- S) l, N2 ~/ B5 x
Wednesday, June 23, 2004 7:11 PM
$ E$ E% O3 s* M0 y
9 v1 m5 c. @$ h6 b i' ^
作者:
liunan7080
时间:
2008-11-9 09:57
[s:6] 高深莫测
欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://chinaavg.com/)
Powered by Discuz! X3.2