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; }

      

  • 相关阅读:
    SCI写作经典替换词,瞬间高大上!(转)
    最佳化常用测试函数 Optimization Test functions
    算法复杂度速查表
    VS 代码行统计
    CPLEX IDE 菜单栏语言设置( 中文 英文 韩文 等多国语言 设置)
    如何从PDF文件中提取矢量图
    Matlab无法打开M文件的错误( Undefined function or method 'uiopen' for input arguments of type 'char)
    visual studio 资源视图 空白 解决方案
    MFC DialogBar 按钮灰色不响应
    嗨翻C语言笔记(二)
  • 原文地址:https://www.cnblogs.com/zzSoftware/p/3285894.html
Copyright © 2011-2022 走看看