zoukankan      html  css  js  c++  java
  • Inlinehook PspCreateProcess

    InineHook通过修改函数指令实现,此次以内核层的PspCreateProcess()为例。

     本来是想写NtCreateProcess()的Inlinehook,但是想到PCHunter对于SSDT和ShadowSSDT的检测,就想试一下PCHunter对于更底层函数的检测功能,虽然最后还是被检测出来了 /桑心。通过查看wrk的源代码,NtCreateprocess() 紧接着调用了NtCreateProcessEx(),在NtCreateProcessEx()函数中又调用了

    PspCreateProcess() ,于是这次打算Inlinehook掉PspCreateProcess()函数。

     1 NTSTATUS
     2 NtCreateProcess(
     3     __out PHANDLE ProcessHandle,
     4     __in ACCESS_MASK DesiredAccess,
     5     __in_opt POBJECT_ATTRIBUTES ObjectAttributes,
     6     __in HANDLE ParentProcess,
     7     __in BOOLEAN InheritObjectTable,
     8     __in_opt HANDLE SectionHandle,
     9     __in_opt HANDLE DebugPort,
    10     __in_opt HANDLE ExceptionPort
    11     )
    12 {
    13   
    14 //……
    15     return NtCreateProcessEx (ProcessHandle,
    16                               DesiredAccess,
    17                               ObjectAttributes OPTIONAL,
    18                               ParentProcess,
    19                               Flags,
    20                               SectionHandle,
    21                               DebugPort,
    22                               ExceptionPort,
    23                               0);
    24 }
    25 
    26 NTSTATUS
    27 NtCreateProcessEx(
    28     __out PHANDLE ProcessHandle,
    29     __in ACCESS_MASK DesiredAccess,
    30     __in_opt POBJECT_ATTRIBUTES ObjectAttributes,
    31     __in HANDLE ParentProcess,
    32     __in ULONG Flags,
    33     __in_opt HANDLE SectionHandle,
    34     __in_opt HANDLE DebugPort,
    35     __in_opt HANDLE ExceptionPort,
    36     __in ULONG JobMemberLevel
    37     )
    38 {
    39     //摘自wrk调用PspCreateProcess()的代码
    40     if (ARGUMENT_PRESENT (ParentProcess)) {
    41         Status = PspCreateProcess (ProcessHandle,
    42                                    DesiredAccess,
    43                                    ObjectAttributes,
    44                                    ParentProcess,
    45                                    Flags,
    46                                    SectionHandle,
    47                                    DebugPort,
    48                                    ExceptionPort,
    49                                    JobMemberLevel);
    50     } else {
    51         Status = STATUS_INVALID_PARAMETER;
    52     }
    53 
    54     return Status;
    55 }

    对于PspCreateProcess()的函数地址在ntoskrnl.exe中并未导出,所以对于PspCreateProcess()函数地址的获得采用匹配特征码的操作。

    //我们自己构建的特征码结构体
    typedef struct _SIGNATURE_INFO{
        UCHAR    cSingature;
        int        Offset;
    }SIGNATURE_INFO,*PSIGNATURE_INFO;

    我们在Windbg中反汇编NtCreateProcess()函数

     

    E8指令表示call 后面的c628e9ff表示的是一个相对偏移地址,是相对下一条指令地址的偏移,即我们图中的840ddea9 

    我们可以构建如下的特征码

    SIGNATURE_INFO SignCode[5] = {{0xff,9},{0x55,5},{0x1c,4},{0x14,1},{0xE8,0}};

    现在就出现了另一个问题,就是搜索特征码的起始地址,我们应该从哪开始匹配特征码,最初我用的是从NtCreateProcess()函数的地址开始搜索,可能有人又会问那NtCreateProcess()函数的地址怎么获得,现在先跳过这个问题,我在以后的博客中会讲解SSDT 与 shadowSSDT结构。但是后来考虑到代码的复用性,我用对PE文件的Section的执行块进行搜索。如果对于PE文件结构不清楚,可以参考《加密与解密》中的”PE文件格式”章节

     1 //需包含<ntimage.h>
     2 ULONG SearchAddressForSign(ULONG uStartBase,ULONG uSearchLength,SIGNATURE_INFO SignatureInfo[5])
     3 {
     4 /*
     5 uStartBase: 搜索开始的地址(PE文件的加载基地址)
     6 uSearchLength: 搜索的长度,一般采用SizeOfImage
     7 SignatureInfo:  构建的特征码数组
     8 返回匹配特征码的地址
     9 */
    10     UCHAR *p;
    11     ULONG u_index1,u_index2;
    12     PIMAGE_DOS_HEADER pimage_dos_header;
    13     PIMAGE_NT_HEADERS pimage_nt_header;
    14     PIMAGE_SECTION_HEADER pimage_section_header;
    15 
    16     if(!MmIsAddressValid((PVOID)uStartBase))
    17     {    return 0;    }
    18 
    19     pimage_dos_header = (PIMAGE_DOS_HEADER)uStartBase;
    20     pimage_nt_header = (PIMAGE_NT_HEADERS)((ULONG)uStartBase+pimage_dos_header->e_lfanew);
    21     pimage_section_header = (PIMAGE_SECTION_HEADER)((ULONG)pimage_nt_header+sizeof(IMAGE_NT_HEADERS));
    22 
    23     for (u_index1 = 0;u_index1<pimage_nt_header->FileHeader.NumberOfSections;u_index1++)
    24     {
    25 //#define IMAGE_SCN_MEM_EXECUTE                0x20000000  // Section is executable.
    26 //#define IMAGE_SCN_MEM_READ                   0x40000000  // Section is readable.
    27 //#define IMAGE_SCN_MEM_WRITE                  0x80000000  // Section is writeable.
    28         //0x60000000 = IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ
    29         if (pimage_section_header[u_index1].Characteristics&0x60000000)
    30         {
    31             //可读可执行的段
    32             p = (UCHAR*)uStartBase + pimage_section_header[u_index1].VirtualAddress;
    33             for (u_index2 = 0;u_index2<pimage_section_header[u_index1].Misc.VirtualSize;u_index2++)
    34             {
    35                 if (!MmIsAddressValid((p-SignatureInfo[0].Offset))||
    36                     !MmIsAddressValid((p-SignatureInfo[4].Offset)))
    37                 {
    38                     p++;
    39                     continue;
    40                 }
    41                 __try{
    42                     if (*(p-SignatureInfo[0].Offset)==SignatureInfo[0].cSingature&&
    43                         *(p-SignatureInfo[1].Offset)==SignatureInfo[1].cSingature&&
    44                         *(p-SignatureInfo[2].Offset)==SignatureInfo[2].cSingature&&
    45                         *(p-SignatureInfo[3].Offset)==SignatureInfo[3].cSingature&&
    46                         *(p-SignatureInfo[4].Offset)==SignatureInfo[4].cSingature)
    47                     {
    48                         return (ULONG)p;
    49                     }
    50 
    51                 }__except(EXCEPTION_EXECUTE_HANDLER){
    52                     DbgPrint("Search error!
    ");
    53                 }
    54                 p++;
    55             }
    56         }
    57     }
    58 
    59     return 0;
    60 }

    这里,我们得到了NtCreateProcess()函数中调用PspCreateProcess()函数的指令的地址,在我们这里就是840eadd4 。

    //这里的g_PspCreateProcessAddress就是我们这条指令的地址
    //840ddea4 e8  c628e9ff      call    nt!PspCreateProcess (83f7076f)
    //注意这里的*(LONG*)(g_PspCreateProcessAddress+1) 取出的c628e9ff是相对于下一条指令的偏移,
    //
    所以还需”+5”跳过我们自己的这条指令的5个字节,//就得到了PspCreateProcess()函数的绝对地址。 g_PspCreateProcessAddress = g_PspCreateProcessAddress + *(LONG*)(g_PspCreateProcessAddress + 1) + 5;

    得到PspCreateProcess()函数的绝对地址以后,就可以进行Inlinehook,将执行流程跳转到我们自己的函数中执行。

    反汇编PspCreateProcess

    我们接下来要做的就是将6878010000这5个字节换成我们自己构建的shellcode,跳转到我们自己的函数执行,大致代码如下

    ULONG       jmp_offset;
    UCHAR    jmp_code[5] = {0xE9};    //jmp指令
    //保存原始函数的5个字节,便于恢复
    RtlCopyMemory(Ori_Code,(PVOID)Destination,5);
    jmp_offset = Source - Destination-5;
    *(ULONG*)&jmp_code[1] = jmp_offset;
    //Destination就是g_PspCreateProcessAddress即PspCreateProcess()的函数地址
    RtlCopyMemory((PVOID)Destination,jmp_code,5);

    可以看到PspCreateProcess函数的前5个字节已经被我们Inlinehook,跳转到我们自己的函数NewPspCreateProcess() 函数了。

    接下来重点就是对我们自己的函数NewPspCreateProcess()的实现。

    函数声明如下

    // __declspec(naked)用来告诉编译器函数代码的汇编语言为自己的所写,不需要编译器添加任何汇编代码
    VOID __declspec(naked) NewPspCreateProcess();
    VOID __declspec(naked) NewPspCreateProcess()
    {
        __asm
        {
            //处理栈
            mov        edi,edi
            push       ebp
            mov        ebp,esp
    
            pushad    //保存我们各个寄存器的值
            pushfd    //保存标志寄存器的值
    
    //         8411ae89 ff7528          push    dword ptr [ebp+28h]
    //         8411ae8c ff7524          push    dword ptr [ebp+24h]
    //         8411ae8f ff7520          push    dword ptr [ebp+20h]
    //         8411ae92 ff7518          push    dword ptr [ebp+18h]
    //         8411ae95 ff7510          push    dword ptr [ebp+10h]
    //         8411ae98 ff750c          push    dword ptr [ebp+0Ch]
    //         8411ae9b ff7508          push    dword ptr [ebp+8]
    //         8411ae9e 8b551c          mov     edx,dword ptr [ebp+1Ch]
    //         8411aea1 8b4d14          mov     ecx,dword ptr [ebp+14h]
    //         8411aea4 e8c628e9ff      call    nt!PspCreateProcess (83fad76f)
    //         PspCreateProcess函数的参数已经入栈
    
            call  FilterPspCreateProcess  //调用我们想要进行操作的函数
    
            popfd
            popad
    push
    0x178 //执行被我们改写了的PspCreateProcess函数的前5个字节的指令
    jmp g_JmpOrignPspCreateProcessAddr //跳转到原来的PspCreateProcess函数的第6个字节的地址开始继续执行 } }

    接下来就是完成FilterPspCreateProcess() 函数,对PspCreateProcess()函数进行一次过滤,通过查看wrk,找到PspCreateProcess()的函数原型

     

    NTSTATUS
    PspCreateProcess(
        OUT PHANDLE ProcessHandle,
        IN ACCESS_MASK DesiredAccess,     //ULONG
        IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
        IN HANDLE ParentProcess OPTIONAL,
        IN ULONG Flags,
        IN HANDLE SectionHandle OPTIONAL,
        IN HANDLE DebugPort OPTIONAL,
        IN HANDLE ExceptionPort OPTIONAL,
        IN ULONG JobMemberLevel
    );
    //我们仿照PspCreateProcess()函数声明构建过滤函数
    ULONG_PTR  
        FilterPspCreateProcess(
        OUT PHANDLE ProcessHandle,   //0x8
        IN ACCESS_MASK DesiredAccess,//0xc
        IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,//0x10
        IN HANDLE ParentProcess OPTIONAL, //0x14
        IN ULONG  Flags,    //0x18
        IN HANDLE SectionHandle OPTIONAL,  //0x1c
        IN HANDLE DebugPort OPTIONAL,   //0x20
        IN HANDLE ExceptionPort OPTIONAL,   //0x24
        IN ULONG JobMemberLevel     //0x28
        )
    {
    
        //我这里什么都不做,只打印一句话,也可以进行一些过滤操作
        DbgPrint("FilterPspCreateProcess
    ");
    
        return 1;
    }

     

    到此,我们的Inlinehook完成,当然还有恢复的工作,在我们的驱动卸载时对我们Inlinehook的修改代码的地方进行恢复就行了,将我们保存的原来的字节恢复就好了。

    我这里只写了比较重要的地方,不太重要的地方的也没有过多的提及,比如关于关闭内核内存保护的问题,我在接下来的博客中会讲解到。

    最后附上完整代码,编译环境采用的是VS2010+DDK7600

    链接:http://pan.baidu.com/s/1gdfDsLP 密码:4y8m

  • 相关阅读:
    Manacher-模版题poj3974 hdu3068
    拓展kmp(带注释版)
    颓の第17周
    php 递归遍历目录带缩进
    php 递归遍历目录
    php session
    apache主机配置
    php环境配置的检测方法
    php 变量
    php MVC
  • 原文地址:https://www.cnblogs.com/lanrenxinxin/p/4508647.html
Copyright © 2011-2022 走看看