现在的位置: 首页 > 综合 > 正文

怎么拦截系统函数

2013年09月17日 ⁄ 综合 ⁄ 共 34817字 ⁄ 字号 评论关闭

 
主  题:  怎么拦截系统函数?(超高度难题!)

 

  在Dos下,我们可以拦截中断,用自己的处理替换系统中断,然后调用原来的中断,在Windows中,没有了中断,只有函数,那么我们如何来拦截系统的函数,然后我们的函数又调用原来的系统函数呢?比如说系统有一个GetSystemDirectory()函数,我想用我自己的函数替换它,经过我的函数MyGetSystemDirectory()进行处理,然后由我的函数调用原来的GetSystemDirectory()进行处理,该如何做到!如果能够解决,我可以再开贴子送分!目前只能给150分。
 
 
 
 回复人: wzs(兔子) ( ) 信誉:99  2000-11-11 12:09:00Z  得分:0
 
 
 
vxd编程!
 
 
Top
 
 回复人: tchaikov() ( ) 信誉:100  2000-11-11 12:19:00Z  得分:0
 
 
 
化点时间去看看
http://www.sysinternals.com/ntinternals.htm
http://www.sysinternals.com/filemon.htm
或许对你有好处。
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-11-11 13:40:00Z  得分:0
 
 
 
  呵呵,用VXD?太烦了,我知道可以用Delphi的内嵌汇编可以做到,这才是我要的效果!我看见有的人已经可以做到这一点,编译后的DCU文件才3KB,谁能够给出例程?快来拿分啊!
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-11-11 21:02:00Z  得分:0
 
 
 
  呵呵,请各位高手该出手时就出手!呵呵,千万别叫我用VXD。给出例程,马上给分!金山词霸取词就是拦截了TextOut,TextOutA吧?原理应该是一样的。Help Me!
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-11-12 21:26:00Z  得分:0
 
 
 
  快回答啊,各位高手!
 
 
Top
 
 回复人: mutant(异类) ( ) 信誉:100  2000-11-13 10:03:00Z  得分:0
 
 
 
楼上的,说错了.你是拦截消息,用钩子当然可以了.人家是替换函数,钩子能行吗?
拜托你看清楚问题再回答.
 
 
Top
 
 回复人: gameboy999(-'_'-) ( ) 信誉:100  2000-11-13 13:26:00Z  得分:10
 
 
 
钩子不行,也不是vxd,对了kingron,你可否买了csdn程序员大本营2000的微软版??
在vc版块里有你所需要的文章!!!!我昨天刚买的.我在网吧,没法贴那篇帖子,但我可以肯定那必然是你所需要的,(既动态重定向系统内部函数).
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-11-13 13:58:00Z  得分:0
 
 
 
 2:tchaikov
 我的机器不能够上国外的网站,用代理也不太行。多谢你的指点,而且我的E文太好,呵呵。
 2: GameBoy999
 对了,就是这个!呵呵,给VC的也行!我会一点儿VC,可以把它翻译过来!我没有买CSDN(在这个偏远的小城也很难买到,不知道有没有卖),麻烦你把它贴出来!
  呵呵,快点儿,就可以拿分了!那位先贴出来,先给分!
 
 
Top
 
 回复人: gameboy999(-'_'-) ( ) 信誉:100  2000-11-13 14:25:00Z  得分:0
 
 
 
to kingron:我也很想,不过我们这里的网吧不让上载,我很头疼这事情,我的原码也不能发上来,哎,您还是拜托一下人家吧:)
 
 
Top
 
 回复人: vcx(vcx) ( ) 信誉:100  2000-11-13 16:48:00Z  得分:20
 
 
 
用Detours或APIHOOKS 2.0。我也在研究这个问题,不知怎么老没有成功, Detours的原理是把DLL注入另一进程空间,APIHOOKS2.0我只成功了本进程的HOOK,不知哪为高手能给个成功的例子
谢谢!
 
 
Top
 
 回复人: 6bytes(吴下阿蒙) ( ) 信誉:100  2000-11-14 01:28:00Z  得分:0
 
 
 
If you mean redirect an API function so that your function gets called
instead, then try this:

Get the Entry point of the exported function in the dll.
Replace it in memory with the address of your own function.
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-11-14 07:53:00Z  得分:0
 
 
 
2 6bytes:
  how to do it?请给出例程。Delphi,VC的都行!
 
 
Top
 
 回复人: uestclx(乐乐) ( ) 信誉:100  2000-11-14 12:21:00Z  得分:0
 
 
 
trap 技术
我好像有原码
 
 
Top
 
 回复人: gameboy999(-'_'-) ( ) 信誉:100  2000-11-14 12:58:00Z  得分:0
 
 
 
to kringron:叫人家帮帮忙,把程序员大本营微软篇的vc下的activex下的那篇叫什么"陷阱技术探密"的文章贴出来.
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-11-16 12:10:00Z  得分:0
 
 
 
那位兄弟帮个忙,贴一下啊!陷阱技术探密的文章!
 
 
Top
 
 回复人: wr960204(武稀松) ( ) 信誉:94  2000-11-16 15:36:00Z  得分:0
 
 
 
与金山词霸原理相同,射击到DLL重入,好象要先写一个DLL,将系统DLL函数在内存中的的地址指向你的函数.有很多Windows内核的书都讲到不过几乎都是C++的代码
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-11-16 15:45:00Z  得分:0
 
 
 
2 wr960204:
  也行!只要达到目的即可,VC的没关系,我自己可以翻译,麻烦你贴出有关的文章好吗?
  呵呵,我想这个问题基本上每个人都看了吧?赶快出手啊!150哦!
 
 
Top
 
 回复人: ab(ab) ( ) 信誉:100  2000-11-16 15:51:00Z  得分:30
 
 
 
大家别争了,微软已经提供了这种开发包,就是上面 VCX 说的 Detours,到
http://www.research.microsoft.com/sn/detours
去下载。不过随带的文档太少了。
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-11-25 14:56:00Z  得分:0
 
 
 
哪儿有Detours的库文件下载?(.dll or .lib or .c or .h or .ocx......)
 
 
Top
 
 回复人: CoolSlob(我拿青春赌明天) ( ) 信誉:102  2000-11-26 19:26:00Z  得分:0
 
 
 
关注,Kingron!!!有了答案,能不能给我一份??????先谢了
 
 
Top
 
 回复人: temps(青石) ( ) 信誉:99  2000-11-28 12:45:00Z  得分:0
 
 
 
关注!!! 有答案 temps@263.net也希望可以得到一分
 
 
Top
 
 回复人: tibetty(laoduan) ( ) 信誉:100  2000-11-28 13:01:00Z  得分:0
 
 
 
