要实现修改导入段来拦截API必须对PE文件格式有很好的了解。网上关于它的资料铺天盖地,自己搜吧。此处不打算介绍。
为了修改要拦截的函数在导入段的地址,首先要获得PE文件导入段的地址。这可以调用ImageDirectoryEntryToData.函数。
PVOID WINAPI ImageDirectoryEntryToData(
__in PVOID Base,
__in BOOLEAN MappedAsImage,
__in USHORT DirectoryEntry,
__out PULONG Size );
Base为要获得导入段所在模块的基地址。它一般是GetModuleHandle的返回值。
MappedAsImage,它为true时,系统将该模块以映像文件的形式映射,否则以数据文件的形式映射。
DirectoryEntry,要获得的段的索引。此函数不仅仅能够获得导入段的地址,根据此索引的不同,该函数返回对应段的地址。此处我们使用IMAGE_DIRECTORY_ENTRY_IMPORT表示我们要获得导入段的地址。
Size返回表项的大小。注意其类型。
看代码:
ULONG size;
HMODULE hModule=GetModuleHandle(NULL);
PIMAGE_IMPORT_DESCRIPTOR pImport=
ImageDirectoryEntryToData(hModule,true,IMAGE_DIRECTORY_ENTRY_IMPORT,&size);
while(pImport->FirstThunk)
{
int i=0;
char *ModuleName=(char*)((BYTE*)hModule+pImport->Name);
PIMAGE_THUNK_DATA pThunk=(PIMAGE_THUNK_DATA)
((BYTE*)hModule+pImport->FirstThunk);
while(pThunk->u1.Function)
{
char*Func=(char*)((BYTE*)hModule+pThunk->u1.AddressOfData+2);
If(Func=="MessageBoxA")
{
MessageBox("找到函数");
}
}
}
IMAGE_IMPORT_DESCRIPTOR结构是导入段的基本结构,导入段是由此类型组成的数组构成,每个DLL对应数组中的一项。数组的最后一项为NULL。每一项中有两个关键成员,指出IMAGEA_THUNK_DATA类型的数组的偏移量,这两个数组分别列出了从此DLL导入的函数名称,以及它它们在本进程地址空间的RVA。
以下是其定义:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;// 指向一个 IMAGE_THUNK_DATA 结构数组的RVA
}
DWORD TimeDateStamp;// 文件生成的时间
DWORD ForwarderChain;// 这个数据一般为0,可以不关心
DWORD Name; // RVA,指向DLL名字的指针,ASCII字符串
DWORD FirstThunk; //指向一个 IMAGE_THUNK_DATA 结构数组的RVA,这个数据与IAT所指向的地址一致
}IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR
OriginalFirstThunk和FirstThunk是此结构中的关键成员。它们指出这两个IMAGE_THUNK_DATA类型的数组。
Name是指向DLL名称的指针。
pImport指向模块的导入段,可以使用它来访问IMAGE_IMPORT_DESCRIPTOR结构的每一个成员。
注意:以上所有偏移量都是相对于模块基地址的,因此在实际使用中一定要加上模块的地址。
OriginalFirstThunk指向NAT,NAT列出了从该DLL导入的所有导入函数的名称。
FirstThunk指向IAT,IAT列出了与NAT相对应的导入函数的地址。
看此结构定义:
typedef struct _IMAGE_THUNK_DATA32 {
union {
PBYTE ForwarderString;
PDWORD Function;
DWORD Ordinal;
PIMAGE_IMPORT_BY_NAME AddressOfData;
} u1;
} IMAGE_THUNK_DATA32;
上述代码中,外层循环while(pImport->FirstThunk)用于遍历所有DLL,内层循环用于遍历查找每个DLL中的导入函数名称。我们以查找MessageBoxA函数为例来做介绍。此处我们通过对NAT进行扫描,将其与我们给出的字符串类型的函数名MessageBoxA进行比较。注意要使用大小写不敏感的函数:
int WINAPI lstrcmpi(
__in LPCTSTR lpString1,
__in LPCTSTR lpString2
);
这是一种方法,另一种方法是比较函数地址。我们可以对IAT进行扫描,将得到的地址与我们给出的函数地址进行比较。这两种方法是经常使用的方法。
下面给出使用地址比较的方法。
HMODULE hModule=GetModuleHandle(NULL);
ULONG size;
PIMAGE_IMPORT_DESCRIPTOR pImport=(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData
(hModule,true,IMAGE_DIRECTORY_ENTRY_IMPORT,&size);
UpdateData();
//获得MessageBoxA的地址。
PROC pfnOld=GetProcAddress(GetModuleHandle("User32.dll"),"MessageBoxA");
while(pImport->FirstThunk)
{
char *ModuleName=(char*)((BYTE*)hModule+pImport->Name);
PIMAGE_THUNK_DATA pThunk=(PIMAGE_THUNK_DATA)((BYTE*)hModule+pImport->FirstThunk);
while(pThunk->u1.Function)
{
PROC *ppfn=(PROC*)&(pThunk->u1.Function);
if(*ppfn==pfnOld)pfnOld为要查询的函数地址。
{
MessageBox("找到给定的函数");
return ;
}
pThunk++;
}
pImport++;
}
当然以上仅仅是找到相应的函数。要用我们期望的函数代替它,还需要我们自己定义函数。
此处有一点要特别注意,当我们对某函数进行拦截时,要替换的函数必须与被替换的函数的原型完全相同。
比如此时我们要对MessageBoxA进行拦截,我们定义自己的函数的原型与MessageBoxA原型是完全一致的:
int WINAPI MyMessageBoxA(HWND hWnd,LPSTR str,LPSTR caption ,UINT type)
{
//定义函数的行为。
}
我们可以在函数中调用被拦截的函数,也可以不对拦截的函数进行调用而进行其他操作。
注意:在拦截后,原来的函数地址已经被我们自定义的函数的地址覆盖,如果之后需要调用被拦截的函数,就必须在覆盖之前保存被拦截函数的地址。如果我们没有保存而直接调用,调用的只是替换后的函数。
以下代码将对MessageBox的调用进行拦截,并用我们的自定义函数MyMessageBox替代。
g_addr用于存储拦截之前MessageBox的地址
Typedef int (WINAPI *PFNMESSAGEBOX(HWND,LPSTR,LPSTR,UINT);//定义函数指针。
PFNMESSAGEBOX g_addr=(PFNMESSAGEBOX)MessageBoxA;//存储MessageBoxA的地址。
//自定义的函数。
int WINAPI MyMessage(HWND hWnd,LPSTR a,LPSTR b,UINT type)
{
return ((PFNMESSAGEBOX)g_addr)(hWnd,a,"替换后的函数!?",MB_YESNO);
}
//对 MessageBox进行拦截,用MyMessageBox进行替换。
Void ReplaceOneFunc(PCSTR ModuleName,PFNMESSAGEBOX pfnOld,PFNMESSAGEBOX pfnNew)
{
pfnOld=(PFNMESSAGEBOX)MessageBoxA;
pfnNew=(PFNMESSAGEBOX)MyMessageBox;
HMODULE hModule=GetModuleHandle(NULL);
ULONG size;
PIMAGE_IMPORT_DESCRIPTOR pImport=(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData
(hModule,true,IMAGE_DIRECTORY_ENTRY_IMPORT,&size);
UpdateData();
while(pImport->FirstThunk)
{
char *ModuleName=(char*)((BYTE*)hModule+pImport->Name);
PIMAGE_THUNK_DATA pThunk=(PIMAGE_THUNK_DATA)((BYTE*)hModule+pImport->FirstThunk);
while(pThunk->u1.Function)
{
PFNMESSAGEBOX *ppfn=(PFNMESSAGEBOX*)&(pThunk->u1.Function);
if(*ppfn==pfnOld)
{
MessageBox("找到函数!恭喜你!");
SIZE_T num;
WriteProcessMemory(GetCurrentProcess(),ppfn,&pfnNew,4,&num);
MessageBox(NULL,"现在调用的是替换后的API!","",MB_OK);
((PFNMESSAGEBOX)g_addr)(NULL,"哈哈,现在终于轮到你了","",MB_OK);
return ;
}
pThunk++;
}
pImport++;
}
MessageBox("没有找到该函数!");
}
WriteProcessMemory(GetCurrentProcess(),ppfn,&pfnNew,4,&num);
此函数用于修改内存。实现用MyMessagebox的地址替换MessageBox的地址。关于此函数的功能请参考MSDN。
((PFNMESSAGEBOX)g_addr)(NULL,"哈哈,现在终于轮到你了","",MB_OK);
此句用于调用在拦截之前MessageBox的地址。此处的调用是调用的MessageBox函数,而不是MyMessageBox函数。
至此关于修改导入段拦截API介绍完毕。
相关推荐
要实现修改导入段来拦截API必须对PE文件格式有很好的了解。网上关于它的资料铺天盖地,自己搜吧。此处不打算介绍。 为了修改要拦截的函数在导入段的地址,首先要获得PE文件导入段的地址。这可以调
API拦截 API拦截API拦截API拦截API拦截API拦截
WINDOWS API拦截技术,调用WINDOWS API拦截消息。
教您如何进行WindowsAPI拦截的实现
《windows核心编程》第22章最后一个例子(拦截API的问题)
vc拦截Windows API调用 的源代码
易语言源代码_Windows Hook 易核心编程(3) API Hook 续 拦截API.zip
API钩子APIHOOK 2.0 源代码 拦截Windows API接口程序源代码
一个完整的例子全局拦截API,十分强大,
Windows API 拦截教程 API HOOK 很稀缺的资料,欢迎下载
Windows系统API函数拦截技术研究
C++ API拦截源码 修改导入表 自己看一下,要改的地方改一下,很不错的例子
VB如何拦截API调用.zip
WIndows API 拦截测试程序
api函数拦截。
Windows用户层下拦截api的原理与实现(附源码) (2008-03-29 16:15:07)转载▼ 标签: computer 杂谈 声明:本页所发布的技术文章及其附件,供自由技术传播,拒绝商业使用。本页文章及其附件的所有权归属本文作者...
易语言API网络拦截源码,API网络拦截,接收send网截,接收recv网截,安装send网截,安装recv网截,卸载网截,BeginHook,StopHook,InstallApiHook,子程序到整数,UninstallApiHook,GetApiHookInfo,拷贝文本,Recv,...
API拦截技术,很好的一个学习资料! ..................
WINDOWS API拦截技术与实现,拦截WINDOW 的消息。钩子实现。
一个实现API拦截的具体例子。欢迎大家 多提宝贵意见。