SSDT—Hook和MDL

85 篇文章 6 订阅
11 篇文章 1 订阅

SSDT HOOK

首先要明白SSDTHOOK就是自己再写一份函数,替换了SSDT表中的地址替换掉
SystemServiceTable系统服务表

如何访问系统服务表呢?

SSDT 的全称是 System Services Descriptor Table,系统服务描述符表

下面这个是个全局变量
kd> dd KeServiceDescriptorTable(SSDT)

这个导出的 声明一下就可以使用了

kd> dd KeServiceDescriptorTableShadow(SSDT Shadow)

这个未导出 需要用其他的方式来查找

kd> dd KeServiceDescriptorTable
83fac9c0  83ec0d9c 00000000 00000191 83ec13e4
83fac9d0  00000000 00000000 00000000 00000000
83fac9e0  83f1f6af 00000000 025355a9 000000bb
83fac9f0  00000011 00000100 5385d2ba d717548f
83faca00  83ec0d9c 00000000 00000191 83ec13e4
83faca10  956d6000 00000000 00000339 956d702c
83faca20  00000000 00000000 83faca24 00000340
83faca30  00000340 865fab00 00000007 00000000

其中83ec0d9c是函数地址,00000191是函数个数,83ec13e4是函数参数个数,单位是一个字节,除以4就是函数地址表上对应函数的个数,第一个函数对第一个个数。

下面就是函数地址表

kd> dd 83ec0d9c
83ec0d9c  840bcc28 83f0340d 8404cb68 83e6788a
83ec0dac  840be4ff 83f403fa 8412eb05 8412eb4e
83ec0dbc  840413bd 84148368 841495c1 84037b95
83ec0dcc  840c8b35 84121963 84074a56 840446cc
83ec0ddc  83fda928 84113898 8402b14e 8406da62
83ec0dec  840b9df1 8401b238 840b91fe 84038c0c
83ec0dfc  840ca5bc 8403b28f 840ca39c 840c2afc
83ec0e0c  8404d0f0 8410e657 840bfec9 840ca7ee

根据这个定义一个结构体
//下面这个是系统服务表的结构体
typedef struct _KSYSTEM_SERVICE_TABLE  
{  
    PULONG  ServiceTableBase;                                       // 服务函数地址表基址  
    PULONG  ServiceCounterTableBase;                                
    ULONG   NumberOfService;                                        // 服务函数的个数  
    PULONG   ParamTableBase;                                        // 服务函数参数表基址   
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;  

下面就是函数参数个数表除四就是真正个数


kd> db 83ec13e4
83ec13e4  18 20 2c 2c 40 2c 40 44-0c 08 08 18 18 08 04 04  . ,,@,@D........
83ec13f4  0c 0c 10 18 24 0c 2c 0c-18 10 0c 0c 0c 0c 0c 0c  ....$.,.........
83ec1404  08 0c 18 18 14 18 0c 20-10 08 08 08 0c 08 0c 0c  ....... ........
83ec1414  08 04 04 0c 08 08 08 08-0c 04 04 20 08 10 0c 20  ........... ... 
83ec1424  14 0c 2c 10 0c 0c 1c 10-20 20 10 38 10 14 10 20  ..,.....  .8... 
83ec1434  24 24 28 1c 1c 14 10 20-2c 10 34 28 18 2c 14 28  $$(.... ,.4(.,.(
83ec1444  08 0c 08 04 04 04 04 04-0c 04 08 28 00 04 04 1c  ...........(....
83ec1454  18 00 08 08 18 0c 14 18-08 18 0c 08 08 0c 04 00  ................

如何得到函数表地址




//这个就是SSDT的结构体,即系统服务描述表,里面就是多个系统服务表
typedef struct _KSERVICE_TABLE_DESCRIPTOR  
{  
    KSYSTEM_SERVICE_TABLE   ntoskrnl;                       // ntoskrnl.exe 的服务函数  
    KSYSTEM_SERVICE_TABLE   win32k;                         // win32k.sys 的服务函数(GDI32.dll/User32.dll 的内核支持)  
    KSYSTEM_SERVICE_TABLE   notUsed1;  
    KSYSTEM_SERVICE_TABLE   notUsed2;  
}KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;  



//导出由 ntoskrnl(10-10-12)所导出的 SSDT
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;

//这个是导出的,要到内核文件找,所以名字不能瞎起

下面的代码是查看到SSDT表地址的代码,可以打印显示

#include <ntddk.h>
#include<ntstatus.h>

typedef struct _KSYSTEM_SERVICE_TABLE
{
    PULONG  ServiceTableBase;                                       // 服务函数地址表基址  
    PULONG  ServiceCounterTableBase;
    ULONG   NumberOfService;                                        // 服务函数的个数  
    PULONG   ParamTableBase;                                        // 服务函数参数表基址   
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;

typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
    KSYSTEM_SERVICE_TABLE   ntoskrnl;                       // ntoskrnl.exe 的服务函数  
    KSYSTEM_SERVICE_TABLE   win32k;                         // win32k.sys 的服务函数(GDI32.dll/User32.dll 的内核支持)  
    KSYSTEM_SERVICE_TABLE   notUsed1;
    KSYSTEM_SERVICE_TABLE   notUsed2;
}KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;


//导出由 ntoskrnl所导出的 SSDT
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;//这个是导出的,要到内核文件找,所以名字不能瞎起

VOID DriverUnload(PDRIVER_OBJECT pDriver) {
    UNREFERENCED_PARAMETER(pDriver);
    KdPrint(("My Dirver is unloading..."));
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPath) {
    UNREFERENCED_PARAMETER(pPath);
    KdPrint(("->%x \n", KeServiceDescriptorTable));
    pDriver->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
}

通过页表基址修改页属性

SSDT所在的物理页是只读的,如果要修改,先要修改页属性为可写:

1、第一种办法,用我们学过的知识,通过页表基址直接修改



if(RCR4 & 0x00000020)
{//说明是2-9-9-12分页
    KdPrint(("2-9-9-12分页 %p\n",RCR4));
    KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8))));
    *(DWORD64*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8)) |= 0x02; 
    KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 9) & 0x007FFFF8))));
}
else
{//说明是10-10-12分页
    KdPrint(("10-10-12分页\n"));
    KdPrint(("PTE1 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC))));
    *(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC)) |= 0x02;
    KdPrint(("PTE2 %p\n",*(DWORD*)(0xC0000000 + ((HookFunAddr >> 10) & 0x003FFFFC))));
}

通过修改CR0寄存器(WP位置改成0)

CR0寄存器

比如我们要SSDTHOOKNtOpenProcess,就要先通过IDA找到kernelBase的OpenProcess,在经过一系列查看最后在ntdll里找到ZWOpenProcess,所在位置看到了NtOpenProcess

.text:77F05D88                 mov     eax, 0BEh       ; NtOpenProcess
.text:77F05D8D                 mov     edx, 7FFE0300h
.text:77F05D92                 call    dword ptr [edx]
.text:77F05D94                 retn    10h
kd> dd KeServiceDescriptorTable
83f789c0  83e8cd9c 00000000 00000191 83e8d3e4
83f789d0  00000000 00000000 00000000 00000000
83f789e0  83eeb6af 00000000 025355a9 000000bb
83f789f0  00000011 00000100 5385d2ba d717548f
83f78a00  83e8cd9c 00000000 00000191 83e8d3e4
83f78a10  95d46000 00000000 00000339 95d4702c
83f78a20  00000000 00000000 83f78a24 00000340
83f78a30  00000340 865fab00 00000007 00000000
kd> dd 83e8cd9c+4*BE
83e8d094  840219dc 84073fff 84061b37 83f8d0c7
83e8d0a4  84079674 83ff50c6 84096977 8405db6f
83e8d0b4  8406dd87 840882e4 84061c4e 84119e0f
83e8d0c4  841026f1 84103989 83ff3506 84050970
83e8d0d4  841022a2 84101fc2 8410235a 8410207a
83e8d0e4  8400693f 83fd5f60 83ff0a51 841040e4
83e8d0f4  841041aa 84052403 840a35a7 840679a1
83e8d104  84114a3e 84114e83 83ed2d34 84086b8c
kd> u 840219dc 
nt!NtOpenProcess:
840219dc 8bff            mov     edi,edi
840219de 55              push    ebp
840219df 8bec            mov     ebp,esp
840219e1 51              push    ecx
840219e2 51              push    ecx
840219e3 64a124010000    mov     eax,dword ptr fs:[00000124h]
840219e9 8a803a010000    mov     al,byte ptr [eax+13Ah]
840219ef 8b4d14          mov     ecx,dword ptr [ebp+14h]

下面说一下通过MDL修改页属性。
就是 Memory Descriptor List,简称 MDL。有的同学可能会问了,MDL究竟是个什么东西呢?从字面意思看,不难理解,内存描述符列表。MDL包含了内存区域的起始、拥有者proc、字节数、标记等。OK,我们需要先定义一个MDL的指针。


PMDL MDLSystemCall;

定义了MDL的指针以后,我们要通过MAPPED系列的参数来使内存拥有可写性,然后锁定内存中的MDL,那么我们就要定义一个PVOID的指针,来供MmMap操作。

PVOID *MappedSCT;
MDLSystemCall = MmCreateMdl(
NULL, //内存描述符列表
KeServiceDescriptorTable.ServiceTableBase,//
内存地址
KeServiceDescriptorTable.NumberOfServices*4);//内存长度
if(!MDLSystemCall)
  return STATUS_UNSUCCESSFUL;

建立了MDL,填充一下页数组

MmBuildMdlForNonPagedPool(MDLSystemCall);//建立内存页的MDL描述   
MDLSystemCall->MdlFlags = MDLSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA; //设置MDL标记为可写
MappedSCT = MmMapLockedPages(MDLSystemCall, KernelMode);   

然后做自己的操作,比如这里的Hook

然后另外还有释放MDL的代码

if(MDSystemCall)
   {
      MmUnmapLockedPages(MappedSCT, MDSystemCall);
      IoFreeMdl(MDSystemCall);
   }

下面是SSDT-Hook代码

#include <ntddk.h>
#include<ntstatus.h>
//1.找到系统服务表的函数地址表
//定义一个全局变量用来存放之前的NtOpenProcess地址
ULONG uOldNtOpenProcess;
//有了地址还需要一个函数NtOpenProcess指针,用于调用原来的NtOpenProcess

//定义修复和恢复页属性的函数
PMDL MDSystemCall;
PVOID *MappedSCT;




typedef NTSTATUS(*NTOPENPROCESS)(
    __out PHANDLE  ProcessHandle,
    __in ACCESS_MASK  DesiredAccess,
    __in POBJECT_ATTRIBUTES  ObjectAttributes,
    __in_opt PCLIENT_ID  ClientId
    );

typedef struct _KSYSTEM_SERVICE_TABLE
{
    PULONG  ServiceTableBase;                                       // 服务函数地址表基址  
    PULONG  ServiceCounterTableBase;
    ULONG   NumberOfService;                                        // 服务函数的个数  
    PULONG   ParamTableBase;                                        // 服务函数参数表基址   
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;

typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
    KSYSTEM_SERVICE_TABLE   ntoskrnl;                       // ntoskrnl.exe 的服务函数  
    KSYSTEM_SERVICE_TABLE   win32k;                         // win32k.sys 的服务函数(GDI32.dll/User32.dll 的内核支持)  
    KSYSTEM_SERVICE_TABLE   notUsed1;
    KSYSTEM_SERVICE_TABLE   notUsed2;
}KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;


//导出由 ntoskrnl所导出的 SSDT
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;//这个是导出的,要到内核文件找,所以名字不能瞎起

//准备用于替换的函数
NTSTATUS NTAPI MyNtOpenProcess(__out PHANDLE  ProcessHandle,
    __in ACCESS_MASK  DesiredAccess,
    __in POBJECT_ATTRIBUTES  ObjectAttributes,
    __in_opt PCLIENT_ID  ClientId
)
{
    NTSTATUS Status;
    Status = STATUS_SUCCESS;
    //这里填自己的业务。。。各种过滤,修改返回结构等
    KdPrint(("MyNtOpenProcess %x %x %x %x \n", ProcessHandle, DesiredAccess, ObjectAttributes, ClientId));
    //后面这里填的是打开原来的函数,因为这个函数也要实现原来的功能,不然就乱套了,除非你自己在自己业务里实现了
    return ((NTOPENPROCESS)uOldNtOpenProcess)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}

void PageProtectOff() {

    //MDSystemCall = MmCreateMdl(NULL, KeServiceDescriptorTable->ntoskrnl.ServiceTableBase, KeServiceDescriptorTable->ntoskrnl.NumberOfService * 4);
    //if (!MDSystemCall)
    //  //return STATUS_UNSUCCESSFUL;
    //  return;
    //MmBuildMdlForNonPagedPool(MDSystemCall);
    //MDSystemCall->MdlFlags = MDSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
    //MappedSCT = MmMapLockedPages(MDSystemCall, KernelMode);
    __asm { //关闭内存保护
        push eax;
        mov eax, cr0;
        and eax, ~0x10000;
        mov cr0, eax;
        pop eax;
    }
}

void PageProtectOn() {
    解锁、释放MDL
    //if (MDSystemCall)
    //{
    //  MmUnmapLockedPages(MappedSCT, MDSystemCall);
    //  IoFreeMdl(MDSystemCall);
    //}
    __asm { //恢复内存保护
        push eax;
        mov eax, cr0;
        or eax, 0x10000;
        mov cr0, eax;
        pop eax;
    }
}
//3.修改函数地址,准备个函数用来修改函数地址
void HookNtOpenProcess() {
    NTSTATUS Status;
    Status = STATUS_SUCCESS;
    PageProtectOff();
    uOldNtOpenProcess = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0xBE];
    KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0xBE] = (ULONG)MyNtOpenProcess;
    PageProtectOn();
}
//4.恢复
void UnHookNtOpenProcess() {
    PageProtectOff();
    KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0xBE] = (ULONG)uOldNtOpenProcess;
    PageProtectOn();
}

VOID DriverUnload(PDRIVER_OBJECT pDriver) {
    UNREFERENCED_PARAMETER(pDriver);
    UnHookNtOpenProcess();

    KdPrint(("My Dirver is unloading..."));

}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPath) {
    UNREFERENCED_PARAMETER(pPath);
    KdPrint(("->%x \n", KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[0xBE]));//得到函数地址表




    HookNtOpenProcess();




    pDriver->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
}

然后操作时可以用PChunt观察HOOk前后这个函数地址变化

这是hook前image

这是Hook后
image

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值