首页
社区
课程
招聘
[原创]导出表钩子------EAT HOOK
发表于: 2008-4-5 17:13 46995

[原创]导出表钩子------EAT HOOK

2008-4-5 17:13
46995

看了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导出函数在内存的地址
VOID ListKernelFunctionAndAddress()
{
      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);

}

}
运行后:


--------------------
--------------
-----------------
---------------------
  好了,讲了这么多时候进去正题,怎样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)();

HANDLE
MyPsGetCurrentProcessId()

{
    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这个头文件

里,因为很长就不帖了,可以在附件里看,上面的代码很多地方不是很好,需要自己修改,不

保证在你机器不蓝,呵呵,学习靠思考,在代码里我修改了一处地方


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (45)
雪    币: 290
活跃值: (11)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
2
Windows的运行机制    复杂的很

从哪开始进去啊。。。会把人搞晕不?

有没有比较入门的 大众话点 系统的点资料啊
2008-4-5 18:31
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
shoucang....
2008-4-6 00:48
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
4
不错,收录到专题中了。
2008-4-6 14:35
0
雪    币: 581
活跃值: (149)
能力值: ( LV12,RANK:600 )
在线值:
发帖
回帖
粉丝
5
哇.....谢谢combojiang 的鼓励
2008-4-6 17:43
0
雪    币: 145
活跃值: (85)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
讲的很复杂..
2008-4-6 17:59
0
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
好文章,学习了
2008-4-6 20:22
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
蓝屏
2008-4-6 20:59
0
雪    币: 381
活跃值: (140)
能力值: ( LV13,RANK:330 )
在线值:
发帖
回帖
粉丝
9
楼上的BSOD了
只要是关于钩子的,都顶之
2008-4-18 09:39
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
不错,
MmGetSystemRoutineAddress取的是挂了服务表后的,
可能不干净了。
这个表是不是在inline hook之后呢,
inline hook里直接jmp处理,会绕过这个表不
2008-5-2 00:52
0
雪    币: 138
活跃值: (461)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
很好.谢谢.

最好把
        PULONG   pAddressOfNameOrdinals;
改成
        PUSHORT pAddressOfNameOrdinals;
不然有可能会蓝屏.

在VM中测试,函数地址确实改了,但测试时,没有调用自定的函数(没看到dbgprint输出的信息)

不知是怎么回事?
2008-5-11 19:26
0
雪    币: 213
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
还是要BSOD
2008-5-31 19:01
0
雪    币: 581
活跃值: (149)
能力值: ( LV12,RANK:600 )
在线值:
发帖
回帖
粉丝
13
  EAT HOOK有一个时间的问题...对那些已经加载运行的程序无效.....你可以打开ICESWORD就可以看到输出了
2008-6-1 08:10
0
雪    币: 581
活跃值: (149)
能力值: ( LV12,RANK:600 )
在线值:
发帖
回帖
粉丝
14
参考11楼的....如果蓝的话自己再改改...
2008-6-1 08:11
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
啊,我要疯了,各种蓝,蓝各种,各种加各种。
调试你的驱动蓝屏。编译成功后依然蓝屏.
另外问一下楼主,为什么GetModlueBaseAdress在我的机器上得到的ntkrnlpa.exe基址为0????
上传的附件:
  • 1.JPG (31.55kb,3066次下载)
2008-6-1 10:34
0
雪    币: 581
活跃值: (149)
能力值: ( LV12,RANK:600 )
在线值:
发帖
回帖
粉丝
16
你先运行我附件里的驱动看看....如果可以的话再说
2008-6-1 11:16
0
雪    币: 581
活跃值: (149)
能力值: ( LV12,RANK:600 )
在线值:
发帖
回帖
粉丝
17
BaseAddress=  GetModlueBaseAdress("ntkrnlpa.exe");//-----这句可能有问题....当时是在我机器里试的...用了硬编码...事实上ZwQuerySystemInformation返回的第一个模块就是我们要的.....所以你可以改下代码....或者
BaseAddress=  GetModlueBaseAdress("ntkrnlpa.exe");改为
BaseAddress=  GetModlueBaseAdress("ntoskrnl.exe");看看
2008-6-1 11:20
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
恩好我试一下
你附加里的驱动我运行了,还是蓝屏(我的机器直接重启)
2008-6-1 11:34
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
Sysnap正如你所说的
BaseAddress=  GetModlueBaseAdress("ntoskrnl.exe");确实可以得到基址,
另外还有几个小问题:
ULONG g_OriginalPsGetCurrentProcessId;是保存原函数的的地址

                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= (ULONG)hMod + pAddressOfFunctions[index] ;
_asm
        {
                MOV        EAX, CR0

g_OriginalPsGetCurrentProcessId= (ULONG)hMod + pAddressOfFunctions[index] ;是不是应该放在pAddressOfFunctions[index] = ( PCHAR )MyPsGetCurrentProcessId - BaseAddress;前面啊,不然g_OriginalPsGetCurrentProcessId得到的是( PCHAR )MyPsGetCurrentProcessId - BaseAddress;

Byw
Sysnap你有没有什么QQ的啊~~给个号呗,研究下问题
2008-6-2 08:07
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
另外你有没有出现过这样的问题
g_OriginalPsGetCurrentProcessId=0了
上传的附件:
  • 2.JPG (86.11kb,502次下载)
  • 1.JPG (68.49kb,500次下载)
2008-6-2 08:10
0
雪    币: 581
活跃值: (149)
能力值: ( LV12,RANK:600 )
在线值:
发帖
回帖
粉丝
21
嗯...看你图片就知道你成功了..呵呵....
2008-6-2 08:17
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
帮我回答下19楼的问题呀
2008-6-2 09:01
0
雪    币: 581
活跃值: (149)
能力值: ( LV12,RANK:600 )
在线值:
发帖
回帖
粉丝
23
嗯....的确是你所说的...当时俺的代码可能改糊涂了...
g_OriginalPsGetCurrentProcessId=0这个问题不会出现
2008-6-2 09:15
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
哦,好的明白
2008-6-2 09:26
0
雪    币: 213
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
我用冰刃看了,获取的ntoskrnl.exe的地址没有错,
但是好像并没有HOOK到样, 运行其他的程序,并没有调用我的My函数
2008-6-3 12:49
0
游客
登录 | 注册 方可回帖
返回
//