• 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;
       }
    }
  • 相关阅读:
    2020软件工程作业04
    2020软件工程作业03
    2020软件工程作业02
    2020软件工程作业01
    Linux操作系统分析-课程学习总结报告
    结合中断上下文切换和进程上下文切换分析Linux内核的一般执行过程
    深入理解系统调用
    基于mykernel 2.0编写一个操作系统内核
    交互式多媒体图书平台的设计与实现
    码农放入自我修养之必备技能学习笔记
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11439441.html
Copyright © 2020-2023  润新知