签到

06月21日
尚未签到

共有回帖数 0

    霜晨守候

    等级:
    此方法来自《从汇编语言到windows内核编程》,但此书给了思路和错误代码。会蓝屏,网上也没人解决。凡是解决了的,也不希望分享。楼楼会一步一步解释如果hook,然后以后通过这个方法,可以hook任何带函数符号的内核函数。
    以IoCallDriver为例子
    nt!IoCallDriver:
    804f04f2 8bff mov edi,edi
    804f04f4 55 push ebp
    804f04f5 8bec mov ebp,esp
    804f04f7 8b550c mov edx,dword ptr [ebp+0Ch]
    804f04fa 8b4d08 mov ecx,dword ptr [ebp+8]//很明显这里是两个参数,没有入栈
    804f04fd ff1500d25480 call dword ptr [nt!pIofCallDriver (8054d200)]
    804f0503 5d pop ebp
    804f0504 c20800 ret 8
    以上是IoCallDriver函数的汇编代码
    看来调用了一个pIofCallDriver函数指针,那我们跟进去看看。
    由于这个是一个指针,所以先要看指针内容
    kd dd 8054d200 l1
    8054d200 804ef0e8
    好了,这里的804ef0e8地址中有真正的函数入口
    nt!IopfCallDriver:
    804ef0e8 fe4a23 dec byte ptr [edx+23h]//这里是IRP结构中0x23处的内容,自己查看IRP结构就可以知道是什么了。
    804ef0eb 8a4223 mov al,byte ptr [edx+23h]
    804ef0ee 84c0 test al,al
    804ef0f0 7f0e jg nt!IopfCallDriver+0x18 (804ef100)//这里才是真正的函数入口地址,有兴趣可以进去看看,自己还原代码,很简单。
    ..
    从上面看。
    804f04fd ff1500d25480 call dword ptr [nt!pIofCallDriver (8054d200)]这条指令是调用了函数指针,那么我们直接hook函数指针就可以了。那函数指针一般放哪里呢?
    当然是一个函数表中,这里先不将这个函数表的获取方法。直接告诉你IoCallDriver函数对应了一个函数符号IofCallDriver。那现在看看这个符号下是什么内容。
    nt!IofCallDriver:
    804ef120 ff2500d25480 jmp dword ptr [nt!pIofCallDriver (8054d200)]
    哈哈。居然是一句简单的jmp指令。看看前面的。
    kd dd 8054d200 l1
    8054d200 804ef0e8
    可以发现。调用IoCallDriver函数时,里面的 call dword ptr [nt!pIofCallDriver (8054d200)]其实就是调用了IofCallDriver这个函数符号中jmp后面的函数指针。
    那么我们直接hook IofCallDriver这个函数符号后面的函数指针就可以了。这样以后每个程序调用IoCallDriver,都会调用我们的函数。
    nt!IofCallDriver:
    804ef120 ff2500d25480 jmp dword ptr [nt!pIofCallDriver (8054d200)]
    针对这条指令,ff占用两个字节,分别是机器码的前缀和操作码。这里不是我们要hook的。那后面的地址就是我们要hook的。
    正确的hook源代码是:
    IoCallDriverType HookIoCallDriver(PWCHAR Symbol, bool fHook)
    {
    UNICODE_STRING uniSymbol;
    RtlInitUnicodeString(&uniSymbol, Symbol);
    static PVOID tempFunc=NULL;
    if (Symbol == NULL) return NULL;
    //获取导出函数符号地址
    PCHAR address = (PCHAR)MmGetSystemRoutineAddress(&uniSymbol);
    if (fHook)
    {
    OldIoCallDriver = (IoCallDriverType)(*PLONG(address + 2));//获取入口指针(非真实地址)
    tempFunc = (PVOID)NewIoCallDriver;//模仿旧函数入口方式,这里很重要
    WPOFF();//写保护关
    InterlockedExchange(PLONG(address + 2), (LONG)&tempFunc);
    WPON();
    }
    else
    {
    if (OldIoCallDriver)
    {
    WPOFF();//这里必须关闭写保护,不然会蓝屏
    InterlockedExchange(PLONG(address + 2), (LONG)OldIoCallDriver);
    WPON();
    }
    else
    return NULL;
    }
    return OldIoCallDriver;//返回函数指针
    }
    下面是关闭和开启写保护的代码
    void WPOFF()
    {
    __asm
    {
    push eax
    mov eax, cr0
    mov g_Cr0, eax;//保存原来cr0,自己定义一个ULONG的全局变量
    and eax, not 10000h//第16位是控制写保护的
    mov cr0, eax
    pop eax
    cli
    }
    }
    void WPON()
    {
    __asm
    {
    sti
    push eax
    mov eax, g_Cr0
    mov cr0, eax//开启写保护
    pop eax
    }
    }
    重点在于新函数结构,由于是hook的函数符号表下的函数指针,那么参数在此之前已经被设置好了。所以直接用一个naked函数就可以了(采用c语言和汇编的混编就简单)
    __declspec(naked) void NewIoCallDriver(void)
    {
    __asm
    {
    push edx
    push ecx
    }//保存入口参数,这里很重要,不要让入口参数被破坏了
    //这里你想干什么就干什么
    DbgPrint("IofCallDriver is hookedn");//只是简单的提醒一下这个函数被hook了
    __asm
    {
    pop ecx
    pop edx
    }
    __asm
    {
    mov eax, OldIoCallDriver //获取函数指针
    jmp dword ptr[eax] //此函数最后调用ret,返回到调用IoCallDriver的例程
    //这个jmp之后,当前程序就失去对此函数的控制,之后控制权会由ret返回调用IoCallDriver的程序中。千万不要用call,因为IoCallDriver是一个很重要的函数,如果把控制权抢过来,系统会瘫痪,那只有重启了。
    }
    }

    楼主 2016-03-25 13:31 回复

共有回帖数 0
  • 回 帖
  • 表情 图片 视频
  • 发表

登录直线网账号

Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号 意见反馈 | 关于直线 | 版权声明 | 会员须知