导出表钩子------EAT HOOK(转)

来源:互联网 发布:对对子软件在线 编辑:程序博客网 时间:2024/05/02 06:11
标 题: 【原创】导出表钩子------EAT HOOK
作 者: Sysnap
时 间: 2008-04-05,17:13:48
链 接: http://bbs.pediy.com/showthread.php?t=62574

看了combojiang大侠的rootkit专题,发现少了一个导出表钩子,既EAT;HOOK,刚好前几天自己搞了个IAT HOOK,然后就把其中的代码稍做修改,于是有这篇文章,
 
偶学的东西不久,很多东西还不知道,请多指教,呵呵
 
导出表钩子比导入表钩子感觉好用多,先说下原理吧,函数导入的函数的地址是再运行时候才确定的,比如我们的一个驱动程序导入了PsGetCurrentProcessId这个ntkrnlpa.exe
 
导出的函数,那在我们驱动程序加载运行的时候,装载程序会确定ntkrnlpa.exe在内存的基地址,接着遍历它的导出表,在AddressOfNames指向的"函数名字表"中找到
 
PsGetCurrentProcessId的位置,也就是如果在AddressOfNames[i]中找到PsGetCurrentProcessId,那就用i在AddressOfNameOrdinals中索引,假使得到是X,那么
 
AddressOfFunctions[index]的值就是PsGetCurrentProcessId的RVA了,最后就可以知道PsGetCurrentProcessId在内存的值是MM=ntkrnlpa.exe在内存的基地址
 
+PsGetCurrentProcessId的RVA,然后转载程序就把这个值写到我们驱动程序的IAT中,好了知道这些后,EAT HOOK就是修改PsGetCurrentProcessId的RVA,使得
 
PsGetCurrentProcessId的RVA(修改后的)+ntkrnlpa.exe在内存的基地址=我们自己函数的值,这样装载程序会把我们的函数的地址写入那些调用PsGetCurrentProcessId
 
的驱动程序的IAT,那么当那些驱动程序调用PsGetCurrentProcessId时,实际上是执行了我们自己的函数...呵呵.是不是比IAT HOOK更好用呢
 
  EAT HOOK可以用来监控系统函数的调用情况,比如我们EAT HOOK了
 
PsGetCurrentProcessId,那谁调用该函数我们就知道了,你也可以HOOK KeInitializeApc等热门函数,其实知道了EAT HOOK原理后,我们
 
可以修改函数名字表,比如把PsGetCurrentProcessId改成其它名字,这样装载程序遍历"函
 
数名字表"就找不到匹对的名字,那驱动程序就宣告装载失败,详细代码请看
<<利用导出表来禁止一些驱动程序的加载>>http://bbs.pediy.com/showthread.php?t=62531
 
 
  那怎么防止EAT HOOK,一个方法是自己定位函数在内存地址,请看下面的代码,用于枚举
 
ntkrnlpa.exe导出函数在内存的地址
{      HANDLE hMod;      PVOID BaseAddress = NULL;      IMAGE_DOS_HEADER * dosheader;      IMAGE_OPTIONAL_HEADER * opthdr;      PIMAGE_EXPORT_DIRECTORY exports;       USHORT index=0 ;       ULONG addr, i;       PVOID FuncNameRVA;      PUCHAR pFuncName = NULL;      PULONG pAddressOfFunctions,pAddressOfNames,pAddressOfNameOrdinals;       BaseAddress= GetDriverBaseAdress("ntkrnlpa.exe");      DbgPrint("Map BaseAddress is:%x\n",BaseAddress);      hMod = BaseAddress;       dosheader = (IMAGE_DOS_HEADER *)hMod;      opthdr =(IMAGE_OPTIONAL_HEADER *) ((BYTE*)hMod+dosheader->e_lfanew+24);      exports = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)dosheader+ opthdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);       pAddressOfFunctions=(ULONG*)((BYTE*)hMod+exports->AddressOfFunctions);       pAddressOfNames=(ULONG*)((BYTE*)hMod+exports->AddressOfNames);       pAddressOfNameOrdinals=(USHORT*)((BYTE*)hMod+exports->AddressOfNameOrdinals);         for (i = 0; i < exports->NumberOfNames; i++) {      index=pAddressOfNameOrdinals[i];     addr=pAddressOfFunctions[index];     pFuncName = (PUCHAR)( (BYTE*)hMod + pAddressOfNames[i]);     addr = pAddressOfFunctions[index];     DbgPrint("the function: %s is at: 0x%x\n",pFuncName,addr+(BYTE*)hMod); } 



运行后:
 
未命名.jpg下载此附件需要消耗2Kx,下载中会自动扣除。
--------------------
--------------
-----------------
---------------------
  好了,讲了这么多时候进去正题,怎样EAT HOOK,这里我们以HOOK ntkrnlpa.exe导出的
 
PsGetCurrentProcessId
   首先我们是定位ntkrnlpa.exe被加载在内存中的什么地方,那就写一个函数吧,