在水木清华的Programming版探讨了不下十次, Matt Pietrek的<<Windows 95 System Programming Secret>>以及Jeffery Richter的<<Advanced Windows Programming>>
里面都有专门的探讨.
 
 
Top
 
 回复人: zheng(一笑) ( ) 信誉:100  2000-11-28 13:06:00Z  得分:0
 
 
 
guanzhu
 
 
Top
 
 回复人: Jujus(某人) ( ) 信誉:99  2000-11-28 13:59:00Z  得分:50
 
 
 
装载一篇文章,也许有用,但我在MSDN上没有查到IMAGE_IMPORT_DESCRIPTOR的结构,Kingron:你把它转成Delphi代码后记得给我发一份哦

如何截获API函数(转)
该程序是基于HOOK原理,主要是将自己的函数放到目标PROCESS的地址空间,这里是使用HOOK实现.首先建立一个MOUSE的HOOK程序,然后在全局鼠标HOOK的DLL中做截获动作,可以在PROCESS_ATTACH时做,也可以在鼠标的HOOK链函数中做.
建立全局HOOK就不说了,可以在网上很多地方看到.主要是截获动作.通过PE格式(使用IMAGE)改变API函数在调用时的地址.DLL部分参考如下代码:
static int WINAPI MyMessageBoxW(HWND hWnd , LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)//自己的MessageBoxW函数
{return MessageBox(hWnd, "TNT"/*lpText*/, "TNT"/*lpCaption*/, uType);
}
我定义了一个结构
typedef struct tag_HOOKAPI
{
LPCSTR szFunc;//待HOOK的API函数名
PROC pNewProc;//新的函数指针
PROC pOldProc;//老的函数指针
}HOOKAPI, *LPHOOKAPI;
extern "C" __declspec(dllexport)PIMAGE_IMPORT_DESCRIPTOR GetNamedImportDescriptor(HMODULE hModule, LPCSTR szImportMod)
{//首先是DOS头
PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER) hModule;
if(pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) return NULL;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDOSHeader + (DWORD)(pDOSHeader->e_lfanew));
if(pNTHeader->Signature != IMAGE_NT_SIGNATURE) return NULL;
//如果没有Import部分,返回失败
if(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress == 0)
return NULL;
//取Import部分
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
((DWORD)pDOSHeader + (DWORD)(pNTHeader->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
//寻找与szImportMod相配部分
while (pImportDesc->Name)
{
PSTR szCurrMod = (PSTR)((DWORD)pDOSHeader + (DWORD)(pImportDesc->Name));
if (stricmp(szCurrMod, szImportMod) == 0)
break; //找到
pImportDesc++;
}
if(pImportDesc->Name == NULL) return NULL;
return pImportDesc;
}
extern "C" __declspec(dllexport) HookAPIByName(HMODULE hModule/*被HOOK的目标进程MODULE*/, LPCSTR szImportMod/*如GDI32.DLL*/,LPHOOKAPI pHookApi/*指定函数名,如"MessageBoxW"*/)
{
PIMAGE_IMPORT_DESCRIPTOR pImportDesc =
GetNamedImportDescriptor(hModule, szImportMod);
if (pImportDesc == NULL)
return FALSE; //需要改换的API不能取到正确描PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule + (DWORD)(pImportDesc->OriginalFirstThunk));
PIMAGE_THUNK_DATA pRealThunk =
(PIMAGE_THUNK_DATA)((DWORD)hModule + (DWORD)(pImportDesc->FirstThunk));
while(pOrigThunk->u1.Function)
{
if((pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG)
{
PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)hModule + (DWORD)(pOrigThunk->u1.AddressOfData));
if(pByName->Name[0] == '/0')
return FALSE; //失败
if(strcmpi(pHookApi->szFunc, (char*)pByName->Name) == 0)
{
//改变thunk保护属性
MEMORY_BASIC_INFORMATION mbi_thunk;
VirtualQuery(pRealThunk, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION));
VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect);
//保存原来的API函数指针
if(pHookApi->pOldProc == NULL)
pHookApi->pOldProc = (PROC)pRealThunk->u1.Function;
//改变API函数指针
pRealThunk->u1.Function = (PDWORD)pHookApi->pNewProc;
//将thunk保护属性改回来
DWORD dwOldProtect;
VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize,
mbi_thunk.Protect, &dwOldProtect);
}
}
pOrigThunk++;
pRealThunk++;
}
SetLastError(ERROR_SUCCESS);
return TRUE;
}

 
 
Top
 
 回复人: Jera(天~~~~塌下来我也不怕...) ( ) 信誉:100  2000-11-28 16:11:00Z  得分:0
 
 
 
太难了
 
 
Top
 
 回复人: tender_edge(温柔一刀) ( ) 信誉:100  2000-12-01 03:27:00Z  得分:0
 
 
 
>>我看见有的人已经可以做到这一点,编译后的DCU文件才3KB,谁能够给出例程?快来拿分啊!

在哪里?给我来一份这个DCU如何?
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-12-01 14:17:00Z  得分:0
 
 
 
2 TENDER_EDGE:
  已经G给你了。
2 all:
  我想我的问题已经快解决了,不过欢迎大家继续讨论,我有了答案之后会贴出来,然后给分。但是我的时间比较紧张,可能要过一段时间。谢谢各位!
 
 
Top
 
 回复人: bbsboyy() ( ) 信誉:100  2000-12-01 17:42:00Z  得分:0
 
 
 
密切关注!
KINGRON:研究出来DELPHI 版一定别忘了给哥们来一份.
liujw@163.net
 
 
Top
 
 回复人: alwssoan(巧克力) ( ) 信誉:100  2000-12-01 21:44:00Z  得分:0
 
 
 
谁说windows下没有中断,用内欠汇编语句。获取中断描述表即可,
实在不行发email给我吧,okinetboy@sina.com.cn
 
 
Top
 
 回复人: wr960204(武稀松) ( ) 信誉:94  2000-12-11 14:25:00Z  得分:0
 
 
 
程序员大本营CD上VC中有例子,好几个呢。我比较喜欢超级解霸II源码中的函数截获例子。还有几篇技术文章你可以参考一下。反正我对VC++不太熟悉,翻译不了。有些例子有现成的。DLL文件可用。
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-12-12 10:24:00Z  得分:0
 
 
 
2 wr960204:
超级解霸II源码中的函数截获例子?麻烦你贴出来好吗?我这儿没有程序员大本营的CD!
有现成的。DLL文件可用?哪儿有下载?哪儿有说明?
谢谢!
 
 
Top
 
 回复人: catthunder(男儿何不带吴钩) ( ) 信誉:100  2000-12-12 21:22:00Z  得分:0
 
 
 
纠正一下,WinApi也是系统调用,也用中断,怎么说windows下无中断?
 
 
Top
 
 回复人: GoodHope(好望角) ( ) 信誉:100  2000-12-12 21:50:00Z  得分:50
 
 
 
Windows中有中断,但是跟Dos下的一般说的中断完全不同了。Windows中已经不怎么用中断来实现通用功能了,提及中断实际上是说的硬件的中断行为,一般是由硬件触发。而Dos下往往代表的通过中断实现特定的功能,一般是软中断。catthunder说的WinApi是系统调用是对的,但WinApi绝对不是中断。保护模式下,中断是用过中断门进入的,不是普通的函数调用。
 
 
Top
 
 回复人: WorldCreater(造化天尊) ( ) 信誉:99  2000-12-12 23:09:00Z  得分:0
 
 
 
研究结果出来后给我一份好吗
primary@email.com.cn
 
 
Top
 
 回复人: GoodHope(好望角) ( ) 信誉:100  2000-12-13 10:58:00Z  得分:50
 
 
 
to Jujus:
  你给出代码中缺一个回车。
return FALSE; //需要改换的API不能取到正确描PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule + (DWORD)(pImportDesc->OriginalFirstThunk));
应该是
return FALSE; //需要改换的API不能取到正确描
PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule + (DWORD)(pImportDesc->OriginalFirstThunk));

另外IMAGE_IMPORT_DESCRIPTOR在WinNT.h中定义,声明如下:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    };
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date/time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)

    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

我正在研究Delphi该怎么写。
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-12-13 15:34:00Z  得分:0
 
 
 
2 goodhope:
  多谢上次的帮忙,这次恐怕又要麻烦你了。写出来的Delphi版给我一份好吗?谢谢了。
 
 
Top
 
 回复人: jimusi() ( ) 信誉:100  2000-12-13 15:56:00Z  得分:0
 
 
 
欢迎到“软件开发者”——“Visual C++”——“严肃讨论...”参加讨论
 
 
Top
 
 回复人: GoodHope(好望角) ( ) 信誉:100  2000-12-13 16:21:00Z  得分:0
 
 
 
to: Kingron
  上次?我很少看名字的:)
 
 
Top
 
 回复人: wxy_2000(风月无痕) ( ) 信誉:100  2000-12-13 16:56:00Z  得分:0
 
 
 
to: goodhope
我敬佩你,请继续努力,研究出来了别忘了给老兄一份。
wxy_2000@263.net
 
 
Top
 
 回复人: tchaikov() ( ) 信誉:100  2000-12-13 17:01:00Z  得分:0
 
 
 
lu0.126.com 上好象有这方面的专题,去看看吧。
 
 
Top
 
 回复人: GoodHope(好望角) ( ) 信誉:100  2000-12-13 17:20:00Z  得分:0
 
 
 
哦,Ring0啊。:)
 
 
Top
 
 回复人: gypb(网螺) ( ) 信誉:99  2000-12-13 17:45:00Z  得分:0
 
 
 
给我发一个
1stchina@elong.com
 
 
Top
 
 回复人: GoodHope(好望角) ( ) 信誉:100  2000-12-15 13:44:00Z  得分:50
 
 
 
// 我只是照着翻译了一下,可以编译通过。代码本身不全,所以没有进行任何的调试。
// 如果在格式对齐上有问题,请把Tab设为2。

unit x;

// 如何截获API函数(转)
// 该程序是基于HOOK原理,主要是将自己的函数放到目标PROCESS的地址空间,这里是使用HOOK实现.
// 首先建立一个MOUSE的HOOK程序,然后在全局鼠标HOOK的DLL中做截获动作,
// 可以在PROCESS_ATTACH时做,也可以在鼠标的HOOK链函数中做.
// 建立全局HOOK就不说了,可以在网上很多地方看到.主要是截获动作.
// 通过PE格式(使用IMAGE)改变API函数在调用时的地址.DLL部分参考如下代码:

interface

const
 IMAGE_ORDINAL_FLAG64 = $8000000000000000;
 IMAGE_ORDINAL_FLAG32 = $80000000;
{$IFDEF _WIN64}
 IMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG64;
{$ELSE}
 IMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG32;
{$ENDIF}

type
 // 我定义了一个结构
 tag_HOOKAPI = record
  szFunc: LPCSTR;//待HOOK的API函数名
  pNewProc: FARPROC;//新的函数指针
  pOldProc: FARPROC;//老的函数指针
 end;
 HOOKAPI = tag_HOOKAPI;
 LPHOOKAPI = ^HOOKAPI;
 THookAPI = tag_HOOKAPI;

 LONG = Longint;
 ULONGLONG = Int64;
 PSTR = LPSTR;
 PVOID = Pointer;

 // WinNT.h Begin;
{$ALIGN OFF}

{$IFDEF _WIN64}
 ULONG_PTR = ULONGLONG;
 PULONG_PTR = ^ULONG_PTR;
{$ELSE}
 ULONG_PTR = LONG;
 PULONG_PTR = ^ULONG_PTR;
{$ENDIF}
 
 SIZE_T = ULONG_PTR;
 PSIZE_T = ^SIZE_T;

 tag_IMAGE_IMPORT_DESCRIPTOR = record
  case Boolean of
  False: (Characteristics: DWORD);    // 0 for terminating null import descriptor
  True:  (OriginalFirstThunk: DWORD; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
      TimeDateStamp: DWORD;       // 0 if not bound,
                    // -1 if bound, and real date/time stamp
                    //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                    // O.W. date/time stamp of DLL bound to (Old BIND)

      ForwarderChain: DWORD;      // -1 if no forwarders
      Name: DWORD;
      FirstThunk: DWORD;          // RVA to IAT (if bound this IAT has actual addresses)
  );
 end;
 IMAGE_IMPORT_DESCRIPTOR = tag_IMAGE_IMPORT_DESCRIPTOR;
 PIMAGE_IMPORT_DESCRIPTOR = ^IMAGE_IMPORT_DESCRIPTOR;

 tag_IMAGE_DOS_HEADER = record  // DOS .EXE header
  e_magic:  WORD;             // Magic number
  e_cblp:   WORD;             // Bytes on last page of file
  e_cp:    WORD;             // Pages in file
  e_crlc:   WORD;             // Relocations
  e_cparhdr: WORD;             // Size of header in paragraphs
  e_minalloc: WORD;             // Minimum extra paragraphs needed
  e_maxalloc: WORD;             // Maximum extra paragraphs needed
  e_ss:    WORD;             // Initial (relative) SS value
  e_sp:    WORD;             // Initial SP value
  e_csum:   WORD;             // Checksum
  e_ip:    WORD;             // Initial IP value
  e_cs:    WORD;             // Initial (relative) CS value
  e_lfarlc:  WORD;             // File address of relocation table
  e_ovno:   WORD;             // Overlay number
  e_res:array[0..4-1]of
        WORD;             // Reserved words
  e_oemid:  WORD;             // OEM identifier (for e_oeminfo)
  e_oeminfo: WORD;             // OEM information; e_oemid specific
  e_res2:array[0..10-1]of
        WORD;             // Reserved words
  e_lfanew:  LONG;             // File address of new exe header
 end;
 IMAGE_DOS_HEADER = tag_IMAGE_DOS_HEADER;
 PIMAGE_DOS_HEADER = ^IMAGE_DOS_HEADER;

 tag_IMAGE_FILE_HEADER  = record
  Machine:       WORD;
  NumberOfSections:   WORD;
  TimeDateStamp:    DWORD;
  PointerToSymbolTable: DWORD;
  NumberOfSymbols:   DWORD;
  SizeOfOptionalHeader: WORD;
  Characteristics:   WORD;
 end;
 IMAGE_FILE_HEADER = tag_IMAGE_FILE_HEADER;
 PIMAGE_FILE_HEADER = ^IMAGE_FILE_HEADER;

 tag_IMAGE_DATA_DIRECTORY = record
  VirtualAddress: DWORD;
  Size:      DWORD;
 end;
 IMAGE_DATA_DIRECTORY = tag_IMAGE_DATA_DIRECTORY;
 PIMAGE_DATA_DIRECTORY = ^IMAGE_DATA_DIRECTORY;

 tag_IMAGE_OPTIONAL_HEADER64 = record
  Magic: WORD;
  MajorLinkerVersion: BYTE;
  MinorLinkerVersion: BYTE;
  SizeOfCode: DWORD;
  SizeOfInitializedData: DWORD;
  SizeOfUninitializedData: DWORD;
  AddressOfEntryPoint: DWORD;
  BaseOfCode: DWORD;
  ImageBase: ULONGLONG;
  SectionAlignment: DWORD;
  FileAlignment: DWORD;
  MajorOperatingSystemVersion: WORD;
  MinorOperatingSystemVersion: WORD;
  MajorImageVersion: WORD;
  MinorImageVersion: WORD;
  MajorSubsystemVersion: WORD;
  MinorSubsystemVersion: WORD;
  Win32VersionValue: DWORD;
  SizeOfImage: DWORD;
  SizeOfHeaders: DWORD;
  CheckSum: DWORD;
  Subsystem: WORD;
  DllCharacteristics: WORD;
  SizeOfStackReserve: ULONGLONG;
  SizeOfStackCommit: ULONGLONG;
  SizeOfHeapReserve:  ULONGLONG;
  SizeOfHeapCommit: ULONGLONG;
  LoaderFlags: DWORD;
  NumberOfRvaAndSizes: DWORD;
  DataDirectory:
   array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1]of IMAGE_DATA_DIRECTORY;
 end;
 IMAGE_OPTIONAL_HEADER64 = tag_IMAGE_OPTIONAL_HEADER64;
 PIMAGE_OPTIONAL_HEADER64 = ^IMAGE_OPTIONAL_HEADER64;

 tag_IMAGE_OPTIONAL_HEADER = record
  //
  // Standard fields.
  //

  Magic:          WORD;
  MajorLinkerVersion:    BYTE;
  MinorLinkerVersion:    BYTE;
  SizeOfCode:        DWORD;
  SizeOfInitializedData:  DWORD;
  SizeOfUninitializedData: DWORD;
  AddressOfEntryPoint:   DWORD;
  BaseOfCode:        DWORD;
  BaseOfData:        DWORD;

  //
  // NT additional fields.
  //

  ImageBase:          DWORD;
  SectionAlignment:       DWORD;
  FileAlignment:        DWORD;
  MajorOperatingSystemVersion: WORD;
  MinorOperatingSystemVersion: WORD;
  MajorImageVersion:      WORD;
  MinorImageVersion:      WORD;
  MajorSubsystemVersion:    WORD;
  MinorSubsystemVersion:    WORD;
  Win32VersionValue:      DWORD;
  SizeOfImage:         DWORD;
  SizeOfHeaders:        DWORD;
  CheckSum:           DWORD;
  Subsystem:          WORD;
  DllCharacteristics:      WORD;
  SizeOfStackReserve:      DWORD;
  SizeOfStackCommit:      DWORD;
  SizeOfHeapReserve:      DWORD;
  SizeOfHeapCommit:       DWORD;
  LoaderFlags:         DWORD;
  NumberOfRvaAndSizes:     DWORD;
  DataDirectory:array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1]of
  IMAGE_DATA_DIRECTORY;
 end;
 IMAGE_OPTIONAL_HEADER32 = tag_IMAGE_OPTIONAL_HEADER;
 PIMAGE_OPTIONAL_HEADER32 = ^IMAGE_OPTIONAL_HEADER32;

 tag_IMAGE_NT_HEADERS64 = record
  Signature: DWORD;
  FileHeader: IMAGE_FILE_HEADER;
  OptionalHeader: IMAGE_OPTIONAL_HEADER64;
 end;
 IMAGE_NT_HEADERS64 = tag_IMAGE_NT_HEADERS64;
 PIMAGE_NT_HEADERS64 = IMAGE_NT_HEADERS64;

 tag_IMAGE_NT_HEADERS = record
  Signature: DWORD;
  FileHeader: IMAGE_FILE_HEADER;
  OptionalHeader: IMAGE_OPTIONAL_HEADER32;
 end;
 IMAGE_NT_HEADERS32 = tag_IMAGE_NT_HEADERS;
 PIMAGE_NT_HEADERS32 = ^IMAGE_NT_HEADERS32;

{$IFDEF _WIN64}
 IMAGE_NT_HEADERS = IMAGE_NT_HEADERS64;
 PIMAGE_NT_HEADERS = PIMAGE_NT_HEADERS64;
// IMAGE_FIRST_SECTION64(ntheader) IMAGE_FIRST_SECTION(ntheader)
{$ELSE}
 IMAGE_NT_HEADERS = IMAGE_NT_HEADERS32;
 PIMAGE_NT_HEADERS = PIMAGE_NT_HEADERS32;
//IMAGE_FIRST_SECTION32(ntheader) = IMAGE_FIRST_SECTION(ntheader);
{$ENDIF}

 tag_IMAGE_IMPORT_BY_NAME = record
  Hint: WORD;
  Name: array[0..0]of BYTE;
 end;
 IMAGE_IMPORT_BY_NAME = tag_IMAGE_IMPORT_BY_NAME;
 PIMAGE_IMPORT_BY_NAME = ^IMAGE_IMPORT_BY_NAME;

 tag_U1_64 = record
  case BYTE of
  1: (ForwarderString: PBYTE;);
  2: (Function_: PDWORD;);
  3: (Ordinal: ULONGLONG;);
  4: (AddressOfData: PIMAGE_IMPORT_BY_NAME;);
 end;
 
 tag_IMAGE_THUNK_DATA64 = record
  u1: tag_U1_64;
 end;
 IMAGE_THUNK_DATA64 = tag_IMAGE_THUNK_DATA64;
 PIMAGE_THUNK_DATA64 = ^IMAGE_THUNK_DATA64;

 tag_U1_32 = record
  case BYTE of
  1: (ForwarderString: PBYTE; );
  2: (Function_: PDWORD);
  3: (Ordinal: DWORD);
  4: (AddressOfData: PIMAGE_IMPORT_BY_NAME);
 end;

 tag_IMAGE_THUNK_DATA32 = record
  u1: tag_U1_32;
 end;

 IMAGE_THUNK_DATA32 = tag_IMAGE_THUNK_DATA32;

 PIMAGE_THUNK_DATA32 = ^IMAGE_THUNK_DATA32;

{$IFDEF _WIN64}
 // IMAGE_ORDINAL64(Ordinal) = IMAGE_ORDINAL(Ordinal)
 IMAGE_THUNK_DATA = IMAGE_THUNK_DATA64;
 PIMAGE_THUNK_DATA = PIMAGE_THUNK_DATA64;
 // IMAGE_SNAP_BY_ORDINAL64(Ordinal)IMAGE_SNAP_BY_ORDINAL(Ordinal)
 //IMAGE_TLS_DIRECTORY = IMAGE_TLS_DIRECTORY64;
 //PIMAGE_TLS_DIRECTORY = PIMAGE_TLS_DIRECTORY64
{$ELSE}
 // IMAGE_ORDINAL32(Ordinal) =IMAGE_ORDINAL(Ordinal)
 IMAGE_THUNK_DATA = IMAGE_THUNK_DATA32;
 PIMAGE_THUNK_DATA = PIMAGE_THUNK_DATA32;
 // IMAGE_SNAP_BY_ORDINAL32(Ordinal) = IMAGE_SNAP_BY_ORDINAL(Ordinal)
 //IMAGE_TLS_DIRECTORY = IMAGE_TLS_DIRECTORY32
 //PIMAGE_TLS_DIRECTORY = PIMAGE_TLS_DIRECTORY32
{$ENDIF}

{$ALIGN ON}

function GetNamedImportDescriptor(
 hModule: HMODULE;
 szImportMod: LPCSTR): PIMAGE_IMPORT_DESCRIPTOR; stdcall;
function HookAPIByName(
 hModule: HMODULE;    // 被HOOK的目标进程MODULE
 szImportMod: LPCSTR;  // 如GDI32.DLL
 pHookApi: LPHOOKAPI   // 指定函数名,如'MessageBoxW'
): Boolean; stdcall

implementation

//自己的MessageBoxW函数

function MyMessageBoxW(hWnd: HWND;
 lpText: LPCWSTR; lpCaption: LPCWSTR; uType: UINT): Integer; stdcall;
begin
 Result := MessageBox(hWnd, 'TNT'{lpText}, 'TNT'{lpCaption}, uType);
end;

function GetNamedImportDescriptor(
 hModule: HMODULE;
 szImportMod: LPCSTR): PIMAGE_IMPORT_DESCRIPTOR;
var
 pDOSHeader: PIMAGE_DOS_HEADER;
 pNTHeader: PIMAGE_NT_HEADERS;
 pImageDataDirectory: PIMAGE_DATA_DIRECTORY;
 pImportDesc: PIMAGE_IMPORT_DESCRIPTOR;
 szCurrMod: PSTR;
begin
 Result := nil;
 //首先是DOS头
 pDOSHeader := PIMAGE_DOS_HEADER(hModule);

 if pDOSHeader.e_magic <> IMAGE_DOS_SIGNATURE then Exit;

 pNTHeader :=
    PIMAGE_NT_HEADERS(DWORD(pDOSHeader) + DWORD(pDOSHeader.e_lfanew));

 if pNTHeader.Signature <> IMAGE_NT_SIGNATURE then Exit;

 //如果没有Import部分,返回失败
 pImageDataDirectory :=
  @pNTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];

 if (pImageDataDirectory.VirtualAddress = 0) then Exit;

 //取Import部分
 pImportDesc := PIMAGE_IMPORT_DESCRIPTOR
  (DWORD(pDOSHeader) + DWORD(pImageDataDirectory.VirtualAddress));

 //寻找与szImportMod相配部分
 while pImportDesc.Name <> 0 do
 begin
  szCurrMod := PSTR(DWORD(pDOSHeader) + DWORD(pImportDesc.Name));
  if (CompareText(szCurrMod, szImportMod) = 0) then
  Break; //找到
  Inc(pImportDesc);
 end;
 if pImportDesc.Name <> 0 then
  Result := pImportDesc;
end;

function HookAPIByName(
 hModule: HMODULE;
 szImportMod: LPCSTR;
 pHookApi: LPHOOKAPI
): Boolean;
var
 pImportDesc: PIMAGE_IMPORT_DESCRIPTOR;
 pOrigThunk: PIMAGE_THUNK_DATA;
 pRealThunk: PIMAGE_THUNK_DATA;
 pByName: PIMAGE_IMPORT_BY_NAME;
 mbi_thunk: TMemoryBasicInformation;
 dwOldProtect: DWORD;
begin
 Result := False;
 pImportDesc :=
  GetNamedImportDescriptor(hModule, szImportMod);

 //需要改换的API不能取到正确描
 pOrigThunk :=
  PIMAGE_THUNK_DATA(DWORD(hModule) + DWORD(pImportDesc.OriginalFirstThunk));
 if pImportDesc = nil then Exit;
 pRealThunk :=
  PIMAGE_THUNK_DATA(DWORD(hModule) + DWORD(pImportDesc.FirstThunk));

 while pOrigThunk.u1.Function_ <> nil do
 begin
  if(pOrigThunk.u1.Ordinal and IMAGE_ORDINAL_FLAG) <> IMAGE_ORDINAL_FLAG then
  begin
   pByName :=
    PIMAGE_IMPORT_BY_NAME(DWORD(hModule) + DWORD(pOrigThunk.u1.AddressOfData));
   if pByName.Name[0] = 0 then Exit; //失败
   if CompareText(pHookApi.szFunc, PChar(@pByName.Name[0])) = 0 then
   begin
    //改变thunk保护属性
    VirtualQuery (
     pRealThunk,
     mbi_thunk,
     SizeOf(MEMORY_BASIC_INFORMATION));
    VirtualProtect(
     mbi_thunk.BaseAddress,
     mbi_thunk.RegionSize,
     PAGE_READWRITE,
     @mbi_thunk.Protect);

    //保存原来的API函数指针
    if pHookApi.pOldProc = nil then
     pHookApi.pOldProc := FARPROC(pRealThunk.u1.Function_);

    //改变API函数指针
    pRealThunk.u1.Function_ := PDWORD(pHookApi.pNewProc);

    //将thunk保护属性改回来
    VirtualProtect(
     mbi_thunk.BaseAddress,
     mbi_thunk.RegionSize,
     mbi_thunk.Protect,
     @dwOldProtect);
   end;
  end;
  Inc(pOrigThunk);
  Inc(pRealThunk);
 end;
 SetLastError(ERROR_SUCCESS);
 Result := True;
end;

 
 
Top
 
 回复人: GoodHope(好望角) ( ) 信誉:100  2000-12-15 13:47:00Z  得分:0
 
 
 
怎么头部的TAB都被干掉了@_@。唉,那就慢慢看把。
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-12-15 14:23:00Z  得分:0
 
 
 
我看看,谢谢了............
一定给分的!
 
 
Top
 
 回复人: GoodHope(好望角) ( ) 信誉:100  2000-12-15 14:27:00Z  得分:0
 
 
 
调试一把,当场出错:(
if pByName.Name[0] = 0 then Exit; //失败
一句内存访问违背,应该是前面生成pByName有问题。

路漫漫兮其修远
 
 
Top
 
 回复人: Kingron(单身走我路……) ( ) 信誉:117  2000-12-17 10:42:00Z  得分:0
 
 
 
佩服!我还等一等!
 
 
Top
 
 回复人: sorry2000(方言) ( ) 信誉:96  2000-12-17 11:04:00Z  得分:40
 
 
 
我看到一篇文章:
此处引用一下
意思相当明了:
AllocCStoDSAlias//这个函数我以前没有见过;不只那位仁兄能指点一二:
"陷阱"技术探秘
----动态汉化Windows技术的分析
华光科技股份公司研究开发中心 张高峰
 
摘 要: 四通利方(RichWin),中文之星(CStar)是大家广为熟知的汉化Windows产品,
"陷阱”技术即动态修改Windows代码,一直是其对外宣称的过人技术,它究竟是如
何实现的,这自然是核心机密。本文试图解开这个秘密,并同时介绍Windows的模块
调用机制与重定位概念,并给出了采用"陷阱"技术动态修改Windows代码的示例源程
序。
关键词:汉化Windows重定位技术

一、发现了什么?
作者多年来一直从事Windows下的软件开发工作,经历
了Windows2.0、3.0、3.1,直至WindowsNT,95的成长过程,也遍历
了长青窗口、长城窗口、DBWin、CStar、RichWin等多个Windows汉化
产品。从现在看来,影响最大也最为成功的,当推四通利方
的RichWin,此外,中文之星CStar与RichWin师出一门,其核心技术
自然也差不许多。其对外宣传采用独特的“陷阱”技术动态
修改Windows代码,一直是作者感兴趣的地方。

EXEHDR是MicrosoftVisualC++开发工具中很有用的一个程
序,它可以检查NE(New_Executable)格式文件,用它来分析RichWin
的WSENGINE.DLL或CStar的CHINESE.DLL就会发现与众不同的两点:
( 以CStar 1.20 为 例)
C:/CSTAR>exehdr chinese.dll /v
..................................
6 type offset target
BASE060aseg 2 offset 0000
PTR 047eimp GDI.GETCHARABCWIDTHS
PTR 059bimp GDI.ENUMFONTFAMILIES
PTR 0451imp DISPLAY.14( EXTTEXTOUT )
PTR 0415imp KEYBOARD.4( TOASCII )
PTR 04baimp KEYBOARD.5( ANSITOOEM )
PTR 04c9imp KEYBOARD.6( OEMTOANSI )
PTR 04d8imp KEYBOARD.134( ANSITOOEMBUFF)
PTR 05f5imp USER.430( LSTRCMP )
PTR 04e7imp KEYBOARD.135( OEMTOANSIBUFF)
PTR 0514imp USER.431( ANSIUPPER)
PTR 0523imp USER.432( ANSILOWER )
PTR 05aaimp GDI.56( CREATEFONT)
PTR 056eimp USER.433( ISCHARALPHA )
PTR 05b9imp GDI.57( CREATEFONTINDIRECT )
PTR 057dimp USER.434( ISCHARALPHANUMERIC )
PTR 049cimp USER.179( GETSYSTEMMETRICS )
PTR 0550imp USER.435( ISCHARUPPER)
PTR 055fimp USER.436( ISCHARLOWER)
PTR 0532imp USER.437( ANSIUPPERBUFF)
PTR 0541imp USER.438( ANSILOWERBUFF)
PTR 05c8imp GDI.69( DELETEOBJECT )
PTR 058cimp GDI.70( ENUMFONTS )
PTR 04abimp KERNEL.ISDBCSLEADBYTE
PTR 05d7imp GDI.82( GETOBJECT)
PTR 048dimp KERNEL.74 ( OPENFILE )
PTR 0460imp GDI.91( GETTEXTEXTENT)
PTR 05e6imp GDI.92( GETTEXTFACE)
PTR 046fimp GDI.350 ( GETCHARWIDTH )
PTR 0442imp GDI.351 ( EXTTEXTOUT )
PTR 0604imp USER.471( LSTRCMPI )
PTR 04f6imp USER.472( ANSINEXT )
PTR 0505imp USER.473( ANSIPREV )
PTR 0424imp USER.108( GETMESSAGE )
PTR 0433imp USER.109( PEEKMESSAGE)
35 relocations

*******扩号内为作者加上的对应WindowsAPI函数
第一,在数据段中,发现了重定位信息。
第二,这些重定位信息提示的函数,全都与文字显示
输出和键盘,字符串有关。也就是说汉化Windows,必须修改这些函数。
在这非常特殊的地方,隐藏着什么呢?无庸致疑,这与众不同的两点,对打开“陷阱”技术之门而言,不是金钥匙,也是敲门砖。

二、Windows的模块调用机制与重定位概念
为了深入探究“陷阱”技术,我们先来介绍Windows的模块调用机制。Windows的运行分实模式(RealMode),标准模式(StandMode)和增强模式(386EnhancedMode)三种,虽然这几种模式各不相同,但其核心模块的调用关系却是完全一致的。
主要的三个模块,有如下的关系:

KERNEL是Windows系统内核,它不依赖其它模块。

GDI是Windows图形设备接口模块,它依赖于KERNEL模块。

USER是Windows用户接口服务模块,它依赖于KERNEL,GDI模块及设备驱动程序等所有模块。
这三个模块,实际上就是Windows的三个动态连接库,在系统的存在形式如下,KERNEL有三种不同形式,Kernel.exe(实模式),Krnl286.exe(标准模式),Krnl386.
exe(386增强模式);GDI模块是Gdi.exe;USER模块是User.exe,虽然文件名都以EXE为扩展名,但它们实际都是动态连接库。

同时,几乎所有的API函数都隐藏在这三个模块中。用EXEHDR对这三个模块分析,就可列出一大堆你所熟悉的WindowsAPI函数。

以GDI模块为例,

C:/WINDOWS/SYSTEM>exehdr gdi.exe
Exports:
ord seg offset name
............
351 1923eEXTTEXTOUT exported, shared data
56 319e1CREATEFONT exported, shared data
............

至此,你已能从Windows纷繁复杂的系统中,理出一些头续来。下面,再引入一个重要概念——重定位。
一个Windows执行程序对调用API函数,或对其它动态库的调用,在程序装入内存前,都是一些不能定位的动态连接,当程序调入内存时,这些远调用都需要重新定位,重新定位的依据就是重定位表。在Windows执行程序(包括动态库)的每个段后面,通常都跟有这样一个重定位表。重定位包含调用函数所在模块,函数序列号,以及定位在模块中的位置。

例如,用EXEHDR/v分析CHINESE.DLL得到

6 type offset target
..........
PTR 0442imp GDI.351
..........

就表明,在本段的0442H偏移处,调用了GDI的第351号函数。如果在0442H处是0000:FFFF,则表示,本段内仅此一处调用了GDI.351函数,否则,表明了本段内还有一处调用此函数,调用的位置就是0442H处所指向的内容,实际上重定位表只含有引用位置的链表的链头。那么,GDI.351是一个什么函数呢?还是用EXEHDR对GDI.EXE作一分析,就可得出,在GDI的出口(Export)函数中,第351号是ExtTextOut。
这样,我们在EXEHDR这一简单而非常有用的工具帮助下,已经在Windows的浩瀚海洋中畅游了一会,下面就来掀开“陷阱”技术的神秘面纱。

三、动态汉化Windows原理
我们知道,传统的汉化Windows的方法,是要直接修改Windows的显示、输入、打印等模块代码,或用DDK直接开发“中文设备”驱动模块,这样不仅工作量浩大,而且,系统的完备性很难保证,性能上也有很多限制(早期的长青窗口就是这样),这样,只有从内核上修改Windows核心代码才是最彻底的办法。
从Windows的模块调用机制,我们可以看到,Windows实际上是由包括在KERNEL,GDI,USER等几个模块中的众多函数支撑的。那么,修改其中涉及语言文字处理的函数,使之能适应中文需要,不就能达到汉化目的了吗?因而,我们可以得出这样的结论:在自己的模块中重新编写涉及文字显示,输入的多个函数,然后,将Windows中对这些函数的引用,改向到自己的这些模块中来。

修改哪些函数才能完成汉化,这需要深入分析Windows的内部结构,但CHINESE.DLL已明确无误地告诉了我们,在其数据段的重定位表中列出的引用函数,正是CStar修改了的Windows函数!

为了验证这一思路,我们利用RichWin作一核实。

用EXEHDR分析GDI.EXE,得出ExtTextOut函数在GDI的第一代码段6139H偏移处(不同版本的Windows其所在代码段和偏移可能不一样)。然后,用HelpWalk(也是MicrosoftVisualC++开发工具中的一个)检查GDI的Code1段,6139H处前5个字节是B8FF054555,经过运行RichWin4.3forInternet后,再查看同样的地方,已改为EA08088F3D,其实反汇编就知道,这5个字节就是代表Jmp3D8F:0808,而句柄为0x3D8F的模块,用HelpWalk能观察到正是RichWin的WSENGINE.DLL的第一代码段(模块名为TEXTMAN)。而偏移0808H处B8B73D45558BEC1E,正是一个函数起始的地方,这实际上就是RichWin所重改写的ExtTextOut函数。退出RichWin后,再用HelpWalk观察GDI的Code1代码段,一切又恢复正常!这与前面的分析结论完全吻合!那么,下一个关键点就是如何动态修改Windows的函数代码,也就是汉化Windows的核心——“陷阱”技术。

四、“陷阱”技术
讨论“陷阱”技术,还要回到前面的两个发现。发现之二,已能解释为修改的Windows函数,而发现之一,却仍是一个迷。
数据段存放的是变量及常量等内容,如果这里面包含有重定位信息,那么,必定要在变量说明中将函数指针赋给一个FARPROC类型的变量,于是,在变量说明中写下:
FARPROCFarProcFunc=ExtTextOut;
果然,我自己程序的数据段中也有了重定位信息。这样,当程序调入内存中时,变量FarProcFunc已是函数ExtTextOut的地址了。

要直接修改代码段的内容,还遇到一个难题,就是代码段是不可改写的。这时,需要用到一个未公开的Windows函数AllocCStoDSAlias取得与代码段有相同基址的可写数据段别名,其函数声明为
WORDFARPASCALAllocCStoDSAlias(WORDcode_sel);
参数是代码段的句柄,返回值是可写数据段别名句柄。

Windows中函数地址是32位,高字是其模块的内存句柄,低字是函数在模块内的偏移。将得到的可写数据段别名句柄锁定,再将函数偏移处的5个字节保留下来,然后将其改为转向替代函数(用EAJmp)

*(lpStr+wOffset)=0xEA;
*(lpStr+wOffset+1)=lpFarProcReplace;

反汇编即是JmplpFarProcReplace,最后,内存解锁。
这就是我们为Windows设的“陷阱”,当所有对此函数的调用都无条件地转到我们规定的替代函数处。当程序结束之前,将保留的5字节内容再置回来,否则,系统会崩溃。

下面给出作者编写的使Windows的ExtTextOut函数落入自己函数“陷阱”的源程序。

//源程序 relocate.c
#include
#include

BOOL WINAPI MyExtTextOut(HDC hDC, int x,
int y, UINT nInt1, const RECT
FAR* lpRect,LPCSTR lpStr, UINT nInt2, int FAR* lpInt);
WORD FAR PASCAL AllocCStoDSAlias(WORD code_sel);

typedef struct tagFUNC
{
FARPROC lpFarProcReplace;//替代函数地址
FARPROC lpFarProcWindows;//Windows函数地址
BYTEbOld;//保存原函数第一字节
LONGlOld;//保存原函数接后的四字节长值
}FUNC;

FUNCFunc={MyExtTextOut,ExtTextOut};

//Windows主函数
int PASCAL WinMain(HINSTANCE
hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
HANDLE hMemCode;//代码段句柄
WORD hMemData;//相同基址的可写数据段别名
WORD wOffset; //函数偏移
LPSTRlpStr;
LPLONG lpLong;
char lpNotice[96];

hMemCode=HIWORD((LONG) Func.lpFarProcWindows );
wOffset=LOWORD((LONG) Func.lpFarProcWindows );

wsprintf(lpNotice,"函数所在模块句柄 0x%4xH,偏移 0x%4xH",
hMemCode,wOffset);
MessageBox(NULL,lpNotice,"提示",MB_OK);

//取与代码段有相同基址的可写数据段别名
hMemData=AllocCStoDSAlias(hMemCode);

lpStr=GlobalLock(hMemData);

lpLong=(lpStr+wOffset+1 );
//保存原函数要替换的头几个字节
Func.bOld=*(lpStr+wOffset);
Func.lOld=*lpLong;

*(lpStr+wOffset)=0xEA;
*lpLong=Func.lpFarProcReplace;
GlobalUnlock(hMemData);

MessageBox(NULL,"改为自己的函数","提示",MB_OK);

//将保留的内容改回来
hMemData=AllocCStoDSAlias(hMemCode);
lpStr=GlobalLock(hMemData);
lpLong=(lpStr+wOffset+1 );
*(lpStr+wOffset)=Func.bOld;
*lpLong=Func.lOld;
GlobalUnlock(hMemData);

MessageBox(NULL,"改回原Windows函数","提示",MB_OK);
return 1;
}

//自己的替代函数
BOOL WINAPI MyExtTextOut(HDC hDC, int x, int y, UINT nInt1,
const RECT FAR* lpRect, LPCSTR lpStr, UINT nInt2, int FAR* lpInt)
{
BYTE NameDot[96]={
0x09, 0x00, 0xfd, 0x08, 0x09, 0x08, 0x09, 0x10, 0x09, 0x20,
0x79, 0x40, 0x41, 0x04, 0x47, 0xfe, 0x41, 0x40, 0x79, 0x40,
0x09, 0x20, 0x09, 0x20, 0x09, 0x10, 0x09, 0x4e, 0x51, 0x84,
0x21, 0x00, 0x02, 0x00, 0x01, 0x04, 0xff, 0xfe, 0x00, 0x00,
0x1f, 0xf0, 0x10, 0x10, 0x10, 0x10, 0x1f, 0xf0, 0x00, 0x00,
0x7f, 0xfc, 0x40, 0x04, 0x4f, 0xe4, 0x48, 0x24, 0x48, 0x24,
0x4f, 0xe4, 0x40, 0x0c, 0x10, 0x80, 0x10, 0xfc, 0x10, 0x88,
0x11, 0x50, 0x56, 0x20, 0x54, 0xd8, 0x57, 0x06, 0x54, 0x20,
0x55, 0xfc, 0x54, 0x20, 0x55, 0xfc, 0x5c, 0x20, 0x67, 0xfe,
0x00, 0x20, 0x00, 0x20, 0x00, 0x20
};

HBITMAP hBitmap,hOldBitmap;
HDC hMemDC;
BYTE far *lpDot;
int i;

for ( i=0;i<3;i++ )
{
lpDot=(LPSTR)NameDot+i*32;
hMemDC=CreateCompatibleDC(hDC);
hBitmap=CreateBitmap(16,16,1,1,lpDot);
SetBitmapBits(hBitmap,32L,lpDot);
hOldBitmap=SelectObject(hMemDC,hBitmap);
BitBlt(hDC,x+i*16,y,16,16,hMemDC,0,0,SRCCOPY);
DeleteDC(hMemDC);
DeleteObject(hBitmap);
}

return TRUE;
}

//模块定义文件relocate.def
NAMERELOCATE
EXETYPE WINDOWS
CODEPRELOAD MOVEABLE DISCARDABLE
DATAPRELOAD MOVEABLE MULTIPLE
HEAPSIZE1024
EXPORTS

五、结束语
本文从原理上分析了称为“陷阱”技术的汉化Windows方法。要彻底汉化Windows还要涉及显示,键盘输入等诸多内容,决非一日之功。但作为对“陷阱”技术的分析,本文介绍了将任一Windows函数调用改向到自己指定函数处的通用方法,这种方法可以拓展到其它应用中,如多语种显示,不同内码制式的切换显示等。
参考文献:
AndrewSchulmanDavidMaxeyMattPietrek,《未公开的Windows核心技术》,清华大学出版社,1993年。

王志东,“Windows中文环境”,《Windows软件的应用与开发》,1993.5。
(作者地址:山东潍坊华光科技股份公司研究开发中心 张高峰 邮编261041 ) 

 
 
Top
 
 回复人: sorry2000(方言) ( ) 信誉:96  2000-12-17 11:09:00Z  得分:0
 
 
 
那位仁兄研究出来什么结果能不能给我mail,bless_2000@263.net,我这里没弄到好的
代理根本上不来
 
 
Top
 
 回复人: GoodHope(好望角) ( ) 信誉:100  2000-12-17 15:10:00Z  得分:0
 
 
 
唉,研究了半天,我说怎么看怎么像16位的带码。才发现是1993年的东西。这些东西跟32位差距还是很大的。
 
 
Top
 
 回复人: stonejhao(navystone) ( ) 信誉:100  2000-12-17 15:43:00Z  得分:0
 
 
 
看看Microsoft 的Detour软件包是否符合你的要求!
 
 
Top
 
 回复人: GoodHope(好望角) ( ) 信誉:100  2000-12-17 15:48:00Z  得分:0
 
 
 
以前老看见Trap的资料,到用的时候却一个也找不到。
中国人真TMD藏私,什么都喜欢偷偷留一手,还想进步!
转贴一个讨论吧。

金山词霸”屏幕取词技术揭密(讨论稿)

主题  屏幕取词技术系列讲座(一)
作者   亦东

很多人对这个问题感兴趣。
原因是这项技术让人感觉很神奇,也很有商业价值。
现在词典市场金山词霸占了绝对优势,所以再做字典也没什么前途了。我就是这么认为的,所以我虽然掌握了这项技术,却没去做字典软件。只做了一个和词霸相似的软件自己用,本来想拿出来做共享软件,但我的词库是“偷”来的,而且词汇不多,所以也就算了,词库太小,只能取词有什么用呢?而且词霸有共享版的。
但既然很多人想了解这项技术,我也不会保留。我准备分多次讲述这项技术的所有细节。
大约每周一两次。想知道的人就常常来看看吧!

一.基础知识
首先想编这种程序需要一些基础知识。
会用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 W

抱歉!评论已关闭.