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] = "x8BxF8xC1xEFx07x83xE7x20x25xFFx0Fx00x00";
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]="x48x8BxC1x4Cx8Dx12x8BxF8xC1xEFx07x83xE7x20x4Ex8Bx14x17x4Dx63x1Cx82x49x8BxC3x49xC1xFBx04x4Dx03xD3x49x8BxC2xC3";
/*
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]="x48x8BxC1x4Cx8Dx12x8BxF8xC1xEFx07x83xE7x20x4Ex8Bx14x17x4Dx63x1Cx82x49x8BxC3x49xC1xFBx04x4Dx03xD3x49x8BxC2xC3";
/*
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
",MyGetKeServiceDescriptorTable64());
DbgPrint("[method 1]NtOpenProcess: %llx
",GetSSDTFunctionAddress64(0x23)); //WIN7X64 HARDCODE
DbgPrint("[method 1]NtTerminateProcess: %llx
",GetSSDTFunctionAddress64(0x29)); //WIN7X64 HARDCODE
DbgPrint("[method 2]NtOpenProcess: %llx
",GetSSDTFunctionAddress64_2(0x23)); //WIN7X64 HARDCODE
DbgPrint("[method 2]NtTerminateProcess: %llx
",GetSSDTFunctionAddress64_2(0x29));//WIN7X64 HARDCODE
//test
return STATUS_SUCCESS;
}
执行结果: