zoukankan      html  css  js  c++  java
  • 写一篇Hook Driver.

    关于Hook,有一本书讲的比较清楚,最近刚刚看完,《Rootkits: Subverting the Windows Kernel》

    http://www.amazon.com/Rootkits-Subverting-Windows-Greg-Hoglund/dp/0321294319

    总结下来,Hook的方法有以下几种:

    1.修改各种table,IAT/IDT/SSDT

    涉及到函数调用的原理,OS,每个bin文件都会维护各种Table,函数调用,系统调用,中断响应都会reference这些table来确定跳转的目的地址。

    所以,通过修改这些table的信息,可以达到一些hook的目的

    2.修改函数入口的汇编

    特别是win32,函数的入口前几个Byte的汇编都是蛮规律的,修改函数入口的几个Byte,用一个jump指令来替换,jump到hook的函数,执行完了再jump回来。

    这个办法是下面会详细讲到的。

    3.安装filter Driver.

    自己create一个driver,通过IoAttachDevice/IoDetachDevice来附加到对应的设备,以后每次发给设备的信息都会先送到我们的driver里面。

    会涉及到windows driver,IRP, IRP stack等知识。

    4.修改Kernel object。

    当我们进入kernel mode的时候,我们就可以修改很多kernel的结构,譬如process list。Processs list是一个LIST_ENTRY的双链表。

    通过回去当前的process,可以遍历到所有的process。

    对于每个Process的LIST_ENRTY,我们可以修改对应的FLINK,BLINK,来remove一个节点。

    所以那些通过遍历process LIST_ENTRY来遍历Process的办法,我们就可以隐藏相应的Process.

    /******** 下面会具体讨论Solution 2 *******/

    Mark:SetWindowsHookEx,好像可以修改文件的IAT表来Hook...

    这两天在想怎么port到x64上去,发现有一些的一些难点:

    1. compiler for X64 don't support inline assemble了,所以不能用汇编来修改a.写保护,b.添加jmp指令

    2. x64版本函数入栈的方式不像x86那么规律了,每个函数入口指令都不一样

    为了hook正常运行,需要保证在调回到函数执行时,ebp,esp,堆栈内容都要保持不变

    想到的办法:

    函数入口直接改写成call xxx,并保存原来的指令,在ret之前修改stack里面的返回地址为原函数入口,并且恢复 for NdisRegisterxxx

    么啥好办法yet...强制该汇编....

    Helps:

    windows提供一下函数_disable(), __writecr0(ULONG)等来替代一些原本需要汇编来做的操作。(注册表可以cancel写保护)

    单独写.asm文件,这个貌似没什么帮助

    =================

    原理蛮好理解的,就是替换了windows api函数入口的汇编代码,用一个5个byte的长跳转jmp指令跳转到自己写的函数,执行完之后再jmp回来。

    先来看一段windows x86函数入口前后的汇编指令(平台有别):

    (Ps: 5byte的nop指令一般都是compiler预留,可用于patch一个长跳转, move edi,edi: 2byte可替换成一个段跳转)

    -5: nop
    -4: nop
    -3: nop
    -2: nop
    -1: nop
    function:
    0000-0000: move edi, edi
    0000-0002: push ebp
    0000-0003: move ebp, esp
    0000-0005: sub esp, xxxx

    不过有几个地方我还是蛮困惑的:

    1.因为函数调用的时候有很多register是上下文相关的,compiler帮忙都配置好了用哪些register。怎么保证跳转过去的函数使用的register跟先前的能够匹配呢。

    [Answer]: function call的时候,会把需要保存的register保存到stack, Jmp过去之后,不从register里面拿信息,所以也不会影响。当然platform dependent,不同的调用规范行为不一样,这里讨论windows 32bit,linux函数调用的时候会把一些参数放在register里,以提高速度。

    2.hook函数执行完之后,在jmp回到先前函数+5的地方开始执行,这时候register context也可能都改变了啊。

    [Answer]:不依赖register的值,只要stack没有破坏,ebp没被改坏就没事,一般都是ebp+xx, ebp-xx来拿参数的,其实就算esp变了也不要紧,函数结束一般都有mov esp, ebp 恢复esp的值。

    3.先前函数那5个byte对应的指令怎么办,略过了?要是先前函数+5的地址不是一个完整的指令起始地址呢?

    [Answer]:本代码刚好在jmp到hook_func的时候,执行了同样的5byte指令,而且本代码hook的函数入口+5byte都刚好是完整的指令地址,这些都是tricky的地方,不通用。

    http://www.cnblogs.com/StudyRush/archive/2011/03/06/1966553.html 这篇文章对于函数调用写的比较清楚。

    粗略的先post一些code.

    VOID DisableIntChangeRight( PULONG pOldAttr){
        ULONG uAttr;
        _asm{
            cli;
            push eax;
            mov eax, cr0;
            mov uAttr, eax;
            and eax, 0FFFEFFFFh; // CR0 16 BIT = 0
            mov cr0, eax;
            pop eax;
        };
        *pOldAttr = uAttr;
    }
    VOID EnableIntRestoreRight( ULONG uOldAttr ){
        _asm{
            push eax;
            mov eax, uOldAttr;
            mov cr0, eax;
            pop eax;
            sti;
        };
    }
    VOID InterceptFunction( PDEVICE_EXTENSION deviceExtension, HOOKINDEX Index, PVOID pOriginalFunc, PVOID pDestFunc){
        UINT uAttrib;
        UINT OffsetJump;
        UCHAR JumpCode[] = { 0xe9, 0x01, 0x02, 0x03, 0x04}; // Code inserted to pOriginalFunc to jump to pDestFunc
        PCHAR pString = NULL;
    
        if (deviceExtension->CodeHooked[Index] == TRUE){
            DebugPrint((ERROR, "Function %s already hooked.
    ", pString));
            return;
        }else {
            DebugPrint((INFO, "Function %s hooked.
    ", pString));
            DisableIntChangeRight(&uAttrib);
    
    // First backup to be overwritten code. RtlCopyMemory((PCHAR)(deviceExtension->BackupCode[Index]), (PCHAR)pOriginalFunc,
    sizeof(deviceExtension->BackupCode[Index])); // Second calculate jmp pDestFunc and overwrite OffsetJump = (UINT)pDestFunc - (UINT)pOriginalFunc - 5; *(PUINT)&JumpCode[1] = OffsetJump; RtlCopyMemory((PCHAR)pOriginalFunc, JumpCode, sizeof(JumpCode)); deviceExtension->BackAddress[Index] = (ULONG_PTR)pOriginalFunc + 5; deviceExtension->CodeHooked[Index] = TRUE; EnableIntRestoreRight(uAttrib); } return; } VOID RestoreFunction( PDEVICE_EXTENSION deviceExtension, HOOKINDEX Index){ UINT uAttrib; PCHAR pString = NULL; if (deviceExtension->CodeHooked[Index]){ DebugPrint((TRACE, "Function %s restored. ", pString)); DisableIntChangeRight(&uAttrib); deviceExtension->CodeHooked[Index] = FALSE; RtlCopyMemory((PCHAR)(deviceExtension->BackAddress[Index] - 5),(PCHAR)(deviceExtension->BackupCode[Index]), sizeof(deviceExtension->BackupCode[Index])); EnableIntRestoreRight(uAttrib); }else { DebugPrint((TRACE, "Function %s is not hooked at all. ", pString)); } return; }

      

  • 相关阅读:
    Eureka Server的多级缓存和过期机制
    eureka-client拉取注册表
    Ribbon的调用流程
    EurekaServer启动
    eureka的注册
    Eureka的客户端是怎么启动的?
    Ribbon的负载均衡源码
    Ribbon是怎么重构URL的?
    Maven添加本地jar
    window 常用软件记录
  • 原文地址:https://www.cnblogs.com/zzSoftware/p/3285894.html
Copyright © 2011-2022 走看看