zoukankan      html  css  js  c++  java
  • 09SSDT概述


    SSDT概述

    通过08内核编程HOOK_KiFastCall.md可以知道,用户层的函数调用都会进入到0环, 0环将服务函数的地址实现保存在SSDT表中. KiFastCallEntry函数会使用调用号找到函数的参数个数表和函数地址表, 并将用户栈的参数拷贝到内核栈,最后调用了系统服务表中的函数.

    SSDT HOOK的原理很简单: 找到SSDT,将对应的函数地址进行替换,就完成HOOK了.

    在进行HOOK的过程中,唯一需要注意的是: SSDT表是不可写的, 强行写入会产生内存访问异常, 但是也有方法使其变成可写:

    1. 关闭CR0寄存器中的WP位 这个位是用于控制是否开启页保护的, 当其被置1, CPU就会做写入检查, 当其置0,就不做检查.(02_寄存器.md)

    2. 使用MDL重新映射SSDT表的元素,这样也可以进行写入(04内核编程内核新概念.md)

    HOOK 代码

    #include <ntddk.h>

    typedef struct _KSYSTEM_SERVICE_TABLE
    {
       PULONG ServiceTableBase;   //函数地址表的首地址
       PULONG ServiceCounterTableBase;// 函数表中每个函数被调用的次数
       ULONG   NumberOfService;// 服务函数的个数, NumberOfService * 4 就是整个地址表的大小
       UCHAR*   ParamTableBase; // 参数个数表首地址
    } KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;

    typedef struct _KSERVICE_TABLE_DESCRIPTOR
    {
       KSYSTEM_SERVICE_TABLE   ntoskrnl;// ntoskrnl.exe的服务函数,即SSDT
       KSYSTEM_SERVICE_TABLE   win32k; // win32k.sys的服务函数(GDI32.dll/User32.dll 的内核支持),即ShadowSSDT
       KSYSTEM_SERVICE_TABLE   notUsed1; // 不使用
       KSYSTEM_SERVICE_TABLE   notUsed2; // 不使用
    }KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
    typedef NTSTATUS(NTAPI*FnZwOpenProcess)(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId);



    void OnUnLoad(DRIVER_OBJECT* driver);

    void installHookSSDT();
    void uninstallHook();
    void disablePageWriteProtect();
    void enablePageWriteProtect();
    NTSTATUS NTAPI MyZwOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId);


    FnZwOpenProcess g_oldZwOpenProcess; // 原始ZwOpenProcess函数
    ULONG           g_uPid; // 需要保护的进程ID, 这个PID可以通过内核通讯来修改.
    KSERVICE_TABLE_DESCRIPTOR* g_pServiceTable = NULL;

    NTSTATUS DriverEntry(DRIVER_OBJECT* driver, UNICODE_STRING* path)
    {

       DbgBreakPoint();
       driver->DriverUnload = OnUnLoad;

       // 安装HOOK
       installHookSSDT();

       return STATUS_SUCCESS;
    }

    void OnUnLoad(DRIVER_OBJECT* driver)
    {
       // 卸载HOOK
       uninstallHook();
    }

    void installHookSSDT()
    {
       // 1. 找到SSDT表的首地址
       // 1.1 在分析KiFastCallEntry时, 可以看到系统从KPCR中取出了当前线程对象
       //     然后又从当前线程对象(KTHREAD)中取出了ServiceTable.因此,可以仿照
       //     这个做法.
       // 1.2 ServiceTable在KTHREAD的以下偏移:
       //       +0x0bc ServiceTable     : Ptr32 Void
       PETHREAD* pCurThread = PsGetCurrentThread();

       g_pServiceTable = (KSERVICE_TABLE_DESCRIPTOR*)
           (*(ULONG*)((ULONG_PTR)pCurThread + 0xBC));

       // 2. 找到函数在表中的位置(其位置就是调用号.)
       // 2.1 保存旧的函数地址(0xBE是ZwOpenProcess函数)
       g_oldZwOpenProcess =
           (FnZwOpenProcess)g_pServiceTable->ntoskrnl.ServiceTableBase[0xBE];

       // 3. 将内存分页设置为可写
       disablePageWriteProtect();
       // 4. 写入新函数地址到SSDT表中
       g_pServiceTable->ntoskrnl.ServiceTableBase[0xBE] = (PULONG)MyZwOpenProcess;
       // 5. 将内存分页属性恢复不可写
       enablePageWriteProtect();
    }

    void uninstallHook()
    {
       if (g_oldZwOpenProcess)
       {
           // 1. 将内存分页设置为可写
           disablePageWriteProtect();
           // 2. 写入新函数地址到SSDT表中
           g_pServiceTable->ntoskrnl.ServiceTableBase[0xBE] = (PULONG)g_oldZwOpenProcess;
           // 3. 将内存分页属性恢复不可写
           enablePageWriteProtect();
       }
    }

    NTSTATUS NTAPI MyZwOpenProcess(PHANDLE ProcessHandle,
                                   ACCESS_MASK DesiredAccess,
                                   POBJECT_ATTRIBUTES ObjectAttributes,
                                   PCLIENT_ID ClientId)
    {
       if (ClientId->UniqueProcess == g_uPid)
       {
           DesiredAccess = 0; // 将访问权限置零
       }
       // 调用原始函数
       return g_oldZwOpenProcess(ProcessHandle,
                                 DesiredAccess,
                                 ObjectAttributes,
                                 ClientId);
    }

    // 关闭内存页写入保护
    void _declspec(naked) disablePageWriteProtect()
    {
       _asm
       {
           push eax;
           mov eax, cr0;
           and eax, ~0x10000;
           mov cr0, eax;
           pop eax;
           ret;
       }
    }

    // 开启内存页写入保护
    void _declspec(naked) enablePageWriteProtect()
    {
       _asm
       {
           push eax;
           mov eax, cr0;
           or eax, 0x10000;
           mov cr0, eax;
           pop eax;
           ret;
       }
    }
  • 相关阅读:
    【codecombat】 试玩全攻略 第二章 边远地区的森林 一步错
    【codecombat】 试玩全攻略 第十八关 最后的kithman族
    【codecombat】 试玩全攻略 第二章 边远地区的森林 woodlang cubbies
    【codecombat】 试玩全攻略 第二章 边远地区的森林 羊肠小道
    【codecombat】 试玩全攻略 第十七关 混乱的梦境
    【codecombat】 试玩全攻略 第二章 边远地区的森林 林中的死亡回避
    【codecombat】 试玩全攻略 特别关:kithguard斗殴
    【codecombat】 试玩全攻略 第二章 边远地区的森林 森林保卫战
    【codecombat】 试玩全攻略 第二章 边远地区的森林
    实验3 类和对象||
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11439441.html
Copyright © 2011-2022 走看看