签到

05月06日
尚未签到

共有回帖数 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是一个很重要的函数,如果把控制权抢过来,系统会瘫痪,那只有重启了。
    }
    }




    既然写到这里了。那就还原一个xp sp3下的IoCallDriver吧。书上给的是vista下的代码。那我们就直接来xp下的代码学习学习。看看IoCallDriver是怎么运行的。
    首先要知道irp结构
    typedef struct _IRP{
    CSHORT Type;
    USHORT Size;
    PMDL MdlAddress;
    ULONG Flags;
    union {
    struct _IRP *MasterIrp;
    __volatile LONG IrpCount;
    PVOID SystemBuffer;
    } AssociatedIrp;
    LIST_ENTRY ThreadListEntry;
    IO_STATUS_BLOCK IoStatus;
    KPROCESSOR_MODE RequestorMode;
    BOOLEAN PendingReturned;
    CHAR StackCount;
    CHAR CurrentLocation;
    BOOLEAN Cancel;
    KIRQL CancelIrql;
    CCHAR ApcEnvironment;
    UCHAR AllocationFlags;
    PIO_STATUS_BLOCK UserIosb;
    PKEVENT UserEvent;
    union {
    struct {
    union {
    PIO_APC_ROUTINE UserApcRoutine;
    PVOID IssuingProcess;
    };
    PVOID UserApcContext;
    } AsynchronousParameters;
    LARGE_INTEGER AllocationSize;
    } Overlay;
    __volatile PDRIVER_CANCEL CancelRoutine;
    PVOID UserBuffer;
    union {
    struct {
    union {
    KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
    struct {
    PVOID DriverContext[4];
    };//0x10字节
    };
    PETHREAD Thread;//0x4字节
    PCHAR AuxiliaryBuffer;//0x4
    struct {
    LIST_ENTRY ListEntry;//0x8
    union {
    struct _IO_STACK_LOCATION *CurrentStackLocation;//此处相对于Tail偏移地址为0x20
    ULONG PacketType;
    }h1;
    };
    PFILE_OBJECT OriginalFileObject;
    } Overlay;
    KAPC Apc;
    PVOID CompletionKey;
    } Tail;
    } IRP,*FIRP;
    在windbg下只能看到Tail字段的偏移地址是0x40,但是看不到Tail的内容。所以里面的字段要自己推算。我已经为大家推算好了。因为IoCallDriver要用到0x60处的字段。
    由于上面说了,CurrentStackLocation相对于Tail是0x20,那么加上Tail相对于结构头的偏移地址0x40,那CurrentStackLocation便是0x60了。
    来看看汇编代码和注释:
    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]//irp,edx
    804f04fa 8b4d08 mov ecx,dword ptr [ebp+8]//deviceobject,ecx
    804f04fd ff1500d25480 call dword ptr [nt!pIofCallDriver (8054d200)]
    804f0503 5d pop ebp
    804f0504 c20800 ret 8
    IopfCallDriver:
    nt!IopfCallDriver:
    804ef0e8 fe4a23 dec byte ptr [edx+23h]//CurrentLocation--
    804ef0eb 8a4223 mov al,byte ptr [edx+23h]
    804ef0ee 84c0 test al,al//CurrentLocation是否为0
    804ef0f0 7f0e jg nt!IopfCallDriver+0x18 (804ef100)
    804ef0f2 6a00 push 0
    804ef0f4 6a00 push 0
    804ef0f6 6a00 push 0
    804ef0f8 52 push edx
    804ef0f9 6a35 push 35h
    804ef0fb e89aab0000 call nt!KeBugCheckEx (804f9c9a)//报错
    804ef100 8b4260 mov eax,dword ptr [edx+60h]//Tail.Overlay.CurrentStackLocation
    804ef103 83e824 sub eax,24h//IO_STACK_LOCATION为0x24字节
    804ef106 56 push esi//--Tail.Overlay.CurrentStackLocation
    804ef107 894260 mov dword ptr [edx+60h],eax//irpStack(PIO_STACK_LOCATION)
    804ef10a 894814 mov dword ptr [eax+14h],ecx//irpStack-DeviceObject=deviceobject
    804ef10d 0fb600 movzx eax,byte ptr [eax]//irpStack-MajorFunction
    804ef110 8b7108 mov esi,dword ptr [ecx+8]//deviceobject-DriverObject
    804ef113 52 push edx
    804ef114 51 push ecx
    804ef115 ff548638 call dword ptr [esi+eax*4+38h]//deviceobject-DriverObject-MajorFuntion[eax]
    804ef119 5e pop esi
    804ef11a c3 ret//返回到调用函数
    那么还原成C语言:
    NTSTATUS _IoCallDriver(PDEVICE_OBJECT DeviceObject, PIRP irp)
    {
    PIO_STACK_LOCATION irpStack;
    if (--irp-CurrentLocation == 0)
    KeBugCheckEx(0x35, (ULONG_PTR)irp, 0, 0, 0);
    else
    {
    irpStack=irp-Tail.Overlay.CurrentStackLocation--;//指针减少1,这个IO_STACK_LOCATION结构是0x24大小
    irpStack-DeviceObject = DeviceObject;
    }
    return DeviceObject-DriverObject-MajorFunction[irpStack-MajorFunction](DeviceObject, irp);
    }

    楼主 2015-10-23 13:30 回复

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

登录直线网账号

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