什么不同,怎么样才能正确的取到IE里面的词呢?需要做什么特殊的处理吗?请各位有
相关经验的大虾务必知无不言、言无不尽,这个问题我愿意出900分给帮我搞定的朋
友!! 谢谢!!!
50 个解决方案
#1
我想你试试加入IE的YUAN!try
#2
看看这个吧,或许会恍然大悟:
http://iflower.363.net/pmzhz.htm
http://iflower.363.net/pmzhz.htm
#3
900大分呢,在哪儿呢?哪儿呢!
#4
yeh, should give to Joey.
#5
我本来认为金山词霸的取词是通过某些接口,例如它如果发现某个控件里有文字就通过getwindowtext来获取那个词,它获得ie里的词我也认为应该有ie的sdk包提供了相应的api接口.
但,后来我自己做了个文本编辑器,并没有用到window9x中带的edit和richedit标准控件,但是..
金山词霸还是可以取词!!
这就说明,它可能的运作方式有
1.直接读取显示缓冲区....(我都觉得不太可能,除非字符以另外一种方式存在)
2.直接截获dc的输出,诸如textout之类的api,我没试过,好象很难.
但,后来我自己做了个文本编辑器,并没有用到window9x中带的edit和richedit标准控件,但是..
金山词霸还是可以取词!!
这就说明,它可能的运作方式有
1.直接读取显示缓冲区....(我都觉得不太可能,除非字符以另外一种方式存在)
2.直接截获dc的输出,诸如textout之类的api,我没试过,好象很难.
#6
我做过一个屏幕取词的东西,最后分成两大类:
一、以记事本为代表,直接用 TextOutA/W、ExtTextOutA/W 输出到屏幕;
二、以IE为代表,用 TextOutA/W、ExtTextOutA/W 输到某内存 DC 上,再 BitBlt 到屏幕,稍微复杂一点;
另外,Acrobat 系列好像有点不同。
一、以记事本为代表,直接用 TextOutA/W、ExtTextOutA/W 输出到屏幕;
二、以IE为代表,用 TextOutA/W、ExtTextOutA/W 输到某内存 DC 上,再 BitBlt 到屏幕,稍微复杂一点;
另外,Acrobat 系列好像有点不同。
#7
屏幕取词核心内幕
本文只对与几个关键性技术的实现细节进行讨论,其它的编程细节,请参考源程序。
32位到16位的形式替换
32位代码与16位代码的数据交换
动态修改Windows内核
1. 32bit到16bit的形式替换(Thunk)
形式替换是指那些允许从16位代码调用32位代码或从32位代码调用16位代码的技术。形式替换用于解决试图在同一操作系统或同一可执行程序上使16位代码与32位代码同时并存的问题,即16位代码与32位代码的混合编程技术。早期的DOS程序及Window3.x上的应用程序均为16位程序,Windows95及Windows NT虽然也可运行旧的16位程序,但它们的主流发展方向是32位应用程序。与Windows NT不同的是,Windows95不是一个“纯”32位操作系统,为了兼有令人满意的速度和与旧的16位程序的良好兼容性,其内核本身就是一个16位与32位的混合体,因此也为编程者使用形式替换提供了便利。Microsoft为编写形式替换程序提供了通用的接口及工具,但因LTW32中的形式替换并不复杂,所以使用了一些编程技巧,而避免了使用Microsoft复杂的开发工具。
形式替换中最主要的问题有两点:①16位与32位数据类型尺寸的变化,如16位代码到32位代码的一个重要变化是整型数int的长度加倍了 ;②是堆栈操作时,16位模式使用SS:SP堆栈指针控制栈顶,而在32位模式中使用ESP寄存器作堆栈指针控制栈顶。
在LTW32中,屏幕抓词功能由16位DLL实现,因而只需实现32位到16位的形式替换,为32位代码提供16位DLL的调用接口。CALL FWORD PTR是32位汇编代码的一种调用方法,它可让32位代码调用到16位代码。作为实现的关键,控制16位侧与32位侧各自的堆栈是编程的要点。
32位侧的调用代码:
_asm{
pusha
call fword ptr [func16bit] /* func16bit是16位被调用者的48位地址 */
popa
}
16位侧的调用代码:
int dummy;
static char stack[8192]; /* 16位代码的临时堆栈 */
static WORD stack_seg;
static WORD prev_seg;
static DWORD prev_ofs;
static WORD prev_ds;
_asm{
push ax;
push bx;
mov ax, ds;
mov bx, seg dummy;
mov ds, bx;
mov stack_seg, bx;
mov prev_ds, ax;
pop bx;
pop ax;
mov prev_seg, ss;
mov dword ptr prev_ofs, esp; /* 保存32位堆栈指针 */
mov ss, stack_seg;
mov sp, offset stack; /* 设置16位堆栈指针 */
add sp, 8192;
}
/* 此处加入16位代码要实现的功能 */
_asm{
mov ss, prev_seg;
mov esp, dword ptr prev_ofs; /* 恢复32位堆栈指针 */
mov ds, prev_ds;
lea sp, word ptr [bp-2];
pop ds;
pop bp;
dec bp;
db 66h;
retf;
}
由于调用中传递的参数有限,所以涉及的代码并不多,唯一比较复杂之处在16位侧,它临时转向一个16位堆栈,服务于来自32位的调用者的请求。在16位侧入口处,必须存放好32位调用者的32位堆栈的指针,并且在16位侧返回时恢复它。由代码可见,这个过程是不可重入的,即一次只支持一个调用者,由于调用者只有LTW32的32位侧代码,所以此限制可以满足。另外,在16位代码返回时,必须恢复32位调用者的48位堆栈指针(SS:ESP)而不是32位的CS:SP,而且必须用一个48位地址(16:32)远返回(66h RETF)到它的32位调用者。
处理好这些细节,实现32位到16位的形式替换实际上是很简单的。
2. 32位代码与16位代码的数据交换
32位代码使用16位段地址加32位线性地址(16:32)的地址形式,16位代码使用16位段选择符(selector)加16位偏移(16:16)的地址形式。在Windows 95的32位侧,段址28h是系统段,即通过CS=28h或DS=28h可以访问系统的4G空间,其它应用程序的地址空间都是映射到28h段的4G空间中。Windows95中所有32位进程的地址空间(共4G)的高2G(80000000H~FFFFFFFFH)全部映射到28:80000000~28:FFFFFFFFH。即这块区域是所有32位程序共享的。这里一般存放系统DLL、虚拟设备驱动程序(VxD)、内存映射文件、16位应用程序和16位全局堆等。最后一项很重要,这为32位代码与16位代码交换数据提供了一个简便的方法。因为16位程序的段选择符的基址即是其所映射的系统段中的线性地址,这样,只要能够得到这个线性地址,32位代码就可以轻易地访问到16位程序的数据(LTW32 的32位侧使用此方法从16位侧获得屏幕截获的信息)。而16位段选择符的线性基址可以通过使用系统调用GetSelectorBase()得到,具体实现可参考源程序。线性地址计算的例子如下:
16位地址:07F2:1234H
段选择符 07F2H的线性基址为:82F41300H
段选择符 07F2H的尺寸为:4000H
∵ 82F41300H + 1234H = 82F42534
∴ 对应的32位线性地址为28:82F42534H
3. 动态修改Windows内核
如前所述,Windows95不是一个“纯”32位操作系统,其内核模块中的USER和GDI均是用16位代码实现的。USER32.DLL和GDI32.DLL只是16位的USER.EXE和GDI.EXE的32位调用接口。因此,如果屏幕截获程序用32位代码实现,则只能截获32位应用程序对USER32.DLL和GDI32.DLL的调用,无法截获16位应用程序对USER.EXE和GDI.EXE的调用,所以如果想截获所有应用程序(包括Windows95的桌面程序Explorer)中有关屏幕输出的系统调用,则应该用16位代码实现屏幕截获功能。这就是LTW32为什么不是“纯”32位应用程序的原因。LTW32主要截获两个系统调用TextOut()和ExtTextOut(),方法很简单,把这两个函数的头五个字节修改为一个JMP FAR 指令,使得对这两个函数的调用均转向屏幕截获程序。这就涉及到一个关键问题:动态修改Windows的代码。
在传统的DOS程序中,动态修改程序代码无任何困难,但在Windows中则不然,因为在Windows中,代码可被同一程序的多个实例(进程)共享,所以系统不允许应用程序动态的修改代码。在16位侧,内存的可读、写属性是与段选择符联系在一起的。段选择符基本上可分为两类:数据段选择符和代码段选择符。前者可读、可写、不可执行;后者可读、可执行、不可写。Windows提供了这两类段选择符相转换的系统调用。未公开的16位系统调用AllocCStoDSAlias()为给定的代码段选择符分配一个具有相同线性基址和尺寸的数据段别名(DS Alias)。通过DS别名可以对给定的代码段进行修改。AllocCStoDSAlias()的使用方法如下:
WORD (FAR PASCAL *AllocCStoDSAlias)(WORD);
AllocCStoDSAlias = GetProcAddress(
GetModuleHandle(“KERNEL”), ”ALLOCCSTODSALIAS”);
调用参数为给定的代码选择符,调用成功时返回一个线性基址和尺寸均与原代码选择符相同的DS别名。当不再使用此DS别名时,要用系统调用FreeSelector()把DS别名释放掉。
使用上述技术,就可实现动态修改Windows代码,从而改变GDI的系统调用TextOut()和ExtTextOut()的执行动作,实时地截获屏幕输出,为实现鼠标随动翻译提供可能。
把上述的32位到16位的形式替换、32位代码与16位代码的数据交换、动态修改Windows内核等技术综合应用在一起,配合单词查找算法和词组分析算法就可以实现鼠标随动翻译功能。
本文只对与几个关键性技术的实现细节进行讨论,其它的编程细节,请参考源程序。
32位到16位的形式替换
32位代码与16位代码的数据交换
动态修改Windows内核
1. 32bit到16bit的形式替换(Thunk)
形式替换是指那些允许从16位代码调用32位代码或从32位代码调用16位代码的技术。形式替换用于解决试图在同一操作系统或同一可执行程序上使16位代码与32位代码同时并存的问题,即16位代码与32位代码的混合编程技术。早期的DOS程序及Window3.x上的应用程序均为16位程序,Windows95及Windows NT虽然也可运行旧的16位程序,但它们的主流发展方向是32位应用程序。与Windows NT不同的是,Windows95不是一个“纯”32位操作系统,为了兼有令人满意的速度和与旧的16位程序的良好兼容性,其内核本身就是一个16位与32位的混合体,因此也为编程者使用形式替换提供了便利。Microsoft为编写形式替换程序提供了通用的接口及工具,但因LTW32中的形式替换并不复杂,所以使用了一些编程技巧,而避免了使用Microsoft复杂的开发工具。
形式替换中最主要的问题有两点:①16位与32位数据类型尺寸的变化,如16位代码到32位代码的一个重要变化是整型数int的长度加倍了 ;②是堆栈操作时,16位模式使用SS:SP堆栈指针控制栈顶,而在32位模式中使用ESP寄存器作堆栈指针控制栈顶。
在LTW32中,屏幕抓词功能由16位DLL实现,因而只需实现32位到16位的形式替换,为32位代码提供16位DLL的调用接口。CALL FWORD PTR是32位汇编代码的一种调用方法,它可让32位代码调用到16位代码。作为实现的关键,控制16位侧与32位侧各自的堆栈是编程的要点。
32位侧的调用代码:
_asm{
pusha
call fword ptr [func16bit] /* func16bit是16位被调用者的48位地址 */
popa
}
16位侧的调用代码:
int dummy;
static char stack[8192]; /* 16位代码的临时堆栈 */
static WORD stack_seg;
static WORD prev_seg;
static DWORD prev_ofs;
static WORD prev_ds;
_asm{
push ax;
push bx;
mov ax, ds;
mov bx, seg dummy;
mov ds, bx;
mov stack_seg, bx;
mov prev_ds, ax;
pop bx;
pop ax;
mov prev_seg, ss;
mov dword ptr prev_ofs, esp; /* 保存32位堆栈指针 */
mov ss, stack_seg;
mov sp, offset stack; /* 设置16位堆栈指针 */
add sp, 8192;
}
/* 此处加入16位代码要实现的功能 */
_asm{
mov ss, prev_seg;
mov esp, dword ptr prev_ofs; /* 恢复32位堆栈指针 */
mov ds, prev_ds;
lea sp, word ptr [bp-2];
pop ds;
pop bp;
dec bp;
db 66h;
retf;
}
由于调用中传递的参数有限,所以涉及的代码并不多,唯一比较复杂之处在16位侧,它临时转向一个16位堆栈,服务于来自32位的调用者的请求。在16位侧入口处,必须存放好32位调用者的32位堆栈的指针,并且在16位侧返回时恢复它。由代码可见,这个过程是不可重入的,即一次只支持一个调用者,由于调用者只有LTW32的32位侧代码,所以此限制可以满足。另外,在16位代码返回时,必须恢复32位调用者的48位堆栈指针(SS:ESP)而不是32位的CS:SP,而且必须用一个48位地址(16:32)远返回(66h RETF)到它的32位调用者。
处理好这些细节,实现32位到16位的形式替换实际上是很简单的。
2. 32位代码与16位代码的数据交换
32位代码使用16位段地址加32位线性地址(16:32)的地址形式,16位代码使用16位段选择符(selector)加16位偏移(16:16)的地址形式。在Windows 95的32位侧,段址28h是系统段,即通过CS=28h或DS=28h可以访问系统的4G空间,其它应用程序的地址空间都是映射到28h段的4G空间中。Windows95中所有32位进程的地址空间(共4G)的高2G(80000000H~FFFFFFFFH)全部映射到28:80000000~28:FFFFFFFFH。即这块区域是所有32位程序共享的。这里一般存放系统DLL、虚拟设备驱动程序(VxD)、内存映射文件、16位应用程序和16位全局堆等。最后一项很重要,这为32位代码与16位代码交换数据提供了一个简便的方法。因为16位程序的段选择符的基址即是其所映射的系统段中的线性地址,这样,只要能够得到这个线性地址,32位代码就可以轻易地访问到16位程序的数据(LTW32 的32位侧使用此方法从16位侧获得屏幕截获的信息)。而16位段选择符的线性基址可以通过使用系统调用GetSelectorBase()得到,具体实现可参考源程序。线性地址计算的例子如下:
16位地址:07F2:1234H
段选择符 07F2H的线性基址为:82F41300H
段选择符 07F2H的尺寸为:4000H
∵ 82F41300H + 1234H = 82F42534
∴ 对应的32位线性地址为28:82F42534H
3. 动态修改Windows内核
如前所述,Windows95不是一个“纯”32位操作系统,其内核模块中的USER和GDI均是用16位代码实现的。USER32.DLL和GDI32.DLL只是16位的USER.EXE和GDI.EXE的32位调用接口。因此,如果屏幕截获程序用32位代码实现,则只能截获32位应用程序对USER32.DLL和GDI32.DLL的调用,无法截获16位应用程序对USER.EXE和GDI.EXE的调用,所以如果想截获所有应用程序(包括Windows95的桌面程序Explorer)中有关屏幕输出的系统调用,则应该用16位代码实现屏幕截获功能。这就是LTW32为什么不是“纯”32位应用程序的原因。LTW32主要截获两个系统调用TextOut()和ExtTextOut(),方法很简单,把这两个函数的头五个字节修改为一个JMP FAR 指令,使得对这两个函数的调用均转向屏幕截获程序。这就涉及到一个关键问题:动态修改Windows的代码。
在传统的DOS程序中,动态修改程序代码无任何困难,但在Windows中则不然,因为在Windows中,代码可被同一程序的多个实例(进程)共享,所以系统不允许应用程序动态的修改代码。在16位侧,内存的可读、写属性是与段选择符联系在一起的。段选择符基本上可分为两类:数据段选择符和代码段选择符。前者可读、可写、不可执行;后者可读、可执行、不可写。Windows提供了这两类段选择符相转换的系统调用。未公开的16位系统调用AllocCStoDSAlias()为给定的代码段选择符分配一个具有相同线性基址和尺寸的数据段别名(DS Alias)。通过DS别名可以对给定的代码段进行修改。AllocCStoDSAlias()的使用方法如下:
WORD (FAR PASCAL *AllocCStoDSAlias)(WORD);
AllocCStoDSAlias = GetProcAddress(
GetModuleHandle(“KERNEL”), ”ALLOCCSTODSALIAS”);
调用参数为给定的代码选择符,调用成功时返回一个线性基址和尺寸均与原代码选择符相同的DS别名。当不再使用此DS别名时,要用系统调用FreeSelector()把DS别名释放掉。
使用上述技术,就可实现动态修改Windows代码,从而改变GDI的系统调用TextOut()和ExtTextOut()的执行动作,实时地截获屏幕输出,为实现鼠标随动翻译提供可能。
把上述的32位到16位的形式替换、32位代码与16位代码的数据交换、动态修改Windows内核等技术综合应用在一起,配合单词查找算法和词组分析算法就可以实现鼠标随动翻译功能。
#8
今天没有时间,先来报到,看看再说!
#9
我来听课
#10
不打算要分,進來學點東西。
#11
感兴趣!
#12
以下是别人的文章,不知对你有没有帮助。
“金山词霸”屏幕取词技术揭密(讨论稿)
主题 屏幕取词技术系列讲座(一)
作者 亦东
很多人对这个问题感兴趣。
原因是这项技术让人感觉很神奇,也很有商业价值。
现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。
但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。
大约每周一两次。想知道的人就常常来看看吧!
一.基础知识
首先想编这种程序需要一些基础知识。
会用Vc++,包括16/32位。
精通Windows API特别是GDI,KERNEL部分。
懂汇编语言,会用softice调试程序,因为这种程序最好用softice调试。
二.基本原理
在Window 3.x时代,windows系统提供的字符输出函数只有很少的几个。
TextOut
ExtTextOut
DrawText
......
其中DrawText最终是用ExtTextOut实现的。
所以Windows的所有字符输出都是由调用TextOut和ExtTextOut实现的。因此,如果你可以修改这两个函数的入口,让程序先调用你自己的一个函数再调用系统的字符输出,你就可以得到Windows所有输出的字符了。
到了Windows95时代,原理基本没变,但是95比3.x要复杂。开始的时候,一些在windows3.x下编写的取词软件仍然可以是使用。但是后来出了个IE4,结果很多词典软件就因为不支持IE4而被淘汰了,但同时也给一些软件创造了机会,如金山词霸。其实IE4的问题并不复杂,只不过它的输出的是unicode字符,是用TextOutW和ExtTextOutW输出的。知道了这一点,只要也截取就可以了。不过实现方法复杂一点,以后会有详细讲解。现在又出了个IE5,结果词霸也不好用了,微软真是#^@#$%$*&^&#@#@..........
我研究后找到了一种解决办法,但还有些问题,有时会取错,正在继续研究,希望大家共同探讨。
另外还有WindowsNT,原理也是一样,只是实现方法和95下完全不同。
三.技术要点
要实现取词,主要要解决以下技术问题。
1.截取API入口,获得API的参数。
2.安全地潜入Windows内部,良好地兼容Windows的各个版本
3.计算鼠标所在的单词和字母。
4.如果你在Window95下,做32位程序,还涉及Windows32/16混合编程的技术。
今天先到这里吧!最好准备一份softice for 95/98和金山词霸,让我们先来分析一下别人是怎么做的。
欢迎与我联系
E-Mail:yeedong@163.net
Guest 1999-04-30 16:00:48
请问用VC自己的DEBUGGER不行吗?为什么要用SOFTICE? 我没用过SOFTICE,它有什么特别之处吗?
葫芦 1999-04-30 19:15:03
本人对这个问题也有兴趣,以前研究过16位版本截获TextOut和ExtTextOut的过程;
但对金山词霸,用Softice跟踪,发现SetWindowsHookEx在程序装载时就安装了鼠标钩子,暂停取字/恢复取字只是设置的内部变量,但本人发现金山词霸并没有象16位版本那样修改TextOut和ExtTextOut。
苍蝇 (555021552) 1999-05-02 08:56:57
有哪位大虾愿意先介绍一下SOFTICE?
蟑螂 1999-05-04 13:58:22
把金山词霸的cjktl95.dll用tdump分析可以看到它根本用的不是hook,而是用了一些kernel32.dll中的Win32 SDK里没有包含的函数,使用这些函数来替换改写原先的几个GDI函数。看来是Win95未公开的一些东西。有些人知道,为什么不肯说出来呢?未必见得多么高深!
葫芦 1999-05-05 23:28:07
你可能没有研究过它的目标代码,怎么能断言没有使用hook呢? 其实金山词霸一运行就安装了鼠标钩子。
亦东 1999-05-07 09:52:42
未必见得多么高深?
你知道有些人是怎么知道这些Win95未公开的东西的吗?你不会是以为微软偷偷告诉他们的吧?其实他们大多和我一样是用softice自己跟踪Win95跟出来的。
还有你从cjktl95.dll里tdump出的未公开函数和hook无关,那是做别的用的。
setwindowshook在user里
亦东 1999-05-10 16:16:14
从cjktl95.dll里tdump出的未公开函数和32位和16位之间的互调有关。
主题 屏幕取词技术系列讲座(二)
作者 亦东
很抱歉让大家久等了!
我看了一些人的回帖,发现很多人对取词的原理还是不太清楚。
首先我来解释一下hook问题。词霸中的确用到了hook,而且他用了两种hook其中一种是Windows标准hook,通过SetWindowHook安装一个回调函数,它安装了一个鼠标hook,是为了可以及时响应鼠标的消息用的和取词没太大关系。
另一种钩子是API钩子,这才是取词的核心技术所在。他在TextOut等函数的开头写了一个jmp语句,跳转到自己的代码里。
你用softice看不到这个跳转语句是因为它只在取词的一瞬间才存在,平时是没有的。
你可以在TextOut开头设一个读写断点
bpm textout
再取词,就会找到词霸用来写钩子的代码了。
/**********************************
所以我在次强调,想学这种技术一定要懂汇编语言和熟练使用softice.
**********************************/
至于从cjktl95中dump出来的未公开函数是和Windows32/16混合编程有关的,以后我会提到他们。
我先来讲述取词的过程,
0 判断鼠标是否在一个地方停留了一段时间
1 取得鼠标当前位置
2 以鼠标位置为中心生成一个矩形
3 挂上API钩子
4 让这个矩形产生重画消息
5 在钩子里等输出字符
6 计算鼠标在哪个单词上面,把这个单词保存下来
7 如果得到单词则摘掉API钩子,在一段时间后,无论是否得到单词都摘掉API钩子
8 用单词查词库,显示解释框。
很多步骤实现起来都有一些难度,所以在中国可以做一个完善的取词词典的人屈指可数。
其中0,1,2,7,8比较简单就不提了。
先说如何挂钩子:
所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句,跳转到自己的代码里。
步骤如下:
1.取得Windows API入口,用GetProcAddress实现
2.保存API入口的前五个字节,因为JMP是0xEA,地址是4个字节
3.写入跳转语句
这步最复杂
Windows的代码段本来是不可以写的,但是Microsoft给自己留了个后门。
有一个未公开函数是AllocCsToDsAlias,
UINT WINAPI ALLOCCSTODSALIAS(UINT);
你可以取到这个函数的入口,把API的代码段的选择符(要是不知道什么是选择符,就先去学学保护模式编程吧)传给他,他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。
这就是取词技术的最核心的东东,不止取词,连外挂中文平台全屏汉化都是使用的这种技术。现在知道为什么这么简单的几句话却很少知道了吧?因为太多的产品使用他,太多的公司靠他赚钱了。
这些公司和产品有:中文之星,四通利方,南极星,金山词霸,实达铭泰的东方快车,roboword,译典通,即时汉化专家等等等等。。。。还有至少20多家小公司。他们的具体实现虽然不同,但大致原理是相同的。
我这些都是随手写的,也没有提纲之类的东西,以后如果有机会我会整理一下,大家先凑合着看吧!xixi...
葫芦 1999-05-06 14:52:30
你说的这个技术是16位的,至少金山词霸III没有采用16位的AllocCsToDsAlias函数,也根本没有修改TextOut开始的语句为JMP XXXXXXXX。softice本人非常精通,是绝对不会错的!
葫芦 1999-05-06 15:38:40
谁假冒吾名,坏吾名声?!本人在此郑重宣布,以上贴子非本人所发!
经过跟踪分析,金山词霸III确实修改了TextOut的入口为转跳到自身代码的JMP XXXXXXXX语句,只是修改没有调用AllocCsToDsAlias,也不是在取字时才去修改,而是常常修改(估计是在Timer中进行的)。
亦东 1999-05-06 17:25:23
我说过词霸里用AllocCsToDsAlias了吗?
我是说AllocCsToDsAlias,的确可以用,但词霸里是用DPMI的Int 31做的,其实本质是一样的,你用softice跟一下就知道了
bpint 31 设一个中断断点。
实际上我至少有五种方法写Windows代码段
我介绍使用AllocCsToDsAlias是因为这种方法最简单,其他方法要麻烦得多。
词霸用int 31来做是出于兼容性的考虑,
AllocCsToDsAlias毕竟不是公开函数,但DPMI却是标准。
词霸在你鼠标在某一点停留超过200ms时就会取词,所以他有一个定时器,会经常修改API入口。
看来“假”葫芦只是自以为非常精通softice.
mao 1999-05-06 19:29:28
在微软的MSDN中有一个程序,包含了全部的Source code, 名字好象叫"Inject" 或 stealth什么的,忘了。提供了一个很完善的hook任何一个windows函数的功能。对win16完全适用,在win95下也有用,但NT下不行。
建议大家去找找这个例子看看会很有帮助。
此外有本清华出的微软的Advanced Windows也介绍了具体的方法。
建议亦东干脆把source code给open出来,让有兴趣的朋友用起来更方便,把取词技术可发展的应用发扬光大!
亦东 1999-05-07 09:40:56
我不主张“把取词技术可发展的应用发扬光大”,这也是我掌握了这项技术一年多才公开它的原因。我公开它并不是想让大家都来做字典软件,相反我希望不要再有人做字典了,现在做字典的人已经太多了。看到某种软件有利可图,大家就一哄而上,这种恶性竞争对中国软件业的发展是极其有害的。我公开他的目的是希望提高大家的编程水平。你会发现在研究这项技术的过程中你的编程水平和对Windows的理解程度会有质的飞跃,我本人就从中获益匪浅。
MSDN中是有这样的代码。
甚至有一个叫ProcHook.Dll,提供SetProcAddress之类的函数,但是没有源码。源码在1994年的 MSJ 上很难弄到的。
源码我会分几次公开,每次会有详细的说明。
想要源码的人,准备一份VC++1.52或Borland C++,最好还有softice,下次我会给出一段代码,教你如何修改Windows的代码。
老冒 (555036) 1999-05-07 11:56:46
如果认为屏幕取词的应用就是做字典,就大错特错了。其实关于拦劫windows api的东东早就在93年的Undocument Windows上公开过了。
其实Adobe的Adobe Type Manager在Windows 3.0的时代就通过这种办法实现了漂亮的字体. (现在有TTF不需要ATM了)
MSDN上的那个东东是有全部source和sample, 我抓下来编译过。是1996年夏天的一张MSDN Level 2光碟上的,现在也不知搁哪里了,有兴趣的朋友自己找去吧。
还是open 完整的source好,很多朋友其实只要用这项技术,并不太想知道细节,不是吗.
亦东 1999-05-07 13:28:19
这种技术不是做字典全屏汉化就是外挂语言平台,自从王志东使用它以来,就没用来编过其他软件,也许有但我不知道。
原来有那么多书和其他资料上都有这种技术的资料还有例子,到是我孤陋寡闻了,以为大家都不知道,在这里给大家讲一些众所周知的东西。回去我要好好研究一下,看看Rasir Dex的词霸是从那里抄的。
我不知道某项技术中细节是不是重要,如果很多人只想用而不想自己编,那么楼下那50多个回帖是怎么回是?
老冒 1999-05-07 13:54:18
呵呵,你可千万要坚持把讲座做下去,否则那50多个回应的哥们企不要把我给痛扁了...:)
俺已两年多不碰底层技术的,这方面很落后啦...俺可应付不了这么多热切的求知朋友,亦东要顶住呵!
欢迎和俺多多交流探导!
P.S. 亦东大侠目前何方高就? 正在忙什么项目?交流交流
亦东 1999-05-07 17:46:06
没什么正经事做,到处瞎混呢!
葫芦 1999-05-08 21:50:29
我对此持否定观点,不要自作聪明,以为AllocCsToDsAlias就是能用的,其实AllocCsToDsAlias只是16位的Windows用的函数,32位的Win95程序不能使用此函数,不信你在VC 5.0或6.0中可以试试。
另外,int 31h也不是说能用就能拿来用的,在Windows 3.x下使用是没有问题的(本人还有这方面的文章发表),但在Win95下随意使用会产生GP错,主要原因是32位并不支持DPMI直接调用,不知亦东先生对此有没有研究,就在此发表诸多理论!本人就先请问:32位程序如何调用16位函数或动态库你懂不懂?
葫芦 1999-05-09 02:39:10
上面的这个帖子并没有攻击谁的意思,只是希望大家探讨问题都要本着认真的态度,不要不懂装懂,至少有一点大家要清楚:32位的程序根本不能使用 int 31h,调用16位的动态库Kernel中的AllocCsToDsAlias也并不是件简单的事。
nn_zdm (555031742) 1999-05-09 16:35:35
使用hook函数,可用的功能并非只是做字典全屏汉化和外挂语言平台。使用hook可以调试程序,就象你们说的softice其本身也是使用了hook函数。
nn_zdm (555031742) 1999-05-09 16:42:05
另外hook函数还可以使用在游戏修改工具中,本人就开发过此类工具。《整人专家》估计也是使用这种方法。当然还有另外两种方法。
亦东 1999-05-10 16:10:59
你们说的都有道理。
但hook有两种,一种是Windows标准钩子,通过SetWindowshook挂。
另一种是非标准的,通过在API入口写JMP XXXXXXXX来实现的。
softice的钩子更高级,他都挂到VXD上了。
从32为代码调用16位DLL碰巧我会。
打倒米D国主义!!!
瓜果 1999-05-10 17:07:00
谁知道在哪能搞到SOFTICE,我以前从未用过它!
葫芦 1999-05-10 21:39:46
愿继续拜读你以后的讲座。
SOFTICE吗?光盘上很多,有for DOS, for Windows95, for Windows NT 各个版本。
孙玮 (555031339) 1999-05-11 11:08:35
能否将 si for NT 上传到 10.82.46.33
(使用 ftp) user: haotao
pass: haotao123
tommy 1999-05-11 11:34:11
http://www.swww.com.cn/htm/down/others/main.html 可以下载
亦东 1999-05-11 14:01:19
最近忙于反美,暂时没时间再写了,过些时间才行,下次我会给出源码。
最新消息,美国海军被黑了。
http://www.nctsw.navy.mil/
打倒米D国主义!!!
黄金狮子 1999-05-12 13:19:32
我对各位大虾的讨论深感兴趣。
有几个问题想请教:
1.AllocCsToDsAlias 在32-bit下调用是否采用Thunk?
2.32-bit 下是否有类似function?
3.Jeffrey Richter的"Advanced Windows"里Remote Thread 的Thread Stack来远程注入DLL函数,因此不需上述Function.
4.我有MSJ 1994-1的ProcHook.dll的source code,不知用于WinNT需如何改动.
5.总而言之,有无WinNT下hook API的source code,请告知.
(我还有Softice 3.24 for Win95, 3.25 for WinNT.)
--这个主题很好
鼹鼠 1999-05-14 09:41:20
请大虾给我发一份MSJ 1994-1的ProcHool.dll的source code, 我现在急需这方面的资料。谢谢!!
Email Address: yanshg@263.net
下面是一个Australia人的API hook软件, 它是基于VxD技术。
Molten Home Page:
http://ourworld.compuserve.com/homepages/molten
黄金狮子 1999-05-14 14:34:54
在Win95和NT上,可通过WriteProcessMemory()直接写Code Segment.(原来以为"advanced Windows" 调CreateRemoteThread(),是因为WriteProcessMemory()只能写代码段和堆栈段)
阿涛 1999-05-14 19:45:05
如何亦东老兄要分步公布是PROCHOOK的代码就不必了,这个程序的代码很容易搞到,只须到MSJ的站点上查一下就可找到。
亦东 1999-05-14 21:44:39
大家到msj的大海里去捞针好了。
在95你WriteProcessMemory 写kernel user gdi试试,一定失败。
调CreateRemoteThread并不是为了写代码段,有别的用途,是为了在其他进程里分配内存。回去再好好看看“advanced Windows" 最好用一个程序试试,你就明白了。
其实在NT4.0调CreateRemoteThread是没必要的,这是为了兼容NT3.51.
nn_zdm (555031742) 1999-05-18 13:57:48
利用CreateRemoteThread()函数,在WinNT4.0中使用很有用,它是在winNT中闯过进程边界的三种办法之一,在WinNT中,使用它可以进行远程调试,及修改他人代码.
nn_zdm (555031742) 1999-05-18 14:07:17
在winNT中用WriteProcessMemory()写code
代码是可以的,win95没试过,但MSDN上说是可以的.不过,可能没什么用,因为CreateRemoteThread()函数只在winNT中有用.
如:
WriteProcessMemory(...,"LoadLibrary(...,"mydll.dll",..).
CreateRemoteThread(...)
nn_zdm (555031742) 1999-05-18 14:14:19
上面写漏了,应是
WriteProcessMemory(...,"LoadLibrary(...,"mydll.dll",..). ");
CreateRemoteThread(...) ;
主题 关于屏幕取词的讨论(三)
作者 亦东
让大家久等,很抱歉,前些时候工作忙硬盘又坏了,太不幸了。
这回来点真格的。
咱们以截取TextOut为例。
下面是代码:
//截取TextOut
typedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT);
ALLOCCSTODSALIAS AllocCsToDsAlias;
BYTE NewValue[5];//保存新的入口代码
BYTE OldValue[5];//API原来的入口代码
unsigned char * Address=NULL;//可写的API入口地址
UINT DsSelector=NULL;//指向API入口的可写的选择符
WORD OffSetEntry=NULL;//API的偏移量
BOOL bHookAlready = FALSE; //是否挂钩子的标志
BOOL InitHook()
{
HMODULE hKernel,hGdi;
hKernel = GetModuleHandle("Kernel");
if(hKernel==NULL)
return FALSE;
AllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//这是未公开的API所以要这样取地址
if(AllocCsToDsAlias==NULL)
return FALSE;
hGdi = GetModuleHandle("Gdi");
if(hmGdi==NULL)
return FALSE;
FARPROC Entry = GetProcAddress(hGdi,"TextOut");
if(Entry==NULL)
return FALSE;
OffSetEntry = (WORD)(FP_OFF(Entry));//取得API代码段的选择符
DsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配一个等同的可写的选择符
Address = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址
NewValue[0]=0xEA;
*((DWORD*)(NewValue+1)) = (DWORD)MyTextOut;
OldValue[0]=Address[0];
*((DWORD*)(OldValue+1)) = *((DWORD*)(Address+1));
}
BOOL ClearHook()
{
if(bHookAlready)
HookOff();
FreeSelector(DsSelector);
}
BOOL HookOn()
{
if(!bHookAlready){
for(int i=0;i<5;i++){
Address[i]=NewValue[i];
}
bHookAlready=TRUE;
}
}
BOOL HookOff()
{
if(bHookAlready){
for(int i=0;i<5;i++){
Address[i]=OldValue[i];
}
bHookAlready=FALSE;
}
}
//钩子函数,一定要和API有相同的参数和声明
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString)
{
BOOL ret;
HookOff();
ret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//调原来的TextOut
HookOn();
return ret;
}
上面的代码是一个最简单的挂API钩子的例子,我要提醒大家的是,这段代码是我凭记忆写的,我以前的代码丢了,我没有编译测试过
因为我没有VC++1.52.所以代码可能会有错。
建议使用Borland c++,按16位编译。
如果用VC++1.52,则要改个选项
在VC++1.52的Option里,有个内存模式的设置,选大模式,和"DS!=SS DS Load on Function entry.",切记,否则会系统崩溃。
有什么不明白的可以给我写信
yeedong@163.net
Guest 1999-05-21 22:20:47
你这是16为的地址存取模式吧
你看这个MK_FP,win32不用了,
而且GetProcAddress(hKernel,"AllocCsToDsAlias")
这个API有用吗?
sorry
Guest 1999-05-21 22:31:47
亦东,想请教一个问题,
win32下,每个process有
自己的地址空间,process
A得到process B 的一个窗口C的handle,这个
handle的值 等于process B自己得到的window C 的handle值 吗?
我想应该不相等,但系统是如何转换的呢?(比如process A 向
window C 发消息,系统如何
知道process A 里的handle 和process B 里的
handle 都是指的window C)
. 是不是用duplicatehandle()?
(声明,我是真的不知道)
亦东 1999-05-21 22:54:48
这段代码就是十六位的。
你用Win32根本就不能编译。
32位没有AllocCsToDsAlias,因为在32位里不能写系统代码段(其实有办法,不是这样,不过比较麻烦)。
系统代码都在0x80000000以上,都是只读的。
所以要截WinAPI只能用16位的代码。
每个Process有自己的地址空间没错,但Window的句柄是共享的,同一个窗口在任何进程里的句柄都是一样的。
你可以在自己的进程里向任何窗口发消息。
Window的句柄很多,有的是共享的有的不是,
我也不知道那里有说明,一般是凭经验或试试看。
GUEST 1999-05-22 20:51:51
如果 window handle 换成
moudle handle呢?我
用moudlefirst,moudlenext遍历
得到的某个moudle 的句柄,
在任何一个process 中得到的
这个moudle handle 都是一样的值吗。(这个handle 和进程地址空间无关吗?)。
thx
亦东 1999-05-23 22:31:59
绝对有关
在Win32里module handle就是模块的起始地址。
说起来比较复杂
在95和NT里有些不同
在Win32里,每个模块(DLL,EXE)有一个ImageBase,这个数存放在DLL和EXE的文件头里。每个模块通常是不一样的。当Windows加载这个模块时优先考虑把模块放到Imagebase指定的地址,但有时会出现两个模块的地址重叠会有冲突,Windows会把模块移到与Imagebase最近的地址。所以Imagebase相同的模块在不同进程可能会在不同的地址上。这个地址就是module handle.Imagebase是可以在编译时指定的。
你用moudlefirst,moudlenext遍历得到的module handle是和进程有关的。
比如:你编了一个Imagebase为0x10000000的DLL A,进程A调用这个DLL A,在进程A里这个DLL被加载到地址0x10000000处,他的module handle为0x10000000,进程B也调用这个DLL A,但是进程B还调用另外一个DLL B,这另外的DLL B也是Imagebase为0x10000000的而且先加载,这是进程B的这个DLL A可能就被加载到0x13000000了,DLL A在进程B里的module handle 就是0x13000000了。
在95下,模块是共享的,也就是Windows只加载一份模块到内存,所有用到这个模块的进程都映射同一个模块,也就是说在95里每个模块在物理内存里只有一份。NT则不同,他为每个进程都加载一份模块。所以NT比95需要的内存多。所以在NT里不但在不同进程里的Module handle可能不同,连物理地址都是不同的。
yeedong@163.net
“金山词霸”屏幕取词技术揭密(讨论稿)
主题 屏幕取词技术系列讲座(一)
作者 亦东
很多人对这个问题感兴趣。
原因是这项技术让人感觉很神奇,也很有商业价值。
现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。
但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。
大约每周一两次。想知道的人就常常来看看吧!
一.基础知识
首先想编这种程序需要一些基础知识。
会用Vc++,包括16/32位。
精通Windows API特别是GDI,KERNEL部分。
懂汇编语言,会用softice调试程序,因为这种程序最好用softice调试。
二.基本原理
在Window 3.x时代,windows系统提供的字符输出函数只有很少的几个。
TextOut
ExtTextOut
DrawText
......
其中DrawText最终是用ExtTextOut实现的。
所以Windows的所有字符输出都是由调用TextOut和ExtTextOut实现的。因此,如果你可以修改这两个函数的入口,让程序先调用你自己的一个函数再调用系统的字符输出,你就可以得到Windows所有输出的字符了。
到了Windows95时代,原理基本没变,但是95比3.x要复杂。开始的时候,一些在windows3.x下编写的取词软件仍然可以是使用。但是后来出了个IE4,结果很多词典软件就因为不支持IE4而被淘汰了,但同时也给一些软件创造了机会,如金山词霸。其实IE4的问题并不复杂,只不过它的输出的是unicode字符,是用TextOutW和ExtTextOutW输出的。知道了这一点,只要也截取就可以了。不过实现方法复杂一点,以后会有详细讲解。现在又出了个IE5,结果词霸也不好用了,微软真是#^@#$%$*&^&#@#@..........
我研究后找到了一种解决办法,但还有些问题,有时会取错,正在继续研究,希望大家共同探讨。
另外还有WindowsNT,原理也是一样,只是实现方法和95下完全不同。
三.技术要点
要实现取词,主要要解决以下技术问题。
1.截取API入口,获得API的参数。
2.安全地潜入Windows内部,良好地兼容Windows的各个版本
3.计算鼠标所在的单词和字母。
4.如果你在Window95下,做32位程序,还涉及Windows32/16混合编程的技术。
今天先到这里吧!最好准备一份softice for 95/98和金山词霸,让我们先来分析一下别人是怎么做的。
欢迎与我联系
E-Mail:yeedong@163.net
Guest 1999-04-30 16:00:48
请问用VC自己的DEBUGGER不行吗?为什么要用SOFTICE? 我没用过SOFTICE,它有什么特别之处吗?
葫芦 1999-04-30 19:15:03
本人对这个问题也有兴趣,以前研究过16位版本截获TextOut和ExtTextOut的过程;
但对金山词霸,用Softice跟踪,发现SetWindowsHookEx在程序装载时就安装了鼠标钩子,暂停取字/恢复取字只是设置的内部变量,但本人发现金山词霸并没有象16位版本那样修改TextOut和ExtTextOut。
苍蝇 (555021552) 1999-05-02 08:56:57
有哪位大虾愿意先介绍一下SOFTICE?
蟑螂 1999-05-04 13:58:22
把金山词霸的cjktl95.dll用tdump分析可以看到它根本用的不是hook,而是用了一些kernel32.dll中的Win32 SDK里没有包含的函数,使用这些函数来替换改写原先的几个GDI函数。看来是Win95未公开的一些东西。有些人知道,为什么不肯说出来呢?未必见得多么高深!
葫芦 1999-05-05 23:28:07
你可能没有研究过它的目标代码,怎么能断言没有使用hook呢? 其实金山词霸一运行就安装了鼠标钩子。
亦东 1999-05-07 09:52:42
未必见得多么高深?
你知道有些人是怎么知道这些Win95未公开的东西的吗?你不会是以为微软偷偷告诉他们的吧?其实他们大多和我一样是用softice自己跟踪Win95跟出来的。
还有你从cjktl95.dll里tdump出的未公开函数和hook无关,那是做别的用的。
setwindowshook在user里
亦东 1999-05-10 16:16:14
从cjktl95.dll里tdump出的未公开函数和32位和16位之间的互调有关。
主题 屏幕取词技术系列讲座(二)
作者 亦东
很抱歉让大家久等了!
我看了一些人的回帖,发现很多人对取词的原理还是不太清楚。
首先我来解释一下hook问题。词霸中的确用到了hook,而且他用了两种hook其中一种是Windows标准hook,通过SetWindowHook安装一个回调函数,它安装了一个鼠标hook,是为了可以及时响应鼠标的消息用的和取词没太大关系。
另一种钩子是API钩子,这才是取词的核心技术所在。他在TextOut等函数的开头写了一个jmp语句,跳转到自己的代码里。
你用softice看不到这个跳转语句是因为它只在取词的一瞬间才存在,平时是没有的。
你可以在TextOut开头设一个读写断点
bpm textout
再取词,就会找到词霸用来写钩子的代码了。
/**********************************
所以我在次强调,想学这种技术一定要懂汇编语言和熟练使用softice.
**********************************/
至于从cjktl95中dump出来的未公开函数是和Windows32/16混合编程有关的,以后我会提到他们。
我先来讲述取词的过程,
0 判断鼠标是否在一个地方停留了一段时间
1 取得鼠标当前位置
2 以鼠标位置为中心生成一个矩形
3 挂上API钩子
4 让这个矩形产生重画消息
5 在钩子里等输出字符
6 计算鼠标在哪个单词上面,把这个单词保存下来
7 如果得到单词则摘掉API钩子,在一段时间后,无论是否得到单词都摘掉API钩子
8 用单词查词库,显示解释框。
很多步骤实现起来都有一些难度,所以在中国可以做一个完善的取词词典的人屈指可数。
其中0,1,2,7,8比较简单就不提了。
先说如何挂钩子:
所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句,跳转到自己的代码里。
步骤如下:
1.取得Windows API入口,用GetProcAddress实现
2.保存API入口的前五个字节,因为JMP是0xEA,地址是4个字节
3.写入跳转语句
这步最复杂
Windows的代码段本来是不可以写的,但是Microsoft给自己留了个后门。
有一个未公开函数是AllocCsToDsAlias,
UINT WINAPI ALLOCCSTODSALIAS(UINT);
你可以取到这个函数的入口,把API的代码段的选择符(要是不知道什么是选择符,就先去学学保护模式编程吧)传给他,他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。
这就是取词技术的最核心的东东,不止取词,连外挂中文平台全屏汉化都是使用的这种技术。现在知道为什么这么简单的几句话却很少知道了吧?因为太多的产品使用他,太多的公司靠他赚钱了。
这些公司和产品有:中文之星,四通利方,南极星,金山词霸,实达铭泰的东方快车,roboword,译典通,即时汉化专家等等等等。。。。还有至少20多家小公司。他们的具体实现虽然不同,但大致原理是相同的。
我这些都是随手写的,也没有提纲之类的东西,以后如果有机会我会整理一下,大家先凑合着看吧!xixi...
葫芦 1999-05-06 14:52:30
你说的这个技术是16位的,至少金山词霸III没有采用16位的AllocCsToDsAlias函数,也根本没有修改TextOut开始的语句为JMP XXXXXXXX。softice本人非常精通,是绝对不会错的!
葫芦 1999-05-06 15:38:40
谁假冒吾名,坏吾名声?!本人在此郑重宣布,以上贴子非本人所发!
经过跟踪分析,金山词霸III确实修改了TextOut的入口为转跳到自身代码的JMP XXXXXXXX语句,只是修改没有调用AllocCsToDsAlias,也不是在取字时才去修改,而是常常修改(估计是在Timer中进行的)。
亦东 1999-05-06 17:25:23
我说过词霸里用AllocCsToDsAlias了吗?
我是说AllocCsToDsAlias,的确可以用,但词霸里是用DPMI的Int 31做的,其实本质是一样的,你用softice跟一下就知道了
bpint 31 设一个中断断点。
实际上我至少有五种方法写Windows代码段
我介绍使用AllocCsToDsAlias是因为这种方法最简单,其他方法要麻烦得多。
词霸用int 31来做是出于兼容性的考虑,
AllocCsToDsAlias毕竟不是公开函数,但DPMI却是标准。
词霸在你鼠标在某一点停留超过200ms时就会取词,所以他有一个定时器,会经常修改API入口。
看来“假”葫芦只是自以为非常精通softice.
mao 1999-05-06 19:29:28
在微软的MSDN中有一个程序,包含了全部的Source code, 名字好象叫"Inject" 或 stealth什么的,忘了。提供了一个很完善的hook任何一个windows函数的功能。对win16完全适用,在win95下也有用,但NT下不行。
建议大家去找找这个例子看看会很有帮助。
此外有本清华出的微软的Advanced Windows也介绍了具体的方法。
建议亦东干脆把source code给open出来,让有兴趣的朋友用起来更方便,把取词技术可发展的应用发扬光大!
亦东 1999-05-07 09:40:56
我不主张“把取词技术可发展的应用发扬光大”,这也是我掌握了这项技术一年多才公开它的原因。我公开它并不是想让大家都来做字典软件,相反我希望不要再有人做字典了,现在做字典的人已经太多了。看到某种软件有利可图,大家就一哄而上,这种恶性竞争对中国软件业的发展是极其有害的。我公开他的目的是希望提高大家的编程水平。你会发现在研究这项技术的过程中你的编程水平和对Windows的理解程度会有质的飞跃,我本人就从中获益匪浅。
MSDN中是有这样的代码。
甚至有一个叫ProcHook.Dll,提供SetProcAddress之类的函数,但是没有源码。源码在1994年的 MSJ 上很难弄到的。
源码我会分几次公开,每次会有详细的说明。
想要源码的人,准备一份VC++1.52或Borland C++,最好还有softice,下次我会给出一段代码,教你如何修改Windows的代码。
老冒 (555036) 1999-05-07 11:56:46
如果认为屏幕取词的应用就是做字典,就大错特错了。其实关于拦劫windows api的东东早就在93年的Undocument Windows上公开过了。
其实Adobe的Adobe Type Manager在Windows 3.0的时代就通过这种办法实现了漂亮的字体. (现在有TTF不需要ATM了)
MSDN上的那个东东是有全部source和sample, 我抓下来编译过。是1996年夏天的一张MSDN Level 2光碟上的,现在也不知搁哪里了,有兴趣的朋友自己找去吧。
还是open 完整的source好,很多朋友其实只要用这项技术,并不太想知道细节,不是吗.
亦东 1999-05-07 13:28:19
这种技术不是做字典全屏汉化就是外挂语言平台,自从王志东使用它以来,就没用来编过其他软件,也许有但我不知道。
原来有那么多书和其他资料上都有这种技术的资料还有例子,到是我孤陋寡闻了,以为大家都不知道,在这里给大家讲一些众所周知的东西。回去我要好好研究一下,看看Rasir Dex的词霸是从那里抄的。
我不知道某项技术中细节是不是重要,如果很多人只想用而不想自己编,那么楼下那50多个回帖是怎么回是?
老冒 1999-05-07 13:54:18
呵呵,你可千万要坚持把讲座做下去,否则那50多个回应的哥们企不要把我给痛扁了...:)
俺已两年多不碰底层技术的,这方面很落后啦...俺可应付不了这么多热切的求知朋友,亦东要顶住呵!
欢迎和俺多多交流探导!
P.S. 亦东大侠目前何方高就? 正在忙什么项目?交流交流
亦东 1999-05-07 17:46:06
没什么正经事做,到处瞎混呢!
葫芦 1999-05-08 21:50:29
我对此持否定观点,不要自作聪明,以为AllocCsToDsAlias就是能用的,其实AllocCsToDsAlias只是16位的Windows用的函数,32位的Win95程序不能使用此函数,不信你在VC 5.0或6.0中可以试试。
另外,int 31h也不是说能用就能拿来用的,在Windows 3.x下使用是没有问题的(本人还有这方面的文章发表),但在Win95下随意使用会产生GP错,主要原因是32位并不支持DPMI直接调用,不知亦东先生对此有没有研究,就在此发表诸多理论!本人就先请问:32位程序如何调用16位函数或动态库你懂不懂?
葫芦 1999-05-09 02:39:10
上面的这个帖子并没有攻击谁的意思,只是希望大家探讨问题都要本着认真的态度,不要不懂装懂,至少有一点大家要清楚:32位的程序根本不能使用 int 31h,调用16位的动态库Kernel中的AllocCsToDsAlias也并不是件简单的事。
nn_zdm (555031742) 1999-05-09 16:35:35
使用hook函数,可用的功能并非只是做字典全屏汉化和外挂语言平台。使用hook可以调试程序,就象你们说的softice其本身也是使用了hook函数。
nn_zdm (555031742) 1999-05-09 16:42:05
另外hook函数还可以使用在游戏修改工具中,本人就开发过此类工具。《整人专家》估计也是使用这种方法。当然还有另外两种方法。
亦东 1999-05-10 16:10:59
你们说的都有道理。
但hook有两种,一种是Windows标准钩子,通过SetWindowshook挂。
另一种是非标准的,通过在API入口写JMP XXXXXXXX来实现的。
softice的钩子更高级,他都挂到VXD上了。
从32为代码调用16位DLL碰巧我会。
打倒米D国主义!!!
瓜果 1999-05-10 17:07:00
谁知道在哪能搞到SOFTICE,我以前从未用过它!
葫芦 1999-05-10 21:39:46
愿继续拜读你以后的讲座。
SOFTICE吗?光盘上很多,有for DOS, for Windows95, for Windows NT 各个版本。
孙玮 (555031339) 1999-05-11 11:08:35
能否将 si for NT 上传到 10.82.46.33
(使用 ftp) user: haotao
pass: haotao123
tommy 1999-05-11 11:34:11
http://www.swww.com.cn/htm/down/others/main.html 可以下载
亦东 1999-05-11 14:01:19
最近忙于反美,暂时没时间再写了,过些时间才行,下次我会给出源码。
最新消息,美国海军被黑了。
http://www.nctsw.navy.mil/
打倒米D国主义!!!
黄金狮子 1999-05-12 13:19:32
我对各位大虾的讨论深感兴趣。
有几个问题想请教:
1.AllocCsToDsAlias 在32-bit下调用是否采用Thunk?
2.32-bit 下是否有类似function?
3.Jeffrey Richter的"Advanced Windows"里Remote Thread 的Thread Stack来远程注入DLL函数,因此不需上述Function.
4.我有MSJ 1994-1的ProcHook.dll的source code,不知用于WinNT需如何改动.
5.总而言之,有无WinNT下hook API的source code,请告知.
(我还有Softice 3.24 for Win95, 3.25 for WinNT.)
--这个主题很好
鼹鼠 1999-05-14 09:41:20
请大虾给我发一份MSJ 1994-1的ProcHool.dll的source code, 我现在急需这方面的资料。谢谢!!
Email Address: yanshg@263.net
下面是一个Australia人的API hook软件, 它是基于VxD技术。
Molten Home Page:
http://ourworld.compuserve.com/homepages/molten
黄金狮子 1999-05-14 14:34:54
在Win95和NT上,可通过WriteProcessMemory()直接写Code Segment.(原来以为"advanced Windows" 调CreateRemoteThread(),是因为WriteProcessMemory()只能写代码段和堆栈段)
阿涛 1999-05-14 19:45:05
如何亦东老兄要分步公布是PROCHOOK的代码就不必了,这个程序的代码很容易搞到,只须到MSJ的站点上查一下就可找到。
亦东 1999-05-14 21:44:39
大家到msj的大海里去捞针好了。
在95你WriteProcessMemory 写kernel user gdi试试,一定失败。
调CreateRemoteThread并不是为了写代码段,有别的用途,是为了在其他进程里分配内存。回去再好好看看“advanced Windows" 最好用一个程序试试,你就明白了。
其实在NT4.0调CreateRemoteThread是没必要的,这是为了兼容NT3.51.
nn_zdm (555031742) 1999-05-18 13:57:48
利用CreateRemoteThread()函数,在WinNT4.0中使用很有用,它是在winNT中闯过进程边界的三种办法之一,在WinNT中,使用它可以进行远程调试,及修改他人代码.
nn_zdm (555031742) 1999-05-18 14:07:17
在winNT中用WriteProcessMemory()写code
代码是可以的,win95没试过,但MSDN上说是可以的.不过,可能没什么用,因为CreateRemoteThread()函数只在winNT中有用.
如:
WriteProcessMemory(...,"LoadLibrary(...,"mydll.dll",..).
CreateRemoteThread(...)
nn_zdm (555031742) 1999-05-18 14:14:19
上面写漏了,应是
WriteProcessMemory(...,"LoadLibrary(...,"mydll.dll",..). ");
CreateRemoteThread(...) ;
主题 关于屏幕取词的讨论(三)
作者 亦东
让大家久等,很抱歉,前些时候工作忙硬盘又坏了,太不幸了。
这回来点真格的。
咱们以截取TextOut为例。
下面是代码:
//截取TextOut
typedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT);
ALLOCCSTODSALIAS AllocCsToDsAlias;
BYTE NewValue[5];//保存新的入口代码
BYTE OldValue[5];//API原来的入口代码
unsigned char * Address=NULL;//可写的API入口地址
UINT DsSelector=NULL;//指向API入口的可写的选择符
WORD OffSetEntry=NULL;//API的偏移量
BOOL bHookAlready = FALSE; //是否挂钩子的标志
BOOL InitHook()
{
HMODULE hKernel,hGdi;
hKernel = GetModuleHandle("Kernel");
if(hKernel==NULL)
return FALSE;
AllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//这是未公开的API所以要这样取地址
if(AllocCsToDsAlias==NULL)
return FALSE;
hGdi = GetModuleHandle("Gdi");
if(hmGdi==NULL)
return FALSE;
FARPROC Entry = GetProcAddress(hGdi,"TextOut");
if(Entry==NULL)
return FALSE;
OffSetEntry = (WORD)(FP_OFF(Entry));//取得API代码段的选择符
DsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配一个等同的可写的选择符
Address = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址
NewValue[0]=0xEA;
*((DWORD*)(NewValue+1)) = (DWORD)MyTextOut;
OldValue[0]=Address[0];
*((DWORD*)(OldValue+1)) = *((DWORD*)(Address+1));
}
BOOL ClearHook()
{
if(bHookAlready)
HookOff();
FreeSelector(DsSelector);
}
BOOL HookOn()
{
if(!bHookAlready){
for(int i=0;i<5;i++){
Address[i]=NewValue[i];
}
bHookAlready=TRUE;
}
}
BOOL HookOff()
{
if(bHookAlready){
for(int i=0;i<5;i++){
Address[i]=OldValue[i];
}
bHookAlready=FALSE;
}
}
//钩子函数,一定要和API有相同的参数和声明
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString)
{
BOOL ret;
HookOff();
ret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//调原来的TextOut
HookOn();
return ret;
}
上面的代码是一个最简单的挂API钩子的例子,我要提醒大家的是,这段代码是我凭记忆写的,我以前的代码丢了,我没有编译测试过
因为我没有VC++1.52.所以代码可能会有错。
建议使用Borland c++,按16位编译。
如果用VC++1.52,则要改个选项
在VC++1.52的Option里,有个内存模式的设置,选大模式,和"DS!=SS DS Load on Function entry.",切记,否则会系统崩溃。
有什么不明白的可以给我写信
yeedong@163.net
Guest 1999-05-21 22:20:47
你这是16为的地址存取模式吧
你看这个MK_FP,win32不用了,
而且GetProcAddress(hKernel,"AllocCsToDsAlias")
这个API有用吗?
sorry
Guest 1999-05-21 22:31:47
亦东,想请教一个问题,
win32下,每个process有
自己的地址空间,process
A得到process B 的一个窗口C的handle,这个
handle的值 等于process B自己得到的window C 的handle值 吗?
我想应该不相等,但系统是如何转换的呢?(比如process A 向
window C 发消息,系统如何
知道process A 里的handle 和process B 里的
handle 都是指的window C)
. 是不是用duplicatehandle()?
(声明,我是真的不知道)
亦东 1999-05-21 22:54:48
这段代码就是十六位的。
你用Win32根本就不能编译。
32位没有AllocCsToDsAlias,因为在32位里不能写系统代码段(其实有办法,不是这样,不过比较麻烦)。
系统代码都在0x80000000以上,都是只读的。
所以要截WinAPI只能用16位的代码。
每个Process有自己的地址空间没错,但Window的句柄是共享的,同一个窗口在任何进程里的句柄都是一样的。
你可以在自己的进程里向任何窗口发消息。
Window的句柄很多,有的是共享的有的不是,
我也不知道那里有说明,一般是凭经验或试试看。
GUEST 1999-05-22 20:51:51
如果 window handle 换成
moudle handle呢?我
用moudlefirst,moudlenext遍历
得到的某个moudle 的句柄,
在任何一个process 中得到的
这个moudle handle 都是一样的值吗。(这个handle 和进程地址空间无关吗?)。
thx
亦东 1999-05-23 22:31:59
绝对有关
在Win32里module handle就是模块的起始地址。
说起来比较复杂
在95和NT里有些不同
在Win32里,每个模块(DLL,EXE)有一个ImageBase,这个数存放在DLL和EXE的文件头里。每个模块通常是不一样的。当Windows加载这个模块时优先考虑把模块放到Imagebase指定的地址,但有时会出现两个模块的地址重叠会有冲突,Windows会把模块移到与Imagebase最近的地址。所以Imagebase相同的模块在不同进程可能会在不同的地址上。这个地址就是module handle.Imagebase是可以在编译时指定的。
你用moudlefirst,moudlenext遍历得到的module handle是和进程有关的。
比如:你编了一个Imagebase为0x10000000的DLL A,进程A调用这个DLL A,在进程A里这个DLL被加载到地址0x10000000处,他的module handle为0x10000000,进程B也调用这个DLL A,但是进程B还调用另外一个DLL B,这另外的DLL B也是Imagebase为0x10000000的而且先加载,这是进程B的这个DLL A可能就被加载到0x13000000了,DLL A在进程B里的module handle 就是0x13000000了。
在95下,模块是共享的,也就是Windows只加载一份模块到内存,所有用到这个模块的进程都映射同一个模块,也就是说在95里每个模块在物理内存里只有一份。NT则不同,他为每个进程都加载一份模块。所以NT比95需要的内存多。所以在NT里不但在不同进程里的Module handle可能不同,连物理地址都是不同的。
yeedong@163.net
#13
我发现Joey的资料比我的全多了,就不来献丑了
#14
我来学习,这么多美味,够我吃几顿的!
#15
ab(ab): 谢谢你的提示,能够具体一点告诉我取词程序针对IE需要做什么特殊的处理吗?
大家注意:
我要解决的不是怎样鼠标取词!!!!!!
而是怎么样在IE下面取词!!!!!!!!!!!!!!!!
我手上的一分源码能够很好的在IE以外的大部分应用程序里面取词,但就是取不了IE下面的词,
大家注意:
我要解决的不是怎样鼠标取词!!!!!!
而是怎么样在IE下面取词!!!!!!!!!!!!!!!!
我手上的一分源码能够很好的在IE以外的大部分应用程序里面取词,但就是取不了IE下面的词,
#16
bensilver(Silver) 问的问题我只有听的份!
#17
IE真怪,View里面除了select一个窗口都没有,相当初用CIBAIII,根本不能用MSDN,滚动条一动,就不能取词了,CIBA2000感觉就好多了,事后想了想,看看了这方面的书和文档,感觉IE是在使用自己的一套GDI系统,所有的东西都是自己画的,我softice一直都懒得学,汇编只能用来跟踪,Win32下的汇编是真的不懂,在这里只有听听课了。
#18
怎么贴的都是老掉牙的资料!!!
#19
prodigy兄有没有新一些的资料?
#20
IE 是比较特殊,记事本输出文本只需要一句:
TextOut(dc, "text here", 10, 10);
而IE需要两句:
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
所以要先截取TextOut,保存起来,再截取BitBlt,判断两个dc2是否相同,还有坐标...
TextOut(dc, "text here", 10, 10);
而IE需要两句:
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
所以要先截取TextOut,保存起来,再截取BitBlt,判断两个dc2是否相同,还有坐标...
#21
关注
#22
ab(ab):
非常感谢!!
有一点不明白:
而IE需要两句:
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
截取TextOut不就已经可以得到“text here”了吗?为什么还要截取BitBlt ?
非常感谢!!
有一点不明白:
而IE需要两句:
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
截取TextOut不就已经可以得到“text here”了吗?为什么还要截取BitBlt ?
#23
是呀,ab能否把你的作品寄来用用,看看效果如何再说!!!
#24
???
#25
to bensilver:
虽然从第一句 TextOut() 就可以得到字符串,但并不能确定这个字符串是否自己想要的,有可能是写在别的窗口里面的,也有可能是写在同一窗口其它位置的,只有到 BitBlt() 时再确定是否同一窗口和是否同一位置。是否同一窗口可以用 TextOut 的目标 dc 和 BitBlt 的源 dc 是否相同确定。是否同一位置可以用 TextOut 的坐标和 BitBlt 的源坐标是否相同确定。最后,即使这两个条件都满足,当然还要确定一下:鼠标恰好处在 BitBlt 的目标坐标上。这三个坐标之间好像还需要转换,而且各个版本的 IE 之间还有小小的区别。
最后,虽然这样能取到大多数的词,但有的版本的IE取不到,具体原因“好像”就是各个版本IE的ViewportOrg不同,肯定有解决办法的,我没有仔细琢磨,要是能解决,还请告诉我怎么回事。还有AcrobatReader,要是不通过plug-in(需要100美元的授权费:),不知道能否取到词。
虽然从第一句 TextOut() 就可以得到字符串,但并不能确定这个字符串是否自己想要的,有可能是写在别的窗口里面的,也有可能是写在同一窗口其它位置的,只有到 BitBlt() 时再确定是否同一窗口和是否同一位置。是否同一窗口可以用 TextOut 的目标 dc 和 BitBlt 的源 dc 是否相同确定。是否同一位置可以用 TextOut 的坐标和 BitBlt 的源坐标是否相同确定。最后,即使这两个条件都满足,当然还要确定一下:鼠标恰好处在 BitBlt 的目标坐标上。这三个坐标之间好像还需要转换,而且各个版本的 IE 之间还有小小的区别。
最后,虽然这样能取到大多数的词,但有的版本的IE取不到,具体原因“好像”就是各个版本IE的ViewportOrg不同,肯定有解决办法的,我没有仔细琢磨,要是能解决,还请告诉我怎么回事。还有AcrobatReader,要是不通过plug-in(需要100美元的授权费:),不知道能否取到词。
#26
忘记打勾了。
#27
我想要分。。。。。。。
#28
我想要分。。。。。。。
#29
学习!
#30
关注。。。。。。。
#31
听课。
怎么没有新鲜一点的文章?
怎么没有新鲜一点的文章?
#32
我是新加盟者,来看一看,学一学!
#33
IE取词的确很麻烦
因为它往内存DC里写东西,而不是屏幕DC。这样坐标就全乱套了。
我做的时候发现其实是能截取到的,只是判断位置的程序把它给忽略了。
因此你要改的是判断位置的程序。
而且写字的方法也变了,就是同在IE里的不同地方,比如SELECT或者INPUT都是用的不同的方法。
但是,如果你把GDI函数都读全了,对TEXTOUT的全部相关函数都掌握了,比如各种坐标变换,文本标尺定位……这个位置还是能确定的。
另外好象遮盖法不管用了,就是以前做个“针尖窗口”去挡词的方法,需要用GDI函数强迫重绘。
最最烦的是WIN9X下的了,因为截取的是16位的GDI,根本没有UNICODE到ANSI的转换函数,明明抓出来却是乱码……
IE每个版本还不一样,烦
不过有个捷径,用S-ICE跟踪已有的屏幕取词程序。 :)
因为它往内存DC里写东西,而不是屏幕DC。这样坐标就全乱套了。
我做的时候发现其实是能截取到的,只是判断位置的程序把它给忽略了。
因此你要改的是判断位置的程序。
而且写字的方法也变了,就是同在IE里的不同地方,比如SELECT或者INPUT都是用的不同的方法。
但是,如果你把GDI函数都读全了,对TEXTOUT的全部相关函数都掌握了,比如各种坐标变换,文本标尺定位……这个位置还是能确定的。
另外好象遮盖法不管用了,就是以前做个“针尖窗口”去挡词的方法,需要用GDI函数强迫重绘。
最最烦的是WIN9X下的了,因为截取的是16位的GDI,根本没有UNICODE到ANSI的转换函数,明明抓出来却是乱码……
IE每个版本还不一样,烦
不过有个捷径,用S-ICE跟踪已有的屏幕取词程序。 :)
#34
关注。
#35
关注
#36
好象有张光盘不是公布了源代码了吗?大家还讨论什么?
马飞涛
http://iflower.myrice.com
马飞涛
http://iflower.myrice.com
#37
900分在哪?
#38
如果哪位朋友帮我解决了,我会另外发两个300分的贴子答谢!
#39
对于IE4下的取词:
1。必须截取ExtTextOutW_16
具体位置的得到方法:
fpExtTextOutW_16 = GetProcAddress(GetModuleHandle("GDI.EXE"), "GetTextMetrics");
WORD offset=(WORD)((DWORD)fpExtTextOutW_16&0xffff);
offset+=0x20;
DWORD uAddr=((DWORD)fpExtTextOutW_16 & 0xffff0000)+offset;
fpExtTextOutW_16=(FARPROC)uAddr;
2。转换被截取函数的显示坐标时与一般情况稍有不同:不要做DPtoLP
3。注意取出的词是unicode
1。必须截取ExtTextOutW_16
具体位置的得到方法:
fpExtTextOutW_16 = GetProcAddress(GetModuleHandle("GDI.EXE"), "GetTextMetrics");
WORD offset=(WORD)((DWORD)fpExtTextOutW_16&0xffff);
offset+=0x20;
DWORD uAddr=((DWORD)fpExtTextOutW_16 & 0xffff0000)+offset;
fpExtTextOutW_16=(FARPROC)uAddr;
2。转换被截取函数的显示坐标时与一般情况稍有不同:不要做DPtoLP
3。注意取出的词是unicode
#40
补充:此法俺只在中文95 98 me 上试过可以。您要有别的平台
也可以试一下。
也可以试一下。
#41
fpExtTextOutW_16 = GetProcAddress(GetModuleHandle("GDI.EXE"), "GetTextMetrics");
WORD offset=(WORD)((DWORD)fpExtTextOutW_16&0xffff);
offset+=0x20;
DWORD uAddr=((DWORD)fpExtTextOutW_16 & 0xffff0000)+offset;
fpExtTextOutW_16=(FARPROC)uAddr;
#42
关注
#43
到http://iflower.363.net/pmzhz.htm看看吧
#44
IE需要两句:
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
请问IE这么做有什么原因?(嘻嘻,借别人的地盘用一下)
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
请问IE这么做有什么原因?(嘻嘻,借别人的地盘用一下)
#45
哈哈,好热闹呀!
我等结果吧!
我等结果吧!
#46
我喜欢这个。报名了。
#47
不知900分拿来有什么用(好像挺多的),可不可以换一点内功心法什么的?
#48
上面的个位大侠:
能不能把全部工程文打个包,送我一份?pageupdown1@263.net非常感谢!
能不能把全部工程文打个包,送我一份?pageupdown1@263.net非常感谢!
#49
push
#50
我记得这个贴子老早就有人讨论过。
#1
我想你试试加入IE的YUAN!try
#2
看看这个吧,或许会恍然大悟:
http://iflower.363.net/pmzhz.htm
http://iflower.363.net/pmzhz.htm
#3
900大分呢,在哪儿呢?哪儿呢!
#4
yeh, should give to Joey.
#5
我本来认为金山词霸的取词是通过某些接口,例如它如果发现某个控件里有文字就通过getwindowtext来获取那个词,它获得ie里的词我也认为应该有ie的sdk包提供了相应的api接口.
但,后来我自己做了个文本编辑器,并没有用到window9x中带的edit和richedit标准控件,但是..
金山词霸还是可以取词!!
这就说明,它可能的运作方式有
1.直接读取显示缓冲区....(我都觉得不太可能,除非字符以另外一种方式存在)
2.直接截获dc的输出,诸如textout之类的api,我没试过,好象很难.
但,后来我自己做了个文本编辑器,并没有用到window9x中带的edit和richedit标准控件,但是..
金山词霸还是可以取词!!
这就说明,它可能的运作方式有
1.直接读取显示缓冲区....(我都觉得不太可能,除非字符以另外一种方式存在)
2.直接截获dc的输出,诸如textout之类的api,我没试过,好象很难.
#6
我做过一个屏幕取词的东西,最后分成两大类:
一、以记事本为代表,直接用 TextOutA/W、ExtTextOutA/W 输出到屏幕;
二、以IE为代表,用 TextOutA/W、ExtTextOutA/W 输到某内存 DC 上,再 BitBlt 到屏幕,稍微复杂一点;
另外,Acrobat 系列好像有点不同。
一、以记事本为代表,直接用 TextOutA/W、ExtTextOutA/W 输出到屏幕;
二、以IE为代表,用 TextOutA/W、ExtTextOutA/W 输到某内存 DC 上,再 BitBlt 到屏幕,稍微复杂一点;
另外,Acrobat 系列好像有点不同。
#7
屏幕取词核心内幕
本文只对与几个关键性技术的实现细节进行讨论,其它的编程细节,请参考源程序。
32位到16位的形式替换
32位代码与16位代码的数据交换
动态修改Windows内核
1. 32bit到16bit的形式替换(Thunk)
形式替换是指那些允许从16位代码调用32位代码或从32位代码调用16位代码的技术。形式替换用于解决试图在同一操作系统或同一可执行程序上使16位代码与32位代码同时并存的问题,即16位代码与32位代码的混合编程技术。早期的DOS程序及Window3.x上的应用程序均为16位程序,Windows95及Windows NT虽然也可运行旧的16位程序,但它们的主流发展方向是32位应用程序。与Windows NT不同的是,Windows95不是一个“纯”32位操作系统,为了兼有令人满意的速度和与旧的16位程序的良好兼容性,其内核本身就是一个16位与32位的混合体,因此也为编程者使用形式替换提供了便利。Microsoft为编写形式替换程序提供了通用的接口及工具,但因LTW32中的形式替换并不复杂,所以使用了一些编程技巧,而避免了使用Microsoft复杂的开发工具。
形式替换中最主要的问题有两点:①16位与32位数据类型尺寸的变化,如16位代码到32位代码的一个重要变化是整型数int的长度加倍了 ;②是堆栈操作时,16位模式使用SS:SP堆栈指针控制栈顶,而在32位模式中使用ESP寄存器作堆栈指针控制栈顶。
在LTW32中,屏幕抓词功能由16位DLL实现,因而只需实现32位到16位的形式替换,为32位代码提供16位DLL的调用接口。CALL FWORD PTR是32位汇编代码的一种调用方法,它可让32位代码调用到16位代码。作为实现的关键,控制16位侧与32位侧各自的堆栈是编程的要点。
32位侧的调用代码:
_asm{
pusha
call fword ptr [func16bit] /* func16bit是16位被调用者的48位地址 */
popa
}
16位侧的调用代码:
int dummy;
static char stack[8192]; /* 16位代码的临时堆栈 */
static WORD stack_seg;
static WORD prev_seg;
static DWORD prev_ofs;
static WORD prev_ds;
_asm{
push ax;
push bx;
mov ax, ds;
mov bx, seg dummy;
mov ds, bx;
mov stack_seg, bx;
mov prev_ds, ax;
pop bx;
pop ax;
mov prev_seg, ss;
mov dword ptr prev_ofs, esp; /* 保存32位堆栈指针 */
mov ss, stack_seg;
mov sp, offset stack; /* 设置16位堆栈指针 */
add sp, 8192;
}
/* 此处加入16位代码要实现的功能 */
_asm{
mov ss, prev_seg;
mov esp, dword ptr prev_ofs; /* 恢复32位堆栈指针 */
mov ds, prev_ds;
lea sp, word ptr [bp-2];
pop ds;
pop bp;
dec bp;
db 66h;
retf;
}
由于调用中传递的参数有限,所以涉及的代码并不多,唯一比较复杂之处在16位侧,它临时转向一个16位堆栈,服务于来自32位的调用者的请求。在16位侧入口处,必须存放好32位调用者的32位堆栈的指针,并且在16位侧返回时恢复它。由代码可见,这个过程是不可重入的,即一次只支持一个调用者,由于调用者只有LTW32的32位侧代码,所以此限制可以满足。另外,在16位代码返回时,必须恢复32位调用者的48位堆栈指针(SS:ESP)而不是32位的CS:SP,而且必须用一个48位地址(16:32)远返回(66h RETF)到它的32位调用者。
处理好这些细节,实现32位到16位的形式替换实际上是很简单的。
2. 32位代码与16位代码的数据交换
32位代码使用16位段地址加32位线性地址(16:32)的地址形式,16位代码使用16位段选择符(selector)加16位偏移(16:16)的地址形式。在Windows 95的32位侧,段址28h是系统段,即通过CS=28h或DS=28h可以访问系统的4G空间,其它应用程序的地址空间都是映射到28h段的4G空间中。Windows95中所有32位进程的地址空间(共4G)的高2G(80000000H~FFFFFFFFH)全部映射到28:80000000~28:FFFFFFFFH。即这块区域是所有32位程序共享的。这里一般存放系统DLL、虚拟设备驱动程序(VxD)、内存映射文件、16位应用程序和16位全局堆等。最后一项很重要,这为32位代码与16位代码交换数据提供了一个简便的方法。因为16位程序的段选择符的基址即是其所映射的系统段中的线性地址,这样,只要能够得到这个线性地址,32位代码就可以轻易地访问到16位程序的数据(LTW32 的32位侧使用此方法从16位侧获得屏幕截获的信息)。而16位段选择符的线性基址可以通过使用系统调用GetSelectorBase()得到,具体实现可参考源程序。线性地址计算的例子如下:
16位地址:07F2:1234H
段选择符 07F2H的线性基址为:82F41300H
段选择符 07F2H的尺寸为:4000H
∵ 82F41300H + 1234H = 82F42534
∴ 对应的32位线性地址为28:82F42534H
3. 动态修改Windows内核
如前所述,Windows95不是一个“纯”32位操作系统,其内核模块中的USER和GDI均是用16位代码实现的。USER32.DLL和GDI32.DLL只是16位的USER.EXE和GDI.EXE的32位调用接口。因此,如果屏幕截获程序用32位代码实现,则只能截获32位应用程序对USER32.DLL和GDI32.DLL的调用,无法截获16位应用程序对USER.EXE和GDI.EXE的调用,所以如果想截获所有应用程序(包括Windows95的桌面程序Explorer)中有关屏幕输出的系统调用,则应该用16位代码实现屏幕截获功能。这就是LTW32为什么不是“纯”32位应用程序的原因。LTW32主要截获两个系统调用TextOut()和ExtTextOut(),方法很简单,把这两个函数的头五个字节修改为一个JMP FAR 指令,使得对这两个函数的调用均转向屏幕截获程序。这就涉及到一个关键问题:动态修改Windows的代码。
在传统的DOS程序中,动态修改程序代码无任何困难,但在Windows中则不然,因为在Windows中,代码可被同一程序的多个实例(进程)共享,所以系统不允许应用程序动态的修改代码。在16位侧,内存的可读、写属性是与段选择符联系在一起的。段选择符基本上可分为两类:数据段选择符和代码段选择符。前者可读、可写、不可执行;后者可读、可执行、不可写。Windows提供了这两类段选择符相转换的系统调用。未公开的16位系统调用AllocCStoDSAlias()为给定的代码段选择符分配一个具有相同线性基址和尺寸的数据段别名(DS Alias)。通过DS别名可以对给定的代码段进行修改。AllocCStoDSAlias()的使用方法如下:
WORD (FAR PASCAL *AllocCStoDSAlias)(WORD);
AllocCStoDSAlias = GetProcAddress(
GetModuleHandle(“KERNEL”), ”ALLOCCSTODSALIAS”);
调用参数为给定的代码选择符,调用成功时返回一个线性基址和尺寸均与原代码选择符相同的DS别名。当不再使用此DS别名时,要用系统调用FreeSelector()把DS别名释放掉。
使用上述技术,就可实现动态修改Windows代码,从而改变GDI的系统调用TextOut()和ExtTextOut()的执行动作,实时地截获屏幕输出,为实现鼠标随动翻译提供可能。
把上述的32位到16位的形式替换、32位代码与16位代码的数据交换、动态修改Windows内核等技术综合应用在一起,配合单词查找算法和词组分析算法就可以实现鼠标随动翻译功能。
本文只对与几个关键性技术的实现细节进行讨论,其它的编程细节,请参考源程序。
32位到16位的形式替换
32位代码与16位代码的数据交换
动态修改Windows内核
1. 32bit到16bit的形式替换(Thunk)
形式替换是指那些允许从16位代码调用32位代码或从32位代码调用16位代码的技术。形式替换用于解决试图在同一操作系统或同一可执行程序上使16位代码与32位代码同时并存的问题,即16位代码与32位代码的混合编程技术。早期的DOS程序及Window3.x上的应用程序均为16位程序,Windows95及Windows NT虽然也可运行旧的16位程序,但它们的主流发展方向是32位应用程序。与Windows NT不同的是,Windows95不是一个“纯”32位操作系统,为了兼有令人满意的速度和与旧的16位程序的良好兼容性,其内核本身就是一个16位与32位的混合体,因此也为编程者使用形式替换提供了便利。Microsoft为编写形式替换程序提供了通用的接口及工具,但因LTW32中的形式替换并不复杂,所以使用了一些编程技巧,而避免了使用Microsoft复杂的开发工具。
形式替换中最主要的问题有两点:①16位与32位数据类型尺寸的变化,如16位代码到32位代码的一个重要变化是整型数int的长度加倍了 ;②是堆栈操作时,16位模式使用SS:SP堆栈指针控制栈顶,而在32位模式中使用ESP寄存器作堆栈指针控制栈顶。
在LTW32中,屏幕抓词功能由16位DLL实现,因而只需实现32位到16位的形式替换,为32位代码提供16位DLL的调用接口。CALL FWORD PTR是32位汇编代码的一种调用方法,它可让32位代码调用到16位代码。作为实现的关键,控制16位侧与32位侧各自的堆栈是编程的要点。
32位侧的调用代码:
_asm{
pusha
call fword ptr [func16bit] /* func16bit是16位被调用者的48位地址 */
popa
}
16位侧的调用代码:
int dummy;
static char stack[8192]; /* 16位代码的临时堆栈 */
static WORD stack_seg;
static WORD prev_seg;
static DWORD prev_ofs;
static WORD prev_ds;
_asm{
push ax;
push bx;
mov ax, ds;
mov bx, seg dummy;
mov ds, bx;
mov stack_seg, bx;
mov prev_ds, ax;
pop bx;
pop ax;
mov prev_seg, ss;
mov dword ptr prev_ofs, esp; /* 保存32位堆栈指针 */
mov ss, stack_seg;
mov sp, offset stack; /* 设置16位堆栈指针 */
add sp, 8192;
}
/* 此处加入16位代码要实现的功能 */
_asm{
mov ss, prev_seg;
mov esp, dword ptr prev_ofs; /* 恢复32位堆栈指针 */
mov ds, prev_ds;
lea sp, word ptr [bp-2];
pop ds;
pop bp;
dec bp;
db 66h;
retf;
}
由于调用中传递的参数有限,所以涉及的代码并不多,唯一比较复杂之处在16位侧,它临时转向一个16位堆栈,服务于来自32位的调用者的请求。在16位侧入口处,必须存放好32位调用者的32位堆栈的指针,并且在16位侧返回时恢复它。由代码可见,这个过程是不可重入的,即一次只支持一个调用者,由于调用者只有LTW32的32位侧代码,所以此限制可以满足。另外,在16位代码返回时,必须恢复32位调用者的48位堆栈指针(SS:ESP)而不是32位的CS:SP,而且必须用一个48位地址(16:32)远返回(66h RETF)到它的32位调用者。
处理好这些细节,实现32位到16位的形式替换实际上是很简单的。
2. 32位代码与16位代码的数据交换
32位代码使用16位段地址加32位线性地址(16:32)的地址形式,16位代码使用16位段选择符(selector)加16位偏移(16:16)的地址形式。在Windows 95的32位侧,段址28h是系统段,即通过CS=28h或DS=28h可以访问系统的4G空间,其它应用程序的地址空间都是映射到28h段的4G空间中。Windows95中所有32位进程的地址空间(共4G)的高2G(80000000H~FFFFFFFFH)全部映射到28:80000000~28:FFFFFFFFH。即这块区域是所有32位程序共享的。这里一般存放系统DLL、虚拟设备驱动程序(VxD)、内存映射文件、16位应用程序和16位全局堆等。最后一项很重要,这为32位代码与16位代码交换数据提供了一个简便的方法。因为16位程序的段选择符的基址即是其所映射的系统段中的线性地址,这样,只要能够得到这个线性地址,32位代码就可以轻易地访问到16位程序的数据(LTW32 的32位侧使用此方法从16位侧获得屏幕截获的信息)。而16位段选择符的线性基址可以通过使用系统调用GetSelectorBase()得到,具体实现可参考源程序。线性地址计算的例子如下:
16位地址:07F2:1234H
段选择符 07F2H的线性基址为:82F41300H
段选择符 07F2H的尺寸为:4000H
∵ 82F41300H + 1234H = 82F42534
∴ 对应的32位线性地址为28:82F42534H
3. 动态修改Windows内核
如前所述,Windows95不是一个“纯”32位操作系统,其内核模块中的USER和GDI均是用16位代码实现的。USER32.DLL和GDI32.DLL只是16位的USER.EXE和GDI.EXE的32位调用接口。因此,如果屏幕截获程序用32位代码实现,则只能截获32位应用程序对USER32.DLL和GDI32.DLL的调用,无法截获16位应用程序对USER.EXE和GDI.EXE的调用,所以如果想截获所有应用程序(包括Windows95的桌面程序Explorer)中有关屏幕输出的系统调用,则应该用16位代码实现屏幕截获功能。这就是LTW32为什么不是“纯”32位应用程序的原因。LTW32主要截获两个系统调用TextOut()和ExtTextOut(),方法很简单,把这两个函数的头五个字节修改为一个JMP FAR 指令,使得对这两个函数的调用均转向屏幕截获程序。这就涉及到一个关键问题:动态修改Windows的代码。
在传统的DOS程序中,动态修改程序代码无任何困难,但在Windows中则不然,因为在Windows中,代码可被同一程序的多个实例(进程)共享,所以系统不允许应用程序动态的修改代码。在16位侧,内存的可读、写属性是与段选择符联系在一起的。段选择符基本上可分为两类:数据段选择符和代码段选择符。前者可读、可写、不可执行;后者可读、可执行、不可写。Windows提供了这两类段选择符相转换的系统调用。未公开的16位系统调用AllocCStoDSAlias()为给定的代码段选择符分配一个具有相同线性基址和尺寸的数据段别名(DS Alias)。通过DS别名可以对给定的代码段进行修改。AllocCStoDSAlias()的使用方法如下:
WORD (FAR PASCAL *AllocCStoDSAlias)(WORD);
AllocCStoDSAlias = GetProcAddress(
GetModuleHandle(“KERNEL”), ”ALLOCCSTODSALIAS”);
调用参数为给定的代码选择符,调用成功时返回一个线性基址和尺寸均与原代码选择符相同的DS别名。当不再使用此DS别名时,要用系统调用FreeSelector()把DS别名释放掉。
使用上述技术,就可实现动态修改Windows代码,从而改变GDI的系统调用TextOut()和ExtTextOut()的执行动作,实时地截获屏幕输出,为实现鼠标随动翻译提供可能。
把上述的32位到16位的形式替换、32位代码与16位代码的数据交换、动态修改Windows内核等技术综合应用在一起,配合单词查找算法和词组分析算法就可以实现鼠标随动翻译功能。
#8
今天没有时间,先来报到,看看再说!
#9
我来听课
#10
不打算要分,進來學點東西。
#11
感兴趣!
#12
以下是别人的文章,不知对你有没有帮助。
“金山词霸”屏幕取词技术揭密(讨论稿)
主题 屏幕取词技术系列讲座(一)
作者 亦东
很多人对这个问题感兴趣。
原因是这项技术让人感觉很神奇,也很有商业价值。
现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。
但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。
大约每周一两次。想知道的人就常常来看看吧!
一.基础知识
首先想编这种程序需要一些基础知识。
会用Vc++,包括16/32位。
精通Windows API特别是GDI,KERNEL部分。
懂汇编语言,会用softice调试程序,因为这种程序最好用softice调试。
二.基本原理
在Window 3.x时代,windows系统提供的字符输出函数只有很少的几个。
TextOut
ExtTextOut
DrawText
......
其中DrawText最终是用ExtTextOut实现的。
所以Windows的所有字符输出都是由调用TextOut和ExtTextOut实现的。因此,如果你可以修改这两个函数的入口,让程序先调用你自己的一个函数再调用系统的字符输出,你就可以得到Windows所有输出的字符了。
到了Windows95时代,原理基本没变,但是95比3.x要复杂。开始的时候,一些在windows3.x下编写的取词软件仍然可以是使用。但是后来出了个IE4,结果很多词典软件就因为不支持IE4而被淘汰了,但同时也给一些软件创造了机会,如金山词霸。其实IE4的问题并不复杂,只不过它的输出的是unicode字符,是用TextOutW和ExtTextOutW输出的。知道了这一点,只要也截取就可以了。不过实现方法复杂一点,以后会有详细讲解。现在又出了个IE5,结果词霸也不好用了,微软真是#^@#$%$*&^&#@#@..........
我研究后找到了一种解决办法,但还有些问题,有时会取错,正在继续研究,希望大家共同探讨。
另外还有WindowsNT,原理也是一样,只是实现方法和95下完全不同。
三.技术要点
要实现取词,主要要解决以下技术问题。
1.截取API入口,获得API的参数。
2.安全地潜入Windows内部,良好地兼容Windows的各个版本
3.计算鼠标所在的单词和字母。
4.如果你在Window95下,做32位程序,还涉及Windows32/16混合编程的技术。
今天先到这里吧!最好准备一份softice for 95/98和金山词霸,让我们先来分析一下别人是怎么做的。
欢迎与我联系
E-Mail:yeedong@163.net
Guest 1999-04-30 16:00:48
请问用VC自己的DEBUGGER不行吗?为什么要用SOFTICE? 我没用过SOFTICE,它有什么特别之处吗?
葫芦 1999-04-30 19:15:03
本人对这个问题也有兴趣,以前研究过16位版本截获TextOut和ExtTextOut的过程;
但对金山词霸,用Softice跟踪,发现SetWindowsHookEx在程序装载时就安装了鼠标钩子,暂停取字/恢复取字只是设置的内部变量,但本人发现金山词霸并没有象16位版本那样修改TextOut和ExtTextOut。
苍蝇 (555021552) 1999-05-02 08:56:57
有哪位大虾愿意先介绍一下SOFTICE?
蟑螂 1999-05-04 13:58:22
把金山词霸的cjktl95.dll用tdump分析可以看到它根本用的不是hook,而是用了一些kernel32.dll中的Win32 SDK里没有包含的函数,使用这些函数来替换改写原先的几个GDI函数。看来是Win95未公开的一些东西。有些人知道,为什么不肯说出来呢?未必见得多么高深!
葫芦 1999-05-05 23:28:07
你可能没有研究过它的目标代码,怎么能断言没有使用hook呢? 其实金山词霸一运行就安装了鼠标钩子。
亦东 1999-05-07 09:52:42
未必见得多么高深?
你知道有些人是怎么知道这些Win95未公开的东西的吗?你不会是以为微软偷偷告诉他们的吧?其实他们大多和我一样是用softice自己跟踪Win95跟出来的。
还有你从cjktl95.dll里tdump出的未公开函数和hook无关,那是做别的用的。
setwindowshook在user里
亦东 1999-05-10 16:16:14
从cjktl95.dll里tdump出的未公开函数和32位和16位之间的互调有关。
主题 屏幕取词技术系列讲座(二)
作者 亦东
很抱歉让大家久等了!
我看了一些人的回帖,发现很多人对取词的原理还是不太清楚。
首先我来解释一下hook问题。词霸中的确用到了hook,而且他用了两种hook其中一种是Windows标准hook,通过SetWindowHook安装一个回调函数,它安装了一个鼠标hook,是为了可以及时响应鼠标的消息用的和取词没太大关系。
另一种钩子是API钩子,这才是取词的核心技术所在。他在TextOut等函数的开头写了一个jmp语句,跳转到自己的代码里。
你用softice看不到这个跳转语句是因为它只在取词的一瞬间才存在,平时是没有的。
你可以在TextOut开头设一个读写断点
bpm textout
再取词,就会找到词霸用来写钩子的代码了。
/**********************************
所以我在次强调,想学这种技术一定要懂汇编语言和熟练使用softice.
**********************************/
至于从cjktl95中dump出来的未公开函数是和Windows32/16混合编程有关的,以后我会提到他们。
我先来讲述取词的过程,
0 判断鼠标是否在一个地方停留了一段时间
1 取得鼠标当前位置
2 以鼠标位置为中心生成一个矩形
3 挂上API钩子
4 让这个矩形产生重画消息
5 在钩子里等输出字符
6 计算鼠标在哪个单词上面,把这个单词保存下来
7 如果得到单词则摘掉API钩子,在一段时间后,无论是否得到单词都摘掉API钩子
8 用单词查词库,显示解释框。
很多步骤实现起来都有一些难度,所以在中国可以做一个完善的取词词典的人屈指可数。
其中0,1,2,7,8比较简单就不提了。
先说如何挂钩子:
所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句,跳转到自己的代码里。
步骤如下:
1.取得Windows API入口,用GetProcAddress实现
2.保存API入口的前五个字节,因为JMP是0xEA,地址是4个字节
3.写入跳转语句
这步最复杂
Windows的代码段本来是不可以写的,但是Microsoft给自己留了个后门。
有一个未公开函数是AllocCsToDsAlias,
UINT WINAPI ALLOCCSTODSALIAS(UINT);
你可以取到这个函数的入口,把API的代码段的选择符(要是不知道什么是选择符,就先去学学保护模式编程吧)传给他,他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。
这就是取词技术的最核心的东东,不止取词,连外挂中文平台全屏汉化都是使用的这种技术。现在知道为什么这么简单的几句话却很少知道了吧?因为太多的产品使用他,太多的公司靠他赚钱了。
这些公司和产品有:中文之星,四通利方,南极星,金山词霸,实达铭泰的东方快车,roboword,译典通,即时汉化专家等等等等。。。。还有至少20多家小公司。他们的具体实现虽然不同,但大致原理是相同的。
我这些都是随手写的,也没有提纲之类的东西,以后如果有机会我会整理一下,大家先凑合着看吧!xixi...
葫芦 1999-05-06 14:52:30
你说的这个技术是16位的,至少金山词霸III没有采用16位的AllocCsToDsAlias函数,也根本没有修改TextOut开始的语句为JMP XXXXXXXX。softice本人非常精通,是绝对不会错的!
葫芦 1999-05-06 15:38:40
谁假冒吾名,坏吾名声?!本人在此郑重宣布,以上贴子非本人所发!
经过跟踪分析,金山词霸III确实修改了TextOut的入口为转跳到自身代码的JMP XXXXXXXX语句,只是修改没有调用AllocCsToDsAlias,也不是在取字时才去修改,而是常常修改(估计是在Timer中进行的)。
亦东 1999-05-06 17:25:23
我说过词霸里用AllocCsToDsAlias了吗?
我是说AllocCsToDsAlias,的确可以用,但词霸里是用DPMI的Int 31做的,其实本质是一样的,你用softice跟一下就知道了
bpint 31 设一个中断断点。
实际上我至少有五种方法写Windows代码段
我介绍使用AllocCsToDsAlias是因为这种方法最简单,其他方法要麻烦得多。
词霸用int 31来做是出于兼容性的考虑,
AllocCsToDsAlias毕竟不是公开函数,但DPMI却是标准。
词霸在你鼠标在某一点停留超过200ms时就会取词,所以他有一个定时器,会经常修改API入口。
看来“假”葫芦只是自以为非常精通softice.
mao 1999-05-06 19:29:28
在微软的MSDN中有一个程序,包含了全部的Source code, 名字好象叫"Inject" 或 stealth什么的,忘了。提供了一个很完善的hook任何一个windows函数的功能。对win16完全适用,在win95下也有用,但NT下不行。
建议大家去找找这个例子看看会很有帮助。
此外有本清华出的微软的Advanced Windows也介绍了具体的方法。
建议亦东干脆把source code给open出来,让有兴趣的朋友用起来更方便,把取词技术可发展的应用发扬光大!
亦东 1999-05-07 09:40:56
我不主张“把取词技术可发展的应用发扬光大”,这也是我掌握了这项技术一年多才公开它的原因。我公开它并不是想让大家都来做字典软件,相反我希望不要再有人做字典了,现在做字典的人已经太多了。看到某种软件有利可图,大家就一哄而上,这种恶性竞争对中国软件业的发展是极其有害的。我公开他的目的是希望提高大家的编程水平。你会发现在研究这项技术的过程中你的编程水平和对Windows的理解程度会有质的飞跃,我本人就从中获益匪浅。
MSDN中是有这样的代码。
甚至有一个叫ProcHook.Dll,提供SetProcAddress之类的函数,但是没有源码。源码在1994年的 MSJ 上很难弄到的。
源码我会分几次公开,每次会有详细的说明。
想要源码的人,准备一份VC++1.52或Borland C++,最好还有softice,下次我会给出一段代码,教你如何修改Windows的代码。
老冒 (555036) 1999-05-07 11:56:46
如果认为屏幕取词的应用就是做字典,就大错特错了。其实关于拦劫windows api的东东早就在93年的Undocument Windows上公开过了。
其实Adobe的Adobe Type Manager在Windows 3.0的时代就通过这种办法实现了漂亮的字体. (现在有TTF不需要ATM了)
MSDN上的那个东东是有全部source和sample, 我抓下来编译过。是1996年夏天的一张MSDN Level 2光碟上的,现在也不知搁哪里了,有兴趣的朋友自己找去吧。
还是open 完整的source好,很多朋友其实只要用这项技术,并不太想知道细节,不是吗.
亦东 1999-05-07 13:28:19
这种技术不是做字典全屏汉化就是外挂语言平台,自从王志东使用它以来,就没用来编过其他软件,也许有但我不知道。
原来有那么多书和其他资料上都有这种技术的资料还有例子,到是我孤陋寡闻了,以为大家都不知道,在这里给大家讲一些众所周知的东西。回去我要好好研究一下,看看Rasir Dex的词霸是从那里抄的。
我不知道某项技术中细节是不是重要,如果很多人只想用而不想自己编,那么楼下那50多个回帖是怎么回是?
老冒 1999-05-07 13:54:18
呵呵,你可千万要坚持把讲座做下去,否则那50多个回应的哥们企不要把我给痛扁了...:)
俺已两年多不碰底层技术的,这方面很落后啦...俺可应付不了这么多热切的求知朋友,亦东要顶住呵!
欢迎和俺多多交流探导!
P.S. 亦东大侠目前何方高就? 正在忙什么项目?交流交流
亦东 1999-05-07 17:46:06
没什么正经事做,到处瞎混呢!
葫芦 1999-05-08 21:50:29
我对此持否定观点,不要自作聪明,以为AllocCsToDsAlias就是能用的,其实AllocCsToDsAlias只是16位的Windows用的函数,32位的Win95程序不能使用此函数,不信你在VC 5.0或6.0中可以试试。
另外,int 31h也不是说能用就能拿来用的,在Windows 3.x下使用是没有问题的(本人还有这方面的文章发表),但在Win95下随意使用会产生GP错,主要原因是32位并不支持DPMI直接调用,不知亦东先生对此有没有研究,就在此发表诸多理论!本人就先请问:32位程序如何调用16位函数或动态库你懂不懂?
葫芦 1999-05-09 02:39:10
上面的这个帖子并没有攻击谁的意思,只是希望大家探讨问题都要本着认真的态度,不要不懂装懂,至少有一点大家要清楚:32位的程序根本不能使用 int 31h,调用16位的动态库Kernel中的AllocCsToDsAlias也并不是件简单的事。
nn_zdm (555031742) 1999-05-09 16:35:35
使用hook函数,可用的功能并非只是做字典全屏汉化和外挂语言平台。使用hook可以调试程序,就象你们说的softice其本身也是使用了hook函数。
nn_zdm (555031742) 1999-05-09 16:42:05
另外hook函数还可以使用在游戏修改工具中,本人就开发过此类工具。《整人专家》估计也是使用这种方法。当然还有另外两种方法。
亦东 1999-05-10 16:10:59
你们说的都有道理。
但hook有两种,一种是Windows标准钩子,通过SetWindowshook挂。
另一种是非标准的,通过在API入口写JMP XXXXXXXX来实现的。
softice的钩子更高级,他都挂到VXD上了。
从32为代码调用16位DLL碰巧我会。
打倒米D国主义!!!
瓜果 1999-05-10 17:07:00
谁知道在哪能搞到SOFTICE,我以前从未用过它!
葫芦 1999-05-10 21:39:46
愿继续拜读你以后的讲座。
SOFTICE吗?光盘上很多,有for DOS, for Windows95, for Windows NT 各个版本。
孙玮 (555031339) 1999-05-11 11:08:35
能否将 si for NT 上传到 10.82.46.33
(使用 ftp) user: haotao
pass: haotao123
tommy 1999-05-11 11:34:11
http://www.swww.com.cn/htm/down/others/main.html 可以下载
亦东 1999-05-11 14:01:19
最近忙于反美,暂时没时间再写了,过些时间才行,下次我会给出源码。
最新消息,美国海军被黑了。
http://www.nctsw.navy.mil/
打倒米D国主义!!!
黄金狮子 1999-05-12 13:19:32
我对各位大虾的讨论深感兴趣。
有几个问题想请教:
1.AllocCsToDsAlias 在32-bit下调用是否采用Thunk?
2.32-bit 下是否有类似function?
3.Jeffrey Richter的"Advanced Windows"里Remote Thread 的Thread Stack来远程注入DLL函数,因此不需上述Function.
4.我有MSJ 1994-1的ProcHook.dll的source code,不知用于WinNT需如何改动.
5.总而言之,有无WinNT下hook API的source code,请告知.
(我还有Softice 3.24 for Win95, 3.25 for WinNT.)
--这个主题很好
鼹鼠 1999-05-14 09:41:20
请大虾给我发一份MSJ 1994-1的ProcHool.dll的source code, 我现在急需这方面的资料。谢谢!!
Email Address: yanshg@263.net
下面是一个Australia人的API hook软件, 它是基于VxD技术。
Molten Home Page:
http://ourworld.compuserve.com/homepages/molten
黄金狮子 1999-05-14 14:34:54
在Win95和NT上,可通过WriteProcessMemory()直接写Code Segment.(原来以为"advanced Windows" 调CreateRemoteThread(),是因为WriteProcessMemory()只能写代码段和堆栈段)
阿涛 1999-05-14 19:45:05
如何亦东老兄要分步公布是PROCHOOK的代码就不必了,这个程序的代码很容易搞到,只须到MSJ的站点上查一下就可找到。
亦东 1999-05-14 21:44:39
大家到msj的大海里去捞针好了。
在95你WriteProcessMemory 写kernel user gdi试试,一定失败。
调CreateRemoteThread并不是为了写代码段,有别的用途,是为了在其他进程里分配内存。回去再好好看看“advanced Windows" 最好用一个程序试试,你就明白了。
其实在NT4.0调CreateRemoteThread是没必要的,这是为了兼容NT3.51.
nn_zdm (555031742) 1999-05-18 13:57:48
利用CreateRemoteThread()函数,在WinNT4.0中使用很有用,它是在winNT中闯过进程边界的三种办法之一,在WinNT中,使用它可以进行远程调试,及修改他人代码.
nn_zdm (555031742) 1999-05-18 14:07:17
在winNT中用WriteProcessMemory()写code
代码是可以的,win95没试过,但MSDN上说是可以的.不过,可能没什么用,因为CreateRemoteThread()函数只在winNT中有用.
如:
WriteProcessMemory(...,"LoadLibrary(...,"mydll.dll",..).
CreateRemoteThread(...)
nn_zdm (555031742) 1999-05-18 14:14:19
上面写漏了,应是
WriteProcessMemory(...,"LoadLibrary(...,"mydll.dll",..). ");
CreateRemoteThread(...) ;
主题 关于屏幕取词的讨论(三)
作者 亦东
让大家久等,很抱歉,前些时候工作忙硬盘又坏了,太不幸了。
这回来点真格的。
咱们以截取TextOut为例。
下面是代码:
//截取TextOut
typedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT);
ALLOCCSTODSALIAS AllocCsToDsAlias;
BYTE NewValue[5];//保存新的入口代码
BYTE OldValue[5];//API原来的入口代码
unsigned char * Address=NULL;//可写的API入口地址
UINT DsSelector=NULL;//指向API入口的可写的选择符
WORD OffSetEntry=NULL;//API的偏移量
BOOL bHookAlready = FALSE; //是否挂钩子的标志
BOOL InitHook()
{
HMODULE hKernel,hGdi;
hKernel = GetModuleHandle("Kernel");
if(hKernel==NULL)
return FALSE;
AllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//这是未公开的API所以要这样取地址
if(AllocCsToDsAlias==NULL)
return FALSE;
hGdi = GetModuleHandle("Gdi");
if(hmGdi==NULL)
return FALSE;
FARPROC Entry = GetProcAddress(hGdi,"TextOut");
if(Entry==NULL)
return FALSE;
OffSetEntry = (WORD)(FP_OFF(Entry));//取得API代码段的选择符
DsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配一个等同的可写的选择符
Address = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址
NewValue[0]=0xEA;
*((DWORD*)(NewValue+1)) = (DWORD)MyTextOut;
OldValue[0]=Address[0];
*((DWORD*)(OldValue+1)) = *((DWORD*)(Address+1));
}
BOOL ClearHook()
{
if(bHookAlready)
HookOff();
FreeSelector(DsSelector);
}
BOOL HookOn()
{
if(!bHookAlready){
for(int i=0;i<5;i++){
Address[i]=NewValue[i];
}
bHookAlready=TRUE;
}
}
BOOL HookOff()
{
if(bHookAlready){
for(int i=0;i<5;i++){
Address[i]=OldValue[i];
}
bHookAlready=FALSE;
}
}
//钩子函数,一定要和API有相同的参数和声明
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString)
{
BOOL ret;
HookOff();
ret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//调原来的TextOut
HookOn();
return ret;
}
上面的代码是一个最简单的挂API钩子的例子,我要提醒大家的是,这段代码是我凭记忆写的,我以前的代码丢了,我没有编译测试过
因为我没有VC++1.52.所以代码可能会有错。
建议使用Borland c++,按16位编译。
如果用VC++1.52,则要改个选项
在VC++1.52的Option里,有个内存模式的设置,选大模式,和"DS!=SS DS Load on Function entry.",切记,否则会系统崩溃。
有什么不明白的可以给我写信
yeedong@163.net
Guest 1999-05-21 22:20:47
你这是16为的地址存取模式吧
你看这个MK_FP,win32不用了,
而且GetProcAddress(hKernel,"AllocCsToDsAlias")
这个API有用吗?
sorry
Guest 1999-05-21 22:31:47
亦东,想请教一个问题,
win32下,每个process有
自己的地址空间,process
A得到process B 的一个窗口C的handle,这个
handle的值 等于process B自己得到的window C 的handle值 吗?
我想应该不相等,但系统是如何转换的呢?(比如process A 向
window C 发消息,系统如何
知道process A 里的handle 和process B 里的
handle 都是指的window C)
. 是不是用duplicatehandle()?
(声明,我是真的不知道)
亦东 1999-05-21 22:54:48
这段代码就是十六位的。
你用Win32根本就不能编译。
32位没有AllocCsToDsAlias,因为在32位里不能写系统代码段(其实有办法,不是这样,不过比较麻烦)。
系统代码都在0x80000000以上,都是只读的。
所以要截WinAPI只能用16位的代码。
每个Process有自己的地址空间没错,但Window的句柄是共享的,同一个窗口在任何进程里的句柄都是一样的。
你可以在自己的进程里向任何窗口发消息。
Window的句柄很多,有的是共享的有的不是,
我也不知道那里有说明,一般是凭经验或试试看。
GUEST 1999-05-22 20:51:51
如果 window handle 换成
moudle handle呢?我
用moudlefirst,moudlenext遍历
得到的某个moudle 的句柄,
在任何一个process 中得到的
这个moudle handle 都是一样的值吗。(这个handle 和进程地址空间无关吗?)。
thx
亦东 1999-05-23 22:31:59
绝对有关
在Win32里module handle就是模块的起始地址。
说起来比较复杂
在95和NT里有些不同
在Win32里,每个模块(DLL,EXE)有一个ImageBase,这个数存放在DLL和EXE的文件头里。每个模块通常是不一样的。当Windows加载这个模块时优先考虑把模块放到Imagebase指定的地址,但有时会出现两个模块的地址重叠会有冲突,Windows会把模块移到与Imagebase最近的地址。所以Imagebase相同的模块在不同进程可能会在不同的地址上。这个地址就是module handle.Imagebase是可以在编译时指定的。
你用moudlefirst,moudlenext遍历得到的module handle是和进程有关的。
比如:你编了一个Imagebase为0x10000000的DLL A,进程A调用这个DLL A,在进程A里这个DLL被加载到地址0x10000000处,他的module handle为0x10000000,进程B也调用这个DLL A,但是进程B还调用另外一个DLL B,这另外的DLL B也是Imagebase为0x10000000的而且先加载,这是进程B的这个DLL A可能就被加载到0x13000000了,DLL A在进程B里的module handle 就是0x13000000了。
在95下,模块是共享的,也就是Windows只加载一份模块到内存,所有用到这个模块的进程都映射同一个模块,也就是说在95里每个模块在物理内存里只有一份。NT则不同,他为每个进程都加载一份模块。所以NT比95需要的内存多。所以在NT里不但在不同进程里的Module handle可能不同,连物理地址都是不同的。
yeedong@163.net
“金山词霸”屏幕取词技术揭密(讨论稿)
主题 屏幕取词技术系列讲座(一)
作者 亦东
很多人对这个问题感兴趣。
原因是这项技术让人感觉很神奇,也很有商业价值。
现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。
但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。
大约每周一两次。想知道的人就常常来看看吧!
一.基础知识
首先想编这种程序需要一些基础知识。
会用Vc++,包括16/32位。
精通Windows API特别是GDI,KERNEL部分。
懂汇编语言,会用softice调试程序,因为这种程序最好用softice调试。
二.基本原理
在Window 3.x时代,windows系统提供的字符输出函数只有很少的几个。
TextOut
ExtTextOut
DrawText
......
其中DrawText最终是用ExtTextOut实现的。
所以Windows的所有字符输出都是由调用TextOut和ExtTextOut实现的。因此,如果你可以修改这两个函数的入口,让程序先调用你自己的一个函数再调用系统的字符输出,你就可以得到Windows所有输出的字符了。
到了Windows95时代,原理基本没变,但是95比3.x要复杂。开始的时候,一些在windows3.x下编写的取词软件仍然可以是使用。但是后来出了个IE4,结果很多词典软件就因为不支持IE4而被淘汰了,但同时也给一些软件创造了机会,如金山词霸。其实IE4的问题并不复杂,只不过它的输出的是unicode字符,是用TextOutW和ExtTextOutW输出的。知道了这一点,只要也截取就可以了。不过实现方法复杂一点,以后会有详细讲解。现在又出了个IE5,结果词霸也不好用了,微软真是#^@#$%$*&^&#@#@..........
我研究后找到了一种解决办法,但还有些问题,有时会取错,正在继续研究,希望大家共同探讨。
另外还有WindowsNT,原理也是一样,只是实现方法和95下完全不同。
三.技术要点
要实现取词,主要要解决以下技术问题。
1.截取API入口,获得API的参数。
2.安全地潜入Windows内部,良好地兼容Windows的各个版本
3.计算鼠标所在的单词和字母。
4.如果你在Window95下,做32位程序,还涉及Windows32/16混合编程的技术。
今天先到这里吧!最好准备一份softice for 95/98和金山词霸,让我们先来分析一下别人是怎么做的。
欢迎与我联系
E-Mail:yeedong@163.net
Guest 1999-04-30 16:00:48
请问用VC自己的DEBUGGER不行吗?为什么要用SOFTICE? 我没用过SOFTICE,它有什么特别之处吗?
葫芦 1999-04-30 19:15:03
本人对这个问题也有兴趣,以前研究过16位版本截获TextOut和ExtTextOut的过程;
但对金山词霸,用Softice跟踪,发现SetWindowsHookEx在程序装载时就安装了鼠标钩子,暂停取字/恢复取字只是设置的内部变量,但本人发现金山词霸并没有象16位版本那样修改TextOut和ExtTextOut。
苍蝇 (555021552) 1999-05-02 08:56:57
有哪位大虾愿意先介绍一下SOFTICE?
蟑螂 1999-05-04 13:58:22
把金山词霸的cjktl95.dll用tdump分析可以看到它根本用的不是hook,而是用了一些kernel32.dll中的Win32 SDK里没有包含的函数,使用这些函数来替换改写原先的几个GDI函数。看来是Win95未公开的一些东西。有些人知道,为什么不肯说出来呢?未必见得多么高深!
葫芦 1999-05-05 23:28:07
你可能没有研究过它的目标代码,怎么能断言没有使用hook呢? 其实金山词霸一运行就安装了鼠标钩子。
亦东 1999-05-07 09:52:42
未必见得多么高深?
你知道有些人是怎么知道这些Win95未公开的东西的吗?你不会是以为微软偷偷告诉他们的吧?其实他们大多和我一样是用softice自己跟踪Win95跟出来的。
还有你从cjktl95.dll里tdump出的未公开函数和hook无关,那是做别的用的。
setwindowshook在user里
亦东 1999-05-10 16:16:14
从cjktl95.dll里tdump出的未公开函数和32位和16位之间的互调有关。
主题 屏幕取词技术系列讲座(二)
作者 亦东
很抱歉让大家久等了!
我看了一些人的回帖,发现很多人对取词的原理还是不太清楚。
首先我来解释一下hook问题。词霸中的确用到了hook,而且他用了两种hook其中一种是Windows标准hook,通过SetWindowHook安装一个回调函数,它安装了一个鼠标hook,是为了可以及时响应鼠标的消息用的和取词没太大关系。
另一种钩子是API钩子,这才是取词的核心技术所在。他在TextOut等函数的开头写了一个jmp语句,跳转到自己的代码里。
你用softice看不到这个跳转语句是因为它只在取词的一瞬间才存在,平时是没有的。
你可以在TextOut开头设一个读写断点
bpm textout
再取词,就会找到词霸用来写钩子的代码了。
/**********************************
所以我在次强调,想学这种技术一定要懂汇编语言和熟练使用softice.
**********************************/
至于从cjktl95中dump出来的未公开函数是和Windows32/16混合编程有关的,以后我会提到他们。
我先来讲述取词的过程,
0 判断鼠标是否在一个地方停留了一段时间
1 取得鼠标当前位置
2 以鼠标位置为中心生成一个矩形
3 挂上API钩子
4 让这个矩形产生重画消息
5 在钩子里等输出字符
6 计算鼠标在哪个单词上面,把这个单词保存下来
7 如果得到单词则摘掉API钩子,在一段时间后,无论是否得到单词都摘掉API钩子
8 用单词查词库,显示解释框。
很多步骤实现起来都有一些难度,所以在中国可以做一个完善的取词词典的人屈指可数。
其中0,1,2,7,8比较简单就不提了。
先说如何挂钩子:
所谓钩子其实就是在WindowsAPI入口写一个JMP XXXX:XXXX语句,跳转到自己的代码里。
步骤如下:
1.取得Windows API入口,用GetProcAddress实现
2.保存API入口的前五个字节,因为JMP是0xEA,地址是4个字节
3.写入跳转语句
这步最复杂
Windows的代码段本来是不可以写的,但是Microsoft给自己留了个后门。
有一个未公开函数是AllocCsToDsAlias,
UINT WINAPI ALLOCCSTODSALIAS(UINT);
你可以取到这个函数的入口,把API的代码段的选择符(要是不知道什么是选择符,就先去学学保护模式编程吧)传给他,他会返回一个可写的数据段选择符。这个选择符用完要释放的。用新选择符和API入口的偏移量合成一个指针就可以写windows的代码段了。
这就是取词技术的最核心的东东,不止取词,连外挂中文平台全屏汉化都是使用的这种技术。现在知道为什么这么简单的几句话却很少知道了吧?因为太多的产品使用他,太多的公司靠他赚钱了。
这些公司和产品有:中文之星,四通利方,南极星,金山词霸,实达铭泰的东方快车,roboword,译典通,即时汉化专家等等等等。。。。还有至少20多家小公司。他们的具体实现虽然不同,但大致原理是相同的。
我这些都是随手写的,也没有提纲之类的东西,以后如果有机会我会整理一下,大家先凑合着看吧!xixi...
葫芦 1999-05-06 14:52:30
你说的这个技术是16位的,至少金山词霸III没有采用16位的AllocCsToDsAlias函数,也根本没有修改TextOut开始的语句为JMP XXXXXXXX。softice本人非常精通,是绝对不会错的!
葫芦 1999-05-06 15:38:40
谁假冒吾名,坏吾名声?!本人在此郑重宣布,以上贴子非本人所发!
经过跟踪分析,金山词霸III确实修改了TextOut的入口为转跳到自身代码的JMP XXXXXXXX语句,只是修改没有调用AllocCsToDsAlias,也不是在取字时才去修改,而是常常修改(估计是在Timer中进行的)。
亦东 1999-05-06 17:25:23
我说过词霸里用AllocCsToDsAlias了吗?
我是说AllocCsToDsAlias,的确可以用,但词霸里是用DPMI的Int 31做的,其实本质是一样的,你用softice跟一下就知道了
bpint 31 设一个中断断点。
实际上我至少有五种方法写Windows代码段
我介绍使用AllocCsToDsAlias是因为这种方法最简单,其他方法要麻烦得多。
词霸用int 31来做是出于兼容性的考虑,
AllocCsToDsAlias毕竟不是公开函数,但DPMI却是标准。
词霸在你鼠标在某一点停留超过200ms时就会取词,所以他有一个定时器,会经常修改API入口。
看来“假”葫芦只是自以为非常精通softice.
mao 1999-05-06 19:29:28
在微软的MSDN中有一个程序,包含了全部的Source code, 名字好象叫"Inject" 或 stealth什么的,忘了。提供了一个很完善的hook任何一个windows函数的功能。对win16完全适用,在win95下也有用,但NT下不行。
建议大家去找找这个例子看看会很有帮助。
此外有本清华出的微软的Advanced Windows也介绍了具体的方法。
建议亦东干脆把source code给open出来,让有兴趣的朋友用起来更方便,把取词技术可发展的应用发扬光大!
亦东 1999-05-07 09:40:56
我不主张“把取词技术可发展的应用发扬光大”,这也是我掌握了这项技术一年多才公开它的原因。我公开它并不是想让大家都来做字典软件,相反我希望不要再有人做字典了,现在做字典的人已经太多了。看到某种软件有利可图,大家就一哄而上,这种恶性竞争对中国软件业的发展是极其有害的。我公开他的目的是希望提高大家的编程水平。你会发现在研究这项技术的过程中你的编程水平和对Windows的理解程度会有质的飞跃,我本人就从中获益匪浅。
MSDN中是有这样的代码。
甚至有一个叫ProcHook.Dll,提供SetProcAddress之类的函数,但是没有源码。源码在1994年的 MSJ 上很难弄到的。
源码我会分几次公开,每次会有详细的说明。
想要源码的人,准备一份VC++1.52或Borland C++,最好还有softice,下次我会给出一段代码,教你如何修改Windows的代码。
老冒 (555036) 1999-05-07 11:56:46
如果认为屏幕取词的应用就是做字典,就大错特错了。其实关于拦劫windows api的东东早就在93年的Undocument Windows上公开过了。
其实Adobe的Adobe Type Manager在Windows 3.0的时代就通过这种办法实现了漂亮的字体. (现在有TTF不需要ATM了)
MSDN上的那个东东是有全部source和sample, 我抓下来编译过。是1996年夏天的一张MSDN Level 2光碟上的,现在也不知搁哪里了,有兴趣的朋友自己找去吧。
还是open 完整的source好,很多朋友其实只要用这项技术,并不太想知道细节,不是吗.
亦东 1999-05-07 13:28:19
这种技术不是做字典全屏汉化就是外挂语言平台,自从王志东使用它以来,就没用来编过其他软件,也许有但我不知道。
原来有那么多书和其他资料上都有这种技术的资料还有例子,到是我孤陋寡闻了,以为大家都不知道,在这里给大家讲一些众所周知的东西。回去我要好好研究一下,看看Rasir Dex的词霸是从那里抄的。
我不知道某项技术中细节是不是重要,如果很多人只想用而不想自己编,那么楼下那50多个回帖是怎么回是?
老冒 1999-05-07 13:54:18
呵呵,你可千万要坚持把讲座做下去,否则那50多个回应的哥们企不要把我给痛扁了...:)
俺已两年多不碰底层技术的,这方面很落后啦...俺可应付不了这么多热切的求知朋友,亦东要顶住呵!
欢迎和俺多多交流探导!
P.S. 亦东大侠目前何方高就? 正在忙什么项目?交流交流
亦东 1999-05-07 17:46:06
没什么正经事做,到处瞎混呢!
葫芦 1999-05-08 21:50:29
我对此持否定观点,不要自作聪明,以为AllocCsToDsAlias就是能用的,其实AllocCsToDsAlias只是16位的Windows用的函数,32位的Win95程序不能使用此函数,不信你在VC 5.0或6.0中可以试试。
另外,int 31h也不是说能用就能拿来用的,在Windows 3.x下使用是没有问题的(本人还有这方面的文章发表),但在Win95下随意使用会产生GP错,主要原因是32位并不支持DPMI直接调用,不知亦东先生对此有没有研究,就在此发表诸多理论!本人就先请问:32位程序如何调用16位函数或动态库你懂不懂?
葫芦 1999-05-09 02:39:10
上面的这个帖子并没有攻击谁的意思,只是希望大家探讨问题都要本着认真的态度,不要不懂装懂,至少有一点大家要清楚:32位的程序根本不能使用 int 31h,调用16位的动态库Kernel中的AllocCsToDsAlias也并不是件简单的事。
nn_zdm (555031742) 1999-05-09 16:35:35
使用hook函数,可用的功能并非只是做字典全屏汉化和外挂语言平台。使用hook可以调试程序,就象你们说的softice其本身也是使用了hook函数。
nn_zdm (555031742) 1999-05-09 16:42:05
另外hook函数还可以使用在游戏修改工具中,本人就开发过此类工具。《整人专家》估计也是使用这种方法。当然还有另外两种方法。
亦东 1999-05-10 16:10:59
你们说的都有道理。
但hook有两种,一种是Windows标准钩子,通过SetWindowshook挂。
另一种是非标准的,通过在API入口写JMP XXXXXXXX来实现的。
softice的钩子更高级,他都挂到VXD上了。
从32为代码调用16位DLL碰巧我会。
打倒米D国主义!!!
瓜果 1999-05-10 17:07:00
谁知道在哪能搞到SOFTICE,我以前从未用过它!
葫芦 1999-05-10 21:39:46
愿继续拜读你以后的讲座。
SOFTICE吗?光盘上很多,有for DOS, for Windows95, for Windows NT 各个版本。
孙玮 (555031339) 1999-05-11 11:08:35
能否将 si for NT 上传到 10.82.46.33
(使用 ftp) user: haotao
pass: haotao123
tommy 1999-05-11 11:34:11
http://www.swww.com.cn/htm/down/others/main.html 可以下载
亦东 1999-05-11 14:01:19
最近忙于反美,暂时没时间再写了,过些时间才行,下次我会给出源码。
最新消息,美国海军被黑了。
http://www.nctsw.navy.mil/
打倒米D国主义!!!
黄金狮子 1999-05-12 13:19:32
我对各位大虾的讨论深感兴趣。
有几个问题想请教:
1.AllocCsToDsAlias 在32-bit下调用是否采用Thunk?
2.32-bit 下是否有类似function?
3.Jeffrey Richter的"Advanced Windows"里Remote Thread 的Thread Stack来远程注入DLL函数,因此不需上述Function.
4.我有MSJ 1994-1的ProcHook.dll的source code,不知用于WinNT需如何改动.
5.总而言之,有无WinNT下hook API的source code,请告知.
(我还有Softice 3.24 for Win95, 3.25 for WinNT.)
--这个主题很好
鼹鼠 1999-05-14 09:41:20
请大虾给我发一份MSJ 1994-1的ProcHool.dll的source code, 我现在急需这方面的资料。谢谢!!
Email Address: yanshg@263.net
下面是一个Australia人的API hook软件, 它是基于VxD技术。
Molten Home Page:
http://ourworld.compuserve.com/homepages/molten
黄金狮子 1999-05-14 14:34:54
在Win95和NT上,可通过WriteProcessMemory()直接写Code Segment.(原来以为"advanced Windows" 调CreateRemoteThread(),是因为WriteProcessMemory()只能写代码段和堆栈段)
阿涛 1999-05-14 19:45:05
如何亦东老兄要分步公布是PROCHOOK的代码就不必了,这个程序的代码很容易搞到,只须到MSJ的站点上查一下就可找到。
亦东 1999-05-14 21:44:39
大家到msj的大海里去捞针好了。
在95你WriteProcessMemory 写kernel user gdi试试,一定失败。
调CreateRemoteThread并不是为了写代码段,有别的用途,是为了在其他进程里分配内存。回去再好好看看“advanced Windows" 最好用一个程序试试,你就明白了。
其实在NT4.0调CreateRemoteThread是没必要的,这是为了兼容NT3.51.
nn_zdm (555031742) 1999-05-18 13:57:48
利用CreateRemoteThread()函数,在WinNT4.0中使用很有用,它是在winNT中闯过进程边界的三种办法之一,在WinNT中,使用它可以进行远程调试,及修改他人代码.
nn_zdm (555031742) 1999-05-18 14:07:17
在winNT中用WriteProcessMemory()写code
代码是可以的,win95没试过,但MSDN上说是可以的.不过,可能没什么用,因为CreateRemoteThread()函数只在winNT中有用.
如:
WriteProcessMemory(...,"LoadLibrary(...,"mydll.dll",..).
CreateRemoteThread(...)
nn_zdm (555031742) 1999-05-18 14:14:19
上面写漏了,应是
WriteProcessMemory(...,"LoadLibrary(...,"mydll.dll",..). ");
CreateRemoteThread(...) ;
主题 关于屏幕取词的讨论(三)
作者 亦东
让大家久等,很抱歉,前些时候工作忙硬盘又坏了,太不幸了。
这回来点真格的。
咱们以截取TextOut为例。
下面是代码:
//截取TextOut
typedef UINT (WINAPI* ALLOCCSTODSALIAS)(UINT);
ALLOCCSTODSALIAS AllocCsToDsAlias;
BYTE NewValue[5];//保存新的入口代码
BYTE OldValue[5];//API原来的入口代码
unsigned char * Address=NULL;//可写的API入口地址
UINT DsSelector=NULL;//指向API入口的可写的选择符
WORD OffSetEntry=NULL;//API的偏移量
BOOL bHookAlready = FALSE; //是否挂钩子的标志
BOOL InitHook()
{
HMODULE hKernel,hGdi;
hKernel = GetModuleHandle("Kernel");
if(hKernel==NULL)
return FALSE;
AllocCsToDsAlias = (ALLOCCSTODSALIAS)GetProcAddress(hKernel,"AllocCsToDsAlias");//这是未公开的API所以要这样取地址
if(AllocCsToDsAlias==NULL)
return FALSE;
hGdi = GetModuleHandle("Gdi");
if(hmGdi==NULL)
return FALSE;
FARPROC Entry = GetProcAddress(hGdi,"TextOut");
if(Entry==NULL)
return FALSE;
OffSetEntry = (WORD)(FP_OFF(Entry));//取得API代码段的选择符
DsSelector = AllocCsToDsAlias(FP_SEG(Entry));//分配一个等同的可写的选择符
Address = (unsigned char*)MK_FP(DsSelector,OffSetEntry);//合成地址
NewValue[0]=0xEA;
*((DWORD*)(NewValue+1)) = (DWORD)MyTextOut;
OldValue[0]=Address[0];
*((DWORD*)(OldValue+1)) = *((DWORD*)(Address+1));
}
BOOL ClearHook()
{
if(bHookAlready)
HookOff();
FreeSelector(DsSelector);
}
BOOL HookOn()
{
if(!bHookAlready){
for(int i=0;i<5;i++){
Address[i]=NewValue[i];
}
bHookAlready=TRUE;
}
}
BOOL HookOff()
{
if(bHookAlready){
for(int i=0;i<5;i++){
Address[i]=OldValue[i];
}
bHookAlready=FALSE;
}
}
//钩子函数,一定要和API有相同的参数和声明
BOOL WINAPI MyTextOut(HDC hdc,int nXStart,int nYStart,LPCSTR lpszString,UINT cbString)
{
BOOL ret;
HookOff();
ret = TextOut(hdc,nXStart,nYStart,lpszString,cbString);//调原来的TextOut
HookOn();
return ret;
}
上面的代码是一个最简单的挂API钩子的例子,我要提醒大家的是,这段代码是我凭记忆写的,我以前的代码丢了,我没有编译测试过
因为我没有VC++1.52.所以代码可能会有错。
建议使用Borland c++,按16位编译。
如果用VC++1.52,则要改个选项
在VC++1.52的Option里,有个内存模式的设置,选大模式,和"DS!=SS DS Load on Function entry.",切记,否则会系统崩溃。
有什么不明白的可以给我写信
yeedong@163.net
Guest 1999-05-21 22:20:47
你这是16为的地址存取模式吧
你看这个MK_FP,win32不用了,
而且GetProcAddress(hKernel,"AllocCsToDsAlias")
这个API有用吗?
sorry
Guest 1999-05-21 22:31:47
亦东,想请教一个问题,
win32下,每个process有
自己的地址空间,process
A得到process B 的一个窗口C的handle,这个
handle的值 等于process B自己得到的window C 的handle值 吗?
我想应该不相等,但系统是如何转换的呢?(比如process A 向
window C 发消息,系统如何
知道process A 里的handle 和process B 里的
handle 都是指的window C)
. 是不是用duplicatehandle()?
(声明,我是真的不知道)
亦东 1999-05-21 22:54:48
这段代码就是十六位的。
你用Win32根本就不能编译。
32位没有AllocCsToDsAlias,因为在32位里不能写系统代码段(其实有办法,不是这样,不过比较麻烦)。
系统代码都在0x80000000以上,都是只读的。
所以要截WinAPI只能用16位的代码。
每个Process有自己的地址空间没错,但Window的句柄是共享的,同一个窗口在任何进程里的句柄都是一样的。
你可以在自己的进程里向任何窗口发消息。
Window的句柄很多,有的是共享的有的不是,
我也不知道那里有说明,一般是凭经验或试试看。
GUEST 1999-05-22 20:51:51
如果 window handle 换成
moudle handle呢?我
用moudlefirst,moudlenext遍历
得到的某个moudle 的句柄,
在任何一个process 中得到的
这个moudle handle 都是一样的值吗。(这个handle 和进程地址空间无关吗?)。
thx
亦东 1999-05-23 22:31:59
绝对有关
在Win32里module handle就是模块的起始地址。
说起来比较复杂
在95和NT里有些不同
在Win32里,每个模块(DLL,EXE)有一个ImageBase,这个数存放在DLL和EXE的文件头里。每个模块通常是不一样的。当Windows加载这个模块时优先考虑把模块放到Imagebase指定的地址,但有时会出现两个模块的地址重叠会有冲突,Windows会把模块移到与Imagebase最近的地址。所以Imagebase相同的模块在不同进程可能会在不同的地址上。这个地址就是module handle.Imagebase是可以在编译时指定的。
你用moudlefirst,moudlenext遍历得到的module handle是和进程有关的。
比如:你编了一个Imagebase为0x10000000的DLL A,进程A调用这个DLL A,在进程A里这个DLL被加载到地址0x10000000处,他的module handle为0x10000000,进程B也调用这个DLL A,但是进程B还调用另外一个DLL B,这另外的DLL B也是Imagebase为0x10000000的而且先加载,这是进程B的这个DLL A可能就被加载到0x13000000了,DLL A在进程B里的module handle 就是0x13000000了。
在95下,模块是共享的,也就是Windows只加载一份模块到内存,所有用到这个模块的进程都映射同一个模块,也就是说在95里每个模块在物理内存里只有一份。NT则不同,他为每个进程都加载一份模块。所以NT比95需要的内存多。所以在NT里不但在不同进程里的Module handle可能不同,连物理地址都是不同的。
yeedong@163.net
#13
我发现Joey的资料比我的全多了,就不来献丑了
#14
我来学习,这么多美味,够我吃几顿的!
#15
ab(ab): 谢谢你的提示,能够具体一点告诉我取词程序针对IE需要做什么特殊的处理吗?
大家注意:
我要解决的不是怎样鼠标取词!!!!!!
而是怎么样在IE下面取词!!!!!!!!!!!!!!!!
我手上的一分源码能够很好的在IE以外的大部分应用程序里面取词,但就是取不了IE下面的词,
大家注意:
我要解决的不是怎样鼠标取词!!!!!!
而是怎么样在IE下面取词!!!!!!!!!!!!!!!!
我手上的一分源码能够很好的在IE以外的大部分应用程序里面取词,但就是取不了IE下面的词,
#16
bensilver(Silver) 问的问题我只有听的份!
#17
IE真怪,View里面除了select一个窗口都没有,相当初用CIBAIII,根本不能用MSDN,滚动条一动,就不能取词了,CIBA2000感觉就好多了,事后想了想,看看了这方面的书和文档,感觉IE是在使用自己的一套GDI系统,所有的东西都是自己画的,我softice一直都懒得学,汇编只能用来跟踪,Win32下的汇编是真的不懂,在这里只有听听课了。
#18
怎么贴的都是老掉牙的资料!!!
#19
prodigy兄有没有新一些的资料?
#20
IE 是比较特殊,记事本输出文本只需要一句:
TextOut(dc, "text here", 10, 10);
而IE需要两句:
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
所以要先截取TextOut,保存起来,再截取BitBlt,判断两个dc2是否相同,还有坐标...
TextOut(dc, "text here", 10, 10);
而IE需要两句:
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
所以要先截取TextOut,保存起来,再截取BitBlt,判断两个dc2是否相同,还有坐标...
#21
关注
#22
ab(ab):
非常感谢!!
有一点不明白:
而IE需要两句:
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
截取TextOut不就已经可以得到“text here”了吗?为什么还要截取BitBlt ?
非常感谢!!
有一点不明白:
而IE需要两句:
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
截取TextOut不就已经可以得到“text here”了吗?为什么还要截取BitBlt ?
#23
是呀,ab能否把你的作品寄来用用,看看效果如何再说!!!
#24
???
#25
to bensilver:
虽然从第一句 TextOut() 就可以得到字符串,但并不能确定这个字符串是否自己想要的,有可能是写在别的窗口里面的,也有可能是写在同一窗口其它位置的,只有到 BitBlt() 时再确定是否同一窗口和是否同一位置。是否同一窗口可以用 TextOut 的目标 dc 和 BitBlt 的源 dc 是否相同确定。是否同一位置可以用 TextOut 的坐标和 BitBlt 的源坐标是否相同确定。最后,即使这两个条件都满足,当然还要确定一下:鼠标恰好处在 BitBlt 的目标坐标上。这三个坐标之间好像还需要转换,而且各个版本的 IE 之间还有小小的区别。
最后,虽然这样能取到大多数的词,但有的版本的IE取不到,具体原因“好像”就是各个版本IE的ViewportOrg不同,肯定有解决办法的,我没有仔细琢磨,要是能解决,还请告诉我怎么回事。还有AcrobatReader,要是不通过plug-in(需要100美元的授权费:),不知道能否取到词。
虽然从第一句 TextOut() 就可以得到字符串,但并不能确定这个字符串是否自己想要的,有可能是写在别的窗口里面的,也有可能是写在同一窗口其它位置的,只有到 BitBlt() 时再确定是否同一窗口和是否同一位置。是否同一窗口可以用 TextOut 的目标 dc 和 BitBlt 的源 dc 是否相同确定。是否同一位置可以用 TextOut 的坐标和 BitBlt 的源坐标是否相同确定。最后,即使这两个条件都满足,当然还要确定一下:鼠标恰好处在 BitBlt 的目标坐标上。这三个坐标之间好像还需要转换,而且各个版本的 IE 之间还有小小的区别。
最后,虽然这样能取到大多数的词,但有的版本的IE取不到,具体原因“好像”就是各个版本IE的ViewportOrg不同,肯定有解决办法的,我没有仔细琢磨,要是能解决,还请告诉我怎么回事。还有AcrobatReader,要是不通过plug-in(需要100美元的授权费:),不知道能否取到词。
#26
忘记打勾了。
#27
我想要分。。。。。。。
#28
我想要分。。。。。。。
#29
学习!
#30
关注。。。。。。。
#31
听课。
怎么没有新鲜一点的文章?
怎么没有新鲜一点的文章?
#32
我是新加盟者,来看一看,学一学!
#33
IE取词的确很麻烦
因为它往内存DC里写东西,而不是屏幕DC。这样坐标就全乱套了。
我做的时候发现其实是能截取到的,只是判断位置的程序把它给忽略了。
因此你要改的是判断位置的程序。
而且写字的方法也变了,就是同在IE里的不同地方,比如SELECT或者INPUT都是用的不同的方法。
但是,如果你把GDI函数都读全了,对TEXTOUT的全部相关函数都掌握了,比如各种坐标变换,文本标尺定位……这个位置还是能确定的。
另外好象遮盖法不管用了,就是以前做个“针尖窗口”去挡词的方法,需要用GDI函数强迫重绘。
最最烦的是WIN9X下的了,因为截取的是16位的GDI,根本没有UNICODE到ANSI的转换函数,明明抓出来却是乱码……
IE每个版本还不一样,烦
不过有个捷径,用S-ICE跟踪已有的屏幕取词程序。 :)
因为它往内存DC里写东西,而不是屏幕DC。这样坐标就全乱套了。
我做的时候发现其实是能截取到的,只是判断位置的程序把它给忽略了。
因此你要改的是判断位置的程序。
而且写字的方法也变了,就是同在IE里的不同地方,比如SELECT或者INPUT都是用的不同的方法。
但是,如果你把GDI函数都读全了,对TEXTOUT的全部相关函数都掌握了,比如各种坐标变换,文本标尺定位……这个位置还是能确定的。
另外好象遮盖法不管用了,就是以前做个“针尖窗口”去挡词的方法,需要用GDI函数强迫重绘。
最最烦的是WIN9X下的了,因为截取的是16位的GDI,根本没有UNICODE到ANSI的转换函数,明明抓出来却是乱码……
IE每个版本还不一样,烦
不过有个捷径,用S-ICE跟踪已有的屏幕取词程序。 :)
#34
关注。
#35
关注
#36
好象有张光盘不是公布了源代码了吗?大家还讨论什么?
马飞涛
http://iflower.myrice.com
马飞涛
http://iflower.myrice.com
#37
900分在哪?
#38
如果哪位朋友帮我解决了,我会另外发两个300分的贴子答谢!
#39
对于IE4下的取词:
1。必须截取ExtTextOutW_16
具体位置的得到方法:
fpExtTextOutW_16 = GetProcAddress(GetModuleHandle("GDI.EXE"), "GetTextMetrics");
WORD offset=(WORD)((DWORD)fpExtTextOutW_16&0xffff);
offset+=0x20;
DWORD uAddr=((DWORD)fpExtTextOutW_16 & 0xffff0000)+offset;
fpExtTextOutW_16=(FARPROC)uAddr;
2。转换被截取函数的显示坐标时与一般情况稍有不同:不要做DPtoLP
3。注意取出的词是unicode
1。必须截取ExtTextOutW_16
具体位置的得到方法:
fpExtTextOutW_16 = GetProcAddress(GetModuleHandle("GDI.EXE"), "GetTextMetrics");
WORD offset=(WORD)((DWORD)fpExtTextOutW_16&0xffff);
offset+=0x20;
DWORD uAddr=((DWORD)fpExtTextOutW_16 & 0xffff0000)+offset;
fpExtTextOutW_16=(FARPROC)uAddr;
2。转换被截取函数的显示坐标时与一般情况稍有不同:不要做DPtoLP
3。注意取出的词是unicode
#40
补充:此法俺只在中文95 98 me 上试过可以。您要有别的平台
也可以试一下。
也可以试一下。
#41
fpExtTextOutW_16 = GetProcAddress(GetModuleHandle("GDI.EXE"), "GetTextMetrics");
WORD offset=(WORD)((DWORD)fpExtTextOutW_16&0xffff);
offset+=0x20;
DWORD uAddr=((DWORD)fpExtTextOutW_16 & 0xffff0000)+offset;
fpExtTextOutW_16=(FARPROC)uAddr;
#42
关注
#43
到http://iflower.363.net/pmzhz.htm看看吧
#44
IE需要两句:
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
请问IE这么做有什么原因?(嘻嘻,借别人的地盘用一下)
TextOut(dc2, "text here", x, y);
BitBlt(dc, a, b, dc2, ...)
请问IE这么做有什么原因?(嘻嘻,借别人的地盘用一下)
#45
哈哈,好热闹呀!
我等结果吧!
我等结果吧!
#46
我喜欢这个。报名了。
#47
不知900分拿来有什么用(好像挺多的),可不可以换一点内功心法什么的?
#48
上面的个位大侠:
能不能把全部工程文打个包,送我一份?pageupdown1@263.net非常感谢!
能不能把全部工程文打个包,送我一份?pageupdown1@263.net非常感谢!
#49
push
#50
我记得这个贴子老早就有人讨论过。