Win64 驱动内核编程-18.SSDT

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;
}
执行结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
详细目录如下: 0.基础的基础 |-学习WIN64驱动开发的硬件准备 |-配置驱动开发环境 ------------------------------ 1.驱动级HelloWorld |-配置驱动测试环境 |-编译和加载内核HelloWorld ------------------------------ 2.内核编程基础 |-WIN64内核编程的基本规则 |-驱动程序与应用程序通信 |-内核里使用内存 |-内核里操作字符串 |-内核里操作文件 |-内核里操作注册表 |-内核里操作进线程 |-驱动里的其它常用代码 ------------------------------ 3.内核HOOK与UNHOOK |-系统调用、WOW64与兼容模式 |-编程实现突破WIN7的PatchGuard |-系统服务描述表结构详解 |-SSDT HOOK和UNHOOK |-SHADOW SSDT HOOK和UNHOOK |-INLINE HOOK和UNHOOK ------------------------------ 4.无HOOK监控技术 |-无HOOK监控进线程启动和退出 |-无HOOK监控模块加载 |-无HOOK监控注册表操作 |-无HOOK监控文件操作 |-无HOOK监控进线程句柄操作 |-使用对象回调监视文件访问 |-无HOOK监控网络访问 |-无HOOK监视修改时间 ------------------------------ 5.零散内容 |-驱动里实现内嵌汇编 |-DKOM隐藏进程+保护进程 |-枚举和隐藏内核模块 |-强制结束进程 |-强制读写进程内存 |-枚举消息钩子 |-强制解锁文件 |-初步探索PE32+格式文件 ------------------------------ 6.用户态HOOK与UNHOOK |-RING3注射DLL到系统进程 |-RING3的INLINE HOOK和UNHOOK |-RING3的EAT HOOK和IAT HOOK ------------------------------ 7.反回调 |-枚举与删除创建进线程回调 |-枚举与删除加载映像回调 |-枚举与删除注册表回调 |-枚举与对抗MiniFilter |-枚举与删除对象回调

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值