首页
社区
课程
招聘
[讨论]Win32 和 Ring0下 Inline Hook统一
发表于: 2010-12-23 10:31 13567

[讨论]Win32 和 Ring0下 Inline Hook统一

2010-12-23 10:31
13567
看到网上到 Inline Hook都是修改前 5 个字节 ,怀疑他们是不是互相抄来抄去,Win32下Hook前8个字节,然后在自己的函数里面先恢复再执行原来的函数,没有问题啊,我就是这样Hook 了
NtOpenProcess的,可以啊??这样的话好像也不是像RootKit专题中说的Win32 和 Ring0 中inline Hook有很多不同点啊。。。。

    还是我什么地方理解不对呢??

顺便贴下我的代码啊:

/////DriverHeader.h
#ifdef __cplusplus
extern "C"
{
#endif

#include "ntddk.h"

#ifdef __cplusplus
}
#endif

#define INITCODE code_seg("INIT")
#define PAGECODE code_seg("PAGE")
#define LOCKCODE code_seg()

#define INITDATA date_seg("INIT")
#define PAGEDATA data_seg("PAGE")
#define LOCKDATA data_seg()

VOID MyDriverUnload(IN PDRIVER_OBJECT  DriverObject);

//声明原始API
extern "C" NTSYSAPI NTSTATUS NTAPI NtOpenProcess(
                          OUT PHANDLE ProcessHandle,    //返回进程句柄
                          IN ACCESS_MASK DesiredAccess,    //进程访问请求
                          IN POBJECT_ATTRIBUTES ObjectAttributes,    //对象属性
                          IN PCLIENT_ID ClientId);    //传入进程的标识符的结构
//声明自己的函数
NTSTATUS MyNtOpenProcess(
                                                 OUT PHANDLE ProcessHandle,
                                                 IN ACCESS_MASK DesiredAccess,
                                                 IN POBJECT_ATTRIBUTES ObjectAttributes,
                                                 IN PCLIENT_ID ClientId);
//保存原ZwOpenProcess的地址
typedef NTSTATUS (*REALZEOPENPROCESS)(OUT PHANDLE ProcessHandle,
                                                                          IN ACCESS_MASK DesiredAccess,
                                                                          IN POBJECT_ATTRIBUTES ObjectAttributes,
                                                                          IN PCLIENT_ID ClientId);
REALZEOPENPROCESS SysNtOpenProcess;

VOID Hook();
VOID CancelPageProtection();
VOID RecoverPageProtection();

/////DriverSrc.cpp
#include "DriverHeader.h"
typedef unsigned char BYTE;

BYTE mNewBytesToJmp[8]={ 0xB8, 0x0, 0x0, 0x40, 0x0, 0xFF, 0xE0, 0x0 };//跳转语句
BYTE mOldBytes[8]={0};//保存函数原来的前8个字节

#pragma INITCODE
NTSTATUS DriverEntry(IN PDRIVER_OBJECT  DriverObject,IN PUNICODE_STRING  RegistryPath)
{
    //分配派遣函数
        DriverObject->DriverUnload=MyDriverUnload;
    KdPrint(("Driverload...\n"));
   
        Hook();

        return STATUS_SUCCESS;
}

#pragma PAGECODE
VOID MyDriverUnload(IN PDRIVER_OBJECT  DriverObject)
{
        CancelPageProtection();//恢复
        KIRQL vOldIRQL=KeRaiseIrqlToDpcLevel();
        RtlCopyMemory(SysNtOpenProcess,mOldBytes,8*sizeof(BYTE));
        KeLowerIrql(vOldIRQL);
        RecoverPageProtection();

        KdPrint(("DriverUnload...\n"));
}

#pragma PAGECODE

VOID CancelPageProtection()
{
        //取消SSDT的内存页面保护属性
        _asm{
                //去掉写保护
                push  eax
                mov  eax, cr0
                and  eax, 0FFFEFFFFh
                mov  cr0, eax
                pop  eax
        }
}

#pragma PAGECODE
VOID RecoverPageProtection()
{
        //恢复SSDT的内存页面保护属性
    _asm{
                //恢复写保护
            push  eax
                mov  eax, cr0
                or  eax, 10000h
                mov  cr0, eax
                pop  eax
        }
}

#pragma PAGECODE

NTSTATUS MyNtOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId)
{
    //先恢复原来函数到前几个字节到内容
        KdPrint(("NtOpenProcess Called!\n"));

        CancelPageProtection();
        KIRQL vOldIRQL=KeRaiseIrqlToDpcLevel();
        RtlCopyMemory(SysNtOpenProcess,mOldBytes,8*sizeof(BYTE));
        KeLowerIrql(vOldIRQL);
        RecoverPageProtection();

        if((LONG)ClientId->UniqueProcess==296)//保护一下RTX
           return STATUS_ACCESS_DENIED;//拒绝访问
        NTSTATUS status=NtOpenProcess(ProcessHandle,DesiredAccess,ObjectAttributes,ClientId);

        Hook();//再次Hook
        return status;
}

#pragma PAGECODE

VOID Hook()
{
        UNICODE_STRING strFunctionName;
        RtlInitUnicodeString(&strFunctionName,L"NtOpenProcess");
        SysNtOpenProcess=(REALZEOPENPROCESS)MmGetSystemRoutineAddress(&strFunctionName);
        if(SysNtOpenProcess)
        {
                KdPrint(("Get Function Orignal Address OK...\n"));

                KIRQL vOldIRQL=KeRaiseIrqlToDpcLevel();//提高代码的优先级等级
                *(ULONG*)(mNewBytesToJmp+1)=(ULONG)MyNtOpenProcess;
                CancelPageProtection();//取消内存写 保护
                RtlCopyMemory(mOldBytes,SysNtOpenProcess,8*sizeof(BYTE));//保存原来的内容
                RtlCopyMemory(SysNtOpenProcess,mNewBytesToJmp,8*sizeof(BYTE));//写入跳转语句
                RecoverPageProtection();//恢复内存写保护
                KeLowerIrql(vOldIRQL);
        }
        else
        {
                KdPrint(("Get Function Orignal Address Failed...\n"));
        }
}

//////////////////////////
大家看看,还是这样有效率上的问题,这样可以和Win32下的Inline Hook 统一了

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (16)
雪    币: 306
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
jmp xxxxxxxx 正好5个字节,这样修改的最少,并不是不行 ,我是菜鸟
2010-12-23 13:23
0
雪    币: 1157
活跃值: (847)
能力值: ( LV8,RANK:150 )
在线值:
发帖
回帖
粉丝
3
其实我是想说这个 相对跳转 和 绝对跳转的问题啦,Ring0下也来个绝对跳转,这样思路清晰。。
2010-12-23 13:38
0
雪    币: 4817
活跃值: (23)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
不大明白LZ的意思,HOOK技术本身跟RING0或RING3没有关系啊,在内核和应用层inline HOOk函数的流程也没变啊,只不过调用的函数不一样而已。你指的RootKit专题的不同点是不是这段话:“对于ring3下的inline hook使用起来非常的方便,也非常简单。但是到了ring0,inline hook就麻烦些,搞不好就出现了bsod.”这是2008年的文章,当时出现的问题现在说不定已经解决了呢。再说,人家可没说有很多不同点啊,就说内核HOOK比较麻烦,这个本来就是,写内核程序都是比较麻烦的嘛,稍有不慎,就蓝屏,很正常的呀。
INLINE HOOK修改前五个字节不是抄来抄去的,本来就是修改五个字节嘛,一个是跳转指令,其他4个字节刚好可以寻址4G,这个使得5个字节的HOOK很通用,干嘛非得像LZ那样用8个字节呢?
2010-12-27 17:03
0
雪    币: 7651
活跃值: (523)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
5
同意楼上,另外调用原函数时先恢复再执行真的很挫……
2010-12-28 12:02
0
雪    币: 4817
活跃值: (23)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
呵呵,我都忘了这个啦!恢复再执行的做法有风险的,自己试验可以,商业用要慎重!!!
LZ尽可以试试大家通用的五个字节HOOK,都在用,没有问题的!
2010-12-28 13:34
0
雪    币: 1157
活跃值: (847)
能力值: ( LV8,RANK:150 )
在线值:
发帖
回帖
粉丝
7
多谢指教,恢复之后执行有什么风险呢??我怎么样才能触发这样的BUG呢??8字节是应该改成5个字节,我的错,现在想知道恢复后执行 “挫” 在哪里啊???呵呵
2010-12-28 19:21
0
雪    币: 4817
活跃值: (23)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
想触发这个BUG还真有点儿困难啊,呵呵!我说了你就明白了。
你inline HOOK 了 NtOpenProcess,对于应用层的两个线程A和B来讲,A调用Kernel32!OpenProcess会进入你的处理函数,你先恢复了NtOpenProcess,再调用,调用完再HOOK,问题是这三个操作不是原子操作,可能会被抢占,如果当你恢复NtOpenProcess时线程A被抢占,线程B调用了OpenProcess,进入内核时刚好你的NtOpenProcess还没还原,那么进程B的OpenProcess不受你的监控。
总而言之,不能解决多线程的问题,而在内核监控,一定要考虑多线程的问题,因为内核是所有进程的。你要是恢复再执行,还不如SSDT HOOK呢。
2010-12-28 20:31
0
雪    币: 157
活跃值: (451)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
其实在内核态下。解决多线程只用关中断和开中断就行了。
我hook的方法是改跳转。

先执行修改了的代码
再jmp  原地址+修改的长度

VOID CancelPageProtection()
{
  //取消SSDT的内存页面保护属性
  _asm{
    //去掉写保护
    push  eax
    mov  eax, cr0
    and  eax, 0FFFEFFFFh
    mov  cr0, eax
    pop  eax
  }
}

#pragma PAGECODE
VOID RecoverPageProtection()
{
  //恢复SSDT的内存页面保护属性
    _asm{
    //恢复写保护
      push  eax
    mov  eax, cr0
    or  eax, 10000h
    mov  cr0, eax
    pop  eax
  }
}

这两断代码。不知道是不是真的有用。我从来没用过。
2010-12-29 00:16
0
雪    币: 1157
活跃值: (847)
能力值: ( LV8,RANK:150 )
在线值:
发帖
回帖
粉丝
10
这个用 自旋锁 能不能解决上述的 多线程的同步问题呢???
2010-12-30 17:28
0
雪    币: 4817
活跃值: (23)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
我说错了,这个问题没法用同步的方式来解决,LZ还是跟《天杀》说得那样,该了解以下恢复后执行的严重性吧
至于为什么无法用同步来解决,也请LZ自己想想吧。
2010-12-30 17:48
0
雪    币: 239
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
这个根本就不是旋不旋的问题,是他根本还没明白为啥会恢复后执行的严重性
原因都没明白就要想去解决,有意义吗?
2010-12-30 17:49
0
雪    币: 1157
活跃值: (847)
能力值: ( LV8,RANK:150 )
在线值:
发帖
回帖
粉丝
13
那请你说明白些原因吧
2010-12-30 17:56
0
雪    币: 1157
活跃值: (847)
能力值: ( LV8,RANK:150 )
在线值:
发帖
回帖
粉丝
14
说明白点吧??想知道原因
2010-12-30 17:57
0
雪    币: 239
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
InLine Hook(下面简称为Hook)本来就是一件危险的事情。

多核的时候,不管你旋不旋都没用,因为另一个核不一定跟着你旋。除非你能将别的核都强占了,很多人以为扔DPC过去就行了,其实没这么简单,主要看你要Hook的是什么代码,普通函数一般没问题,如果你要Hook的是中断,那么你危险了,因为就算DPC也会被打断的,除非你屏蔽中断。幸好我们一般Hook的地方都在PASSIVE_LEVEL执行的。下面说的Hook都是关于PASSIVE_LEVEL代码的,别的就不说了

先说说为什么Hook很危险,因为就算是常用的5字节Hook,一般也无法用一条指令完成修改动作,如果这时候别的核调用该函数的时候你正好修改了一半,那么微软会很高兴的送上你一盒蓝瓶的钙。这里正确的做法一定要给别的核送点DISPATCH_LEVEL级别的代码执行,以确保你要操作的函数不被调用。遗憾的是目前几乎没人会在Hook的时候做这个事情。

现在再说说恢复后执行的问题,还是老样子,多核,你将代码恢复了,不说恢复到一半出错的问题,就算你恢复好了,你什么时候再去Hook呢?一般都是等你本次调用这个函数返回了再去Hook,那么这时候别的核调用该函数你就没机会拦截了,这跟Hook的初衷是背道而驰的。

再说说你Hook了后别人再Hook的情况吧,恢复后执行可能更危险了,具体什么就不说了,很多人应该都能想到。

最后再说说5字节这种Hook的另一种弊端,就是不是什么代码你都能用这种方法去Hook的。
因为我们不希望去恢复代码,那么被替换的原始代码就需要复制到新位置执行,因为执行的地址变了,所以很多代码我们其实是需要修正的,比如call ****,jz/jnz等,其机器码中都是保存的相对偏移,所以如果要正确执行,那么就需要去修正这些值。但是这里些郁闷的代码是无法修正的,比如74/75这种两字节偏移的,因为新旧代码地址的差值往往很大。

这种无法修正的指令也就不说了,下面再说一种无法这样Hook的代码,就是有代码会跳转到你修改5字节中间的,为什么就不用再说了,稍微动动脑子的都知道,所幸的是微软的代码几乎没有。

说了这么多,也就是为了告诉大家,Hook不是万能的,不是哪儿都好用的,如果可以,请尽量使用指针替换方式的Hook,相对来说稳定性和兼容性会好一些,比如IAT Hook和SSDT Hook。当然,这种方式可能并不能满足你的需要。

那么有没有可能去克服上面说的这些问题呢?鄙人愚钝,只知道如果用单字节Hook就没这些问题,那么单字节能产生跳转的,并且Ring 0/Ring3通用的好像也就一个CC了。不过这玩意儿又要扯到IDT,也很麻烦。哪位要有好的点子请跟帖发表一下。

说了这么多废话,只是为了给大家理一下Hook中可能会遇到的一些问题。顺便说一下,其实我也在用这种并不完美的东西,让大家见笑了。呵呵
2010-12-30 23:50
0
雪    币: 4817
活跃值: (23)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
LS高见啊,学习了
2010-12-31 09:01
0
雪    币: 1157
活跃值: (847)
能力值: ( LV8,RANK:150 )
在线值:
发帖
回帖
粉丝
17
感谢总结...
2010-12-31 09:33
0
游客
登录 | 注册 方可回帖
返回
//