PVOID GetModlueBaseAdress(char* ModlueName){    ULONG size,index;    PULONG buf;NTSTATUS status;    PSYSTEM_MODULE_INFORMATION module;    PVOID driverAddress=0;     ZwQuerySystemInformation(SystemModuleInformation,&size, 0, &size);if(NULL==(buf = (PULONG)ExAllocatePool(PagedPool, size)))    {        DbgPrint("failed alloc memory failed \n");        return 0;    }status=ZwQuerySystemInformation(SystemModuleInformation,buf, size , 0);    if(!NT_SUCCESS( status ))    {DbgPrint("failed query\n");     return 0;    }module = (PSYSTEM_MODULE_INFORMATION)(( PULONG )buf + 1);    for (index = 0; index < *buf; index++)    if (_stricmp(module[index].ImageName + module[index].ModuleNameOffset, ModlueName) == 0)     {driverAddress = module[index].Base;DbgPrint("Module found at:%x\n",driverAddress);    }    ExFreePool(buf);    return driverAddress;}


自己添加点测试代码编译下,没什么问题,这样我们就完成了第一个问题
 
  接着是写自己的函数了,就是替换PsGetCurrentProcessId的函数,这里我们很简单的输
 
出点内容就可以了
ULONG g_OriginalPsGetCurrentProcessId;typedef HANDLE (*PSGETCURRENTPROCESSID)(); HANDLEMyPsGetCurrentProcessId() {    HANDLE handle;    DbgPrint("HOOK_PsGetCurrentProcessId called!\n");handle =((PSGETCURRENTPROCESSID)(g_OriginalPsGetCurrentProcessId))();return handle; } 


 
好了,那就开始写安装钩子程序吧,因为在卸在钩子时需要用到一些变量,这里我们就把安
 
装和卸载写成一个函数就可以了,注意IN unsigned int test,传入1表示安装钩子,否则表
 
示卸载,IN PCSTR funName这里我们传入PsGetCurrentProcessId,好了请看代码
 
VOID StartHook_And_Unhook(IN PCSTR funName, IN unsigned int test) {    HANDLE hMod;    PUCHAR BaseAddress = NULL;    IMAGE_DOS_HEADER * dosheader;    IMAGE_OPTIONAL_HEADER * opthdr;    PIMAGE_EXPORT_DIRECTORY exports;     USHORT index=0 ;     ULONG addr ,i;    PUCHAR pFuncName = NULL;    PULONG pAddressOfFunctions,pAddressOfNames;    PUSHORT pAddressOfNameOrdinals;     BaseAddress= GetModlueBaseAdress("ntkrnlpa.exe");    DbgPrint("Map BaseAddress is:%x\n",BaseAddress);    hMod = BaseAddress;     dosheader = (IMAGE_DOS_HEADER *)hMod;    opthdr =(IMAGE_OPTIONAL_HEADER *) ((BYTE*)hMod+dosheader->e_lfanew+24);    exports = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)dosheader+ opthdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);     pAddressOfFunctions=(ULONG*)((BYTE*)hMod+exports->AddressOfFunctions);     pAddressOfNames=(ULONG*)((BYTE*)hMod+exports->AddressOfNames);     pAddressOfNameOrdinals=(USHORT*)((BYTE*)hMod+exports->AddressOfNameOrdinals);      for (i = 0; i < exports->NumberOfNames; i++) {    index=pAddressOfNameOrdinals[i];    pFuncName = (PUCHAR)( (BYTE*)hMod + pAddressOfNames[i]);    if (_stricmp( (char*)pFuncName,funName) == 0){    addr=pAddressOfFunctions[index];     break;} }  if(test==1)    {     _asm    {        CLI                            MOV    EAX, CR0                AND EAX, NOT 10000H         MOV    CR0, EAX            }           DbgPrint("PsGetCurrentProcessId is:%x\n",(PUCHAR)hMod + pAddressOfFunctions[index]);      pAddressOfFunctions[index] = ( PCHAR )MyPsGetCurrentProcessId - BaseAddress;      DbgPrint("g_OriginalPsGetCurrentProcessId is:%x\n",g_OriginalPsGetCurrentProcessId);      g_OriginalPsGetCurrentProcessId= (PUCHAR)hMod + pAddressOfFunctions[index] ;_asm     {        MOV    EAX, CR0                OR    EAX, 10000H                    MOV    CR0, EAX                    STI                        }    }     else    {_asm    {        CLI                            MOV    EAX, CR0                AND EAX, NOT 10000H         MOV    CR0, EAX            }     pAddressOfFunctions[index] = ( PCHAR )g_OriginalPsGetCurrentProcessId - BaseAddress;  _asm     {        MOV    EAX, CR0                OR    EAX, 10000H                    MOV    CR0, EAX                    STI                        }      } } 


好了,基本框架就差不多了,接着就是一些结构的声明,我们把它放在hookiat.h这个头文件
 
里,因为很长就不帖了,可以在附件里看,上面的代码很多地方不是很好,需要自己修改,不
 
保证在你机器不蓝,呵呵,学习靠思考,在代码里我修改了一处地方
原创粉丝点击