SSDT

学习资料:http://blog.csdn.net/zfdyq0/article/details/26515019

学习资料:WIN64内核编程基础 胡文亮

SSDT(系统服务描述表),刚开始接触什么进程保护XXX啥的,都是看到在说SSDT(虽然说目前很多杀软都已经采用稳定简单的回调姿势了)。这次就弄清楚两个问题:

如何在内核里动态获得 SSDT 的基址;

如何在内核里动态获得 SSDT 函数的地址;

在 WIN32 下,第一个问题就根本不是问题,因为 KeServiceDescriptorTable 直接被导

出了。但是 WIN64 下 KeServiceDescriptorTable 没有被导出。所以必须搜索得到它的地址。

反汇编:uf KisystemCall64

上面看到,貌似直接反汇编uf KiSystemServiceRepeat 试了下,一样可以找到SSDT基址,但是问题是,KiSystemServiceRepeat这个东西的地址找不到,而KiSystemCall64的地址直接可以直接读取指定的 msr 得出。

通过读取 C0000082 寄存器,能够得到 KiSystemCall64 的地址,然后从

KiSystemCall64 的地址开始,往下搜索 0x500 字节左右(特征码是 4c8d15),就能得到

KeServiceDescriptorTable 的地址了。同理,我们换一下特征码(4c8d1d),就能获得

KeServiceDescriptorTableShadow 的地址了。

然后是资料里面带的两个SSDT基址的函数:

方法1(这个方法蓝屏了,我直接用的方法2)

ULONGLONG GetKeServiceDescriptorTable64()
{
char KiSystemServiceStart_pattern[13] = "\x8B\xF8\xC1\xEF\x07\x83\xE7\x20\x25\xFF\x0F\x00\x00";
ULONGLONG CodeScanStart = (ULONGLONG)&_strnicmp;
ULONGLONG CodeScanEnd = (ULONGLONG)&KdDebuggerNotPresent;
UNICODE_STRING Symbol;
ULONGLONG i, tbl_address, b;
for (i = 0; i < CodeScanEnd - CodeScanStart; i++)
{
if (!memcmp((char*)(ULONGLONG)CodeScanStart +i, (char*)KiSystemServiceStart_pattern,13))
{
for (b = 0; b < 50; b++)
{
tbl_address = ((ULONGLONG)CodeScanStart+i+b);
if (*(USHORT*) ((ULONGLONG)tbl_address ) == (USHORT)0x8d4c)
return ((LONGLONG)tbl_address +7) + *(LONG*)(tbl_address +3);
}
}
}
return 0;
}

方法2

ULONGLONG MyGetKeServiceDescriptorTable64()
{
PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082);
PUCHAR EndSearchAddress = StartSearchAddress + 0x500;
PUCHAR i = NULL;
UCHAR b1=0,b2=0,b3=0;
ULONG templong=0;
ULONGLONG addr=0;
for(i=StartSearchAddress;i<EndSearchAddress;i++)
{
if( MmIsAddressValid(i) && MmIsAddressValid(i+1) && MmIsAddressValid(i+2) )
{
b1=*i;
b2=*(i+1);
b3=*(i+2);
if( b1==0x4c && b2==0x8d && b3==0x15 ) //4c8d15
{
memcpy(&templong,i+3,4);
addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
return addr;
}
}
}
return 0;
}

接下来就是讲述 SSDT 函数地址了。在获得地址之前,需要知道 SSDT 函数的 INDEX。获得这个 INDEX 的方法很简单,直接在 RING3 读取 NTDLL 的内容即可。使用 WINDBG 的方法如下:随便创建一个进程,然后使用 WINDBG 附加:

可以看到两次反汇编的结果几乎完全相同,唯一不同的地方是第二句。XXh 就是此函数的 index。知道 INDEX 之后,就可以计算地址了(资料上也是给了两个方法,建议使用方法2)。

方法1

VOID Initxxxx()
{
UCHAR strShellCode[36]="\x48\x8B\xC1\x4C\x8D\x12\x8B\xF8\xC1\xEF\x07\x83\xE7\x20\x4E\x8B\x14\x17\x4D\x63\x1C\x82\x49\x8B\xC3\x49\xC1\xFB\x04\x4D\x03\xD3\x49\x8B\xC2\xC3";
/*
mov rax, rcx ;rcx=index
lea r10,[rdx] ;rdx=ssdt
mov edi,eax
shr edi,7
and edi,20h
mov r10, qword ptr [r10+rdi]
movsxd r11,dword ptr [r10+rax*4]
mov rax,r11
sar r11,4
add r10,r11
mov rax,r10
ret
*/
scfn=ExAllocatePool(NonPagedPool,36);
memcpy(scfn,strShellCode,36);
}
ULONGLONG GetSSDTFunctionAddress64(ULONGLONG NtApiIndex)
{
ULONGLONG ret=0;
ULONGLONG ssdt= MyGetKeServiceDescriptorTable64();
if(scfn==NULL)
Initxxxx();
ret=scfn(NtApiIndex, ssdt);
return ret;
}

方法2

typedef struct _SYSTEM_SERVICE_TABLE{
PVOID ServiceTableBase;
PVOID ServiceCounterTableBase;
ULONGLONG NumberOfServices;
PVOID ParamTableBase;
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE; ULONGLONG GetSSDTFunctionAddress64_2(ULONGLONG Index)
{
LONG dwTemp=0;
ULONGLONG qwTemp=0,stb=0,ret=0;
PSYSTEM_SERVICE_TABLE ssdt=(PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
stb=(ULONGLONG)(ssdt->ServiceTableBase);
qwTemp = stb + 4 * Index;
dwTemp = *(PLONG)qwTemp;
dwTemp = dwTemp >> 4;
ret = stb + (LONG64)dwTemp;
return ret;
}

最后,总结一下 WIN32 和 WIN64 在 SSDT 方面的不同(我直接截图过来)。大家可以把 SSDT(其实 SHADOWSSDT 同理)想像成一排保险柜,每个柜子都有编号(从 0 开始),柜子的长度为四字节,每个柜子里都放了一个 LONG 数据。但不同的是,WIN32 的“柜子”里放的数据是某个函数的绝对地址,而 WIN64 的“柜子”里放的数据是某个函数的偏移地址。这个偏移地址要经过一定的计算才能变成绝对地址。

测试代码:

#include <ntddk.h>
#include <windef.h>
#include "MyDriver.h" #pragma intrinsic(__readmsr)
typedef UINT64 (__fastcall *SCFN)(UINT64,UINT64);
SCFN scfn; ULONGLONG MyGetKeServiceDescriptorTable64()
{
PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082);
PUCHAR EndSearchAddress = StartSearchAddress + 0x500;
PUCHAR i = NULL;
UCHAR b1=0,b2=0,b3=0;
ULONG templong=0;
ULONGLONG addr=0;
for(i=StartSearchAddress;i<EndSearchAddress;i++)
{
if( MmIsAddressValid(i) && MmIsAddressValid(i+1) && MmIsAddressValid(i+2) )
{
b1=*i;
b2=*(i+1);
b3=*(i+2);
if( b1==0x4c && b2==0x8d && b3==0x15 ) //4c8d15
{
memcpy(&templong,i+3,4);
addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
return addr;
}
}
}
return 0;
} VOID Initxxxx()
{
UCHAR strShellCode[36]="\x48\x8B\xC1\x4C\x8D\x12\x8B\xF8\xC1\xEF\x07\x83\xE7\x20\x4E\x8B\x14\x17\x4D\x63\x1C\x82\x49\x8B\xC3\x49\xC1\xFB\x04\x4D\x03\xD3\x49\x8B\xC2\xC3";
/*
mov rax, rcx ;rcx=index
lea r10,[rdx] ;rdx=ssdt
mov edi,eax
shr edi,7
and edi,20h
mov r10, qword ptr [r10+rdi]
movsxd r11,dword ptr [r10+rax*4]
mov rax,r11
sar r11,4
add r10,r11
mov rax,r10
ret
*/
scfn=ExAllocatePool(NonPagedPool,36);
memcpy(scfn,strShellCode,36);
} ULONGLONG GetSSDTFunctionAddress64(ULONGLONG NtApiIndex)
{
ULONGLONG ret=0;
ULONGLONG ssdt= MyGetKeServiceDescriptorTable64();
if(scfn==NULL)
Initxxxx();
ret=scfn(NtApiIndex, ssdt);
return ret;
} typedef struct _SYSTEM_SERVICE_TABLE{
PVOID ServiceTableBase;
PVOID ServiceCounterTableBase;
ULONGLONG NumberOfServices;
PVOID ParamTableBase;
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE; ULONGLONG GetSSDTFunctionAddress64_2(ULONGLONG Index)
{
LONG dwTemp=0;
ULONGLONG qwTemp=0,stb=0,ret=0;
PSYSTEM_SERVICE_TABLE ssdt=(PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
stb=(ULONGLONG)(ssdt->ServiceTableBase);
qwTemp = stb + 4 * Index;
dwTemp = *(PLONG)qwTemp;
dwTemp = dwTemp >> 4;
ret = stb + (LONG64)dwTemp;
return ret;
} VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
{
UNICODE_STRING strLink;
RtlInitUnicodeString(&strLink, LINK_NAME);
IoDeleteSymbolicLink(&strLink);
IoDeleteDevice(pDriverObj->DeviceObject);
} NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
} NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
} NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
PIO_STACK_LOCATION pIrpStack;
ULONG uIoControlCode;
PVOID pIoBuffer;
ULONG uInSize;
ULONG uOutSize;
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
switch(uIoControlCode)
{
;
}
if(status == STATUS_SUCCESS)
pIrp->IoStatus.Information = uOutSize;
else
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
} NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
{
NTSTATUS status = STATUS_SUCCESS;
UNICODE_STRING ustrLinkName;
UNICODE_STRING ustrDevName;
PDEVICE_OBJECT pDevObj;
pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
pDriverObj->DriverUnload = DriverUnload;
RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);
status = IoCreateDevice(pDriverObj, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
if(!NT_SUCCESS(status)) return status;
if(IoIsWdmVersionAvailable(1, 0x10))
RtlInitUnicodeString(&ustrLinkName, LINK_GLOBAL_NAME);
else
RtlInitUnicodeString(&ustrLinkName, LINK_NAME);
status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);
if(!NT_SUCCESS(status))
{
IoDeleteDevice(pDevObj);
return status;
}
//test
DbgPrint("[method 1]SSDT: %llx\n",MyGetKeServiceDescriptorTable64()); DbgPrint("[method 1]NtOpenProcess: %llx\n",GetSSDTFunctionAddress64(0x23)); //WIN7X64 HARDCODE
DbgPrint("[method 1]NtTerminateProcess: %llx\n",GetSSDTFunctionAddress64(0x29)); //WIN7X64 HARDCODE DbgPrint("[method 2]NtOpenProcess: %llx\n",GetSSDTFunctionAddress64_2(0x23)); //WIN7X64 HARDCODE
DbgPrint("[method 2]NtTerminateProcess: %llx\n",GetSSDTFunctionAddress64_2(0x29));//WIN7X64 HARDCODE
//test
return STATUS_SUCCESS;
}

执行结果:

Win64 驱动内核编程-18.SSDT的更多相关文章

  1. Win64 驱动内核编程-7.内核里操作进程

    在内核里操作进程 在内核里操作进程,相信是很多对 WINDOWS 内核编程感兴趣的朋友第一个学习的知识点.但在这里,我要让大家失望了,在内核里操作进程没什么特别的,就标准方法而言,还是调用那几个和进程 ...

  2. Win64 驱动内核编程-3.内核里使用内存

    内核里使用内存 内存使用,无非就是申请.复制.设置.释放.在 C 语言里,它们对应的函数是:malloc.memcpy.memset.free:在内核编程里,他们分别对应 ExAllocatePool ...

  3. Win64 驱动内核编程-8.内核里的其他常用

    内核里的其他常用 1.遍历链表.内核里有很多数据结构,但它们并不是孤立的,内核使用双向链表把它们像糖 葫芦一样给串了起来.所以遍历双向链表能获得很多重要的内核数据.举个简单的例子,驱 动对象 Driv ...

  4. Win64 驱动内核编程-2.基本框架(安装.通讯.HelloWorld)

    驱动安装,通讯,Hello World 开发驱动的简单流程是这样,开发驱动安装程序,开发驱动程序,然后安装程序(或者其他程序)通过通讯给驱动传命令,驱动接到之后进行解析并且执行,然后把执行结果返回. ...

  5. Win64 驱动内核编程-22.SHADOW SSDT HOOK(宋孖健)

    SHADOW SSDT HOOK HOOK 和 UNHOOK SHADOW SSDT 跟之前的 HOOK/UNHOOK SSDT 类似,区别是查找SSSDT的特征码,以及根据索引计算函数地址的公式,还 ...

  6. Win64 驱动内核编程-20.UnHook SSDT

    UNHOOK SSDT 要恢复 SSDT,首先要获得 SSDT 各个函数的原始地址,而 SSDT 各个函数的原始地址,自然是存储在内核文件里的.于是,有了以下思路: 1.获得内核里 KiService ...

  7. Win64 驱动内核编程-23.Ring0 InLineHook 和UnHook

    Ring0 InLineHook 和UnHook 如果是要在R0里hook,作者的建议是InLine HOOK,毕竟SSDT HOOK 和 SHADOW SSDT HOOK比较麻烦,不好修改.目前R3 ...

  8. Win64 驱动内核编程-19.HOOK-SSDT

    HOOK SSDT 在 WIN64 上 HOOK SSDT 和 UNHOOK SSDT 在原理上跟 WIN32 没什么不同,甚至说 HOOK 和 UNHOOK 在本质上也没有不同,都是在指定的地址上填 ...

  9. Win64 驱动内核编程-11.回调监控进线程句柄操作

    无HOOK监控进线程句柄操作 在 NT5 平台下,要监控进线程句柄的操作. 通常要挂钩三个API:NtOpenProcess.NtOpenThread.NtDuplicateObject.但是在 VI ...

随机推荐

  1. 使用wireshark 抓取 http https tcp ip 协议进行学习

    使用wireshark 抓取 http https tcp ip 协议进行学习 前言 本节使用wireshark工具抓包学习tcp ip http 协议 1. tcp 1.1 tcp三次握手在wire ...

  2. Kotlin/Java Base64编码和解码(图片、文件)

    原文: Kotlin/Java Base64编码和解码(图片.文件) | Stars-One的杂货小窝 最近在项目中使用到了Base64编码和解码,便是稍微写篇文章记录一下 PS:本文代码都是使用Ko ...

  3. P2766 最长不下降子序列问题 题解(网络流)

    题目链接 最长不下降子序列问题 解题思路 分成三小问解决. 第一小问,求\(LIS\),因为\(n<=500\),直接\(O(N^2)\)暴力求解即可. 第二三小问,建立模型用网络流求解. 对于 ...

  4. 有必要了解的大数据知识(一) Hadoop

    前言 之前工作中,有接触到大数据的需求,虽然当时我们体系有专门的大数据部门,但是由于当时我们中台重构,整个体系的开发量巨大,共用一个大数据部门,人手已经忙不过来,没法办,为了赶时间,我自己负责的系统的 ...

  5. python2 虚拟环境

    1.安装virtualenv pip install virtualenv 2.创建虚拟环境 virtualenv 虚拟环境名称  3.激活虚拟环境 source 虚拟环境目录/bin/activat ...

  6. Flutter Widget中的State

    一.Flutter 的声明式视图开发 在原生系统(Android.iOS)或原生JavaScript 开发的话,应该知道视图开发是命令式的,需要精确地告诉操作系统或浏览器用何种方式去做事情. 比如,如 ...

  7. Kubernetes,kubectl常用命令详解

    kubectl概述 祭出一张图,转载至 kubernetes-handbook/kubectl命令概述 ,可以对命令族有个整体的概念. 环境准备 允许master节点部署pod,使用命令如下: kub ...

  8. [Fundamental of Power Electronics]-PART II-9. 控制器设计-9.1 引言

    9.1 引言 在所有的开关变换器中,输出电压\(v(t)\)都是输入电压\(v_{g}(t)\),占空比\(d(t)\),负载电流\(i_{load}(t)\)和电路元件值的函数.在DC-DC变换器应 ...

  9. Element源码:项目初始化和webpack配置

    0x00.项目初始化 由于整个过程像素级 copy element,所以将不使用vue-cli初始化项目. 创建项目 新建一个空的文件夹,使用npm init 来初始化项目,并安装vue模块. 修改目 ...

  10. 【Git基本命令】

    [基本指令] git init :使目标文件夹变成一个仓库 git add <文件名,含后缀> : 告诉git我要添加文件了 git commit -m "<提交说明> ...