如前所述,
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
内核等技术综合应用在一起,配合单词查找算法和词组分析算法就可以实现鼠标随动翻译功能。