能力值:
( LV2,RANK:10 )
3 楼
SSDT索引号的获取
对于这个问题,困惑了许久,也许是应为很久没弄这个方面的东西了,曾经看过关于Native API 和 SSDT都忘得差不多了。吸取教训,今天把知识记录下来,并时常温故而知新。
系统服务描述符表,System Service Dispatch Table,SSDT
KeServiceDescriptorTable是由内核导出的表。该表拥有一个指针(其实仅有ntoskrnel一项,没有包含win32k),指向SSDT中包含由Ntoskrnl.exe实现的核心系统服务的相应部分,它是内核的主要组成部分。
typedef struct _SystemServiceDescriptorTable
{
PVOID ServiceTableBase;
PULONG ServiceCounterTableBase;
ULONG NumberOfService;
ULONG ParamTableBase;
}SystemServiceDescriptorTable,*PSystemServiceDescriptorTable;
其中SSDT包含了所有内核导出函数的地址。每个地址长度为4个字节。所以要获得某个函数在SSDT中的偏移量,可以用:KeServiceDescriptorTable->ServiceTableBase + 函数ID* 4获取。
那么怎么获取函数ID呢?
其实很简单,ntoskrnl.exe导出的Zw*函数和Nt*函数,大家肯定都注意到了很多函数都是同名的,只是前缀不同。其实是这样的,Nt*函数是私有函数,其地址列于SSDT中。Zw*函数是由内核为使用设备驱动程序和其他内核组件而导出的函数,目的就在于同过Zw*函数去调用内核的Nt函数。(SSDT中的每一项和每个Zw*函数之间不存在一对一的对应关系)
内核中的所有Zw*函数都以操作码mov eax, ULONG起始,其中ULONG是系统调用在SSDT中的索引号。
说的具体点,就是每个Zw*号函数的第一行中的 ULONG值 就是其所对应的Nt*函数在SSDT中的索引号。
举个例子,如果我现在想获取 NtOpenProcess的ID号,那么就可以在Windbg中运行 u ZwOpenProcess
u ZwOpenProcess
nt!ZwOpenProcess:
805016c4 b87a000000 mov eax,7Ah
805016c9 8d542404 lea edx,[esp+4]
805016cd 9c pushfd
805016ce 6a08 push 8
805016d0 e88c0d0400 call nt!KiSystemService (80542461)
805016d5 c21000 ret 10h 获取到的0x7A就是NtOpenProcess在SSDT中的ID号了。
代码1: (需要自己获取索引号)
#include<ntddk.h> typedef struct _SystemServiceDescriptorTable
{
PVOID ServiceTableBase;
PULONG ServiceCounterTableBase;
ULONG NumberOfService;
ULONG ParamTableBase;
}SystemServiceDescriptorTable,*PSystemServiceDescriptorTable;
// KeServiceDescriptorTable为ntoskrnl.exe导出
extern PSystemServiceDescriptorTable KeServiceDescriptorTable;
// 定义一下NtOpenProcess的原型,下面如果用汇编调用就不用定义了,但是我想尽量不用汇编
typedef NTSTATUS (__stdcall *NTOPENPROCESS)( OUT PHANDLE ProcessHandle,
IN ACCESS_MASK AccessMask,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId
);
NTOPENPROCESS RealNtOpenProcess;
// 定义函数原型
VOID Hook();
VOID Unhook();
VOID OnUnload(IN PDRIVER_OBJECT DriverObject);
// 真实的函数地址,我们会在自定义的函数中调用
ULONG RealServiceAddress;
// 需要被驱动保护的进程ID
HANDLE MyPID;
// 自定义的NtOpenProcess函数
NTSTATUS __stdcall MyNtOpenProcess( OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId )
{
NTSTATUS rc;
ULONG PID;
//DbgPrint( "NtOpenProcess() called.\n" );
rc = (NTSTATUS)(NTOPENPROCESS)RealNtOpenProcess( ProcessHandle, DesiredAccess,
ObjectAttributes, ClientId );
if( (ClientId != NULL) )
{
PID = (ULONG)ClientId->UniqueProcess;
//DbgPrint( "%d was opened,Handle is %d.\n", PID, (ULONG)ProcessHandle );
// 如果进程PID是1520,直接返回权限不足,并将句柄设置为空
if( PID == 1520 )
{
DbgPrint( "Some want to open pid 1520!\n" );
ProcessHandle = NULL;
rc = STATUS_ACCESS_DENIED;
}
}
return rc;
}
// 驱动入口
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath )
{
DriverObject->DriverUnload = OnUnload;
Hook();
return STATUS_SUCCESS;
}
// 驱动卸载
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
Unhook( );
}
// 此处修改SSDT中的NtOpenProcess服务地址
VOID Hook()
{
ULONG Address;
// 0x7A为Winxp+SP2下NtOpenProcess服务ID号
// Adress是个地址A,这个地址的数据还是一个地址B,这个地址B就是NtOpenProcess的地址了
// (ULONG)KeServiceDescriptorTable->ServiceTableBase就是温家堡的第一个房间
// Address是第7A个房间。
Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;
// 取得地址A的值,也就是NtOpenProcess服务的地址了,保存原来NtOpenProcess的地址以后恢复用
RealServiceAddress = *(ULONG*)Address;
RealNtOpenProcess = (NTOPENPROCESS)RealServiceAddress;
DbgPrint( "Address of Real NtOpenProcess: 0x%08X\n", RealServiceAddress );
DbgPrint(" Address of MyNtOpenProcess: 0x%08X\n", MyNtOpenProcess );
// 去掉内存保护
__asm
{
cli
mov eax, cr0
and eax, not 10000h
mov cr0, eax
}
// 修改SSDT中NtOpenProcess服务的地址
*((ULONG*)Address) = (ULONG)MyNtOpenProcess;
// 恢复内存保护
__asm
{
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
}
}
//////////////////////////////////////////////////////
VOID Unhook()
{
ULONG Address;
Address = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;
__asm
{
cli
mov eax, cr0
and eax, not 10000h
mov cr0, eax
}
// 还原SSDT
*((ULONG*)Address) = (ULONG)RealServiceAddress;
__asm
{
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
}
DbgPrint("Unhook");
} 代码2:(不需要自己获取索引号,程序自动获取)
#include <ntddk.h>
#pragma pack(1)
typedef struct _SYSTEM_SERVICES_DESCRIPTOR_TABLE
{
PULONG *ServiceTableBase;
PULONG *ServiceCounterTableBase; //Used in check builds only
unsigned int NumberOfServices;
PULONG *ParamTableBase;
}SSDT, *PSSDT;
#pragma pack()
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT pDevObj;
UNICODE_STRING uniSymLink;
PMDL pMdl;
PULONG pulSSDTMapped;
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
typedef NTSTATUS (*ZWTERMINATEPROCESS)(
IN HANDLE ProcessHandle,
IN NTSTATUS ExitStatus
);
/*Getting system service function address, the index of function locates 1 bytes offset*/
#define SYSTEM_SERVICE(_Func) KeServiceDescriptorTable.ServiceTableBase[*(PULONG)((PUCHAR)_Func + 1)]
#define SYSTEM_INDEX(_Func) (*(PULONG)((PUCHAR)_Func + 1))
#define IOCTL_START_PROTECTION CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define C_MAXPROCNUMS 12
//Global variable
__declspec(dllimport) SSDT KeServiceDescriptorTable;
ZWTERMINATEPROCESS ZwTerminateProcessReal;
ULONG ulPIDs[C_MAXPROCNUMS];
DRIVER_UNLOAD DriverUnload;
DRIVER_DISPATCH DispatchDevOpen, DispatchDevCtl;
NTSTATUS ZwTerminateProcessHook(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus); NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryPath)
{
PDEVICE_OBJECT pDevObj;
PDEVICE_EXTENSION pDevExt;
UNICODE_STRING uniSymLink, uniDevName;
NTSTATUS ntStatus;
PMDL pMdl;
PULONG pulSSDTMapped;
RtlInitUnicodeString(&uniSymLink, L"\\DosDevices\\ProcessesGuard_KernoneSoft");
RtlInitUnicodeString(&uniDevName, L"\\Device\\ProcessesGuard");
pDriverObj->DriverUnload = DriverUnload;
pDriverObj->MajorFunction[IRP_MJ_CREATE] =
pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchDevOpen;
pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDevCtl;
ntStatus = IoCreateDevice(pDriverObj, sizeof (DEVICE_EXTENSION), &uniDevName, FILE_DEVICE_UNKNOWN,
0, FALSE, &pDevObj);
if (!NT_SUCCESS(ntStatus))
return(ntStatus);
IoCreateSymbolicLink(&uniSymLink, &uniDevName);
pDevObj->Flags |= DO_BUFFERED_IO;
pDevExt = pDevObj->DeviceExtension;
pDevExt->pDevObj = pDevObj;
pDevExt->uniSymLink = uniSymLink;
pMdl = IoAllocateMdl(KeServiceDescriptorTable.ServiceTableBase, KeServiceDescriptorTable.NumberOfServices * 4,
FALSE, FALSE, NULL);
if (pMdl == NULL)
{
IoDeleteSymbolicLink(&uniSymLink);
IoDeleteDevice(pDevObj);
return(STATUS_INSUFFICIENT_RESOURCES);
}
MmBuildMdlForNonPagedPool(pMdl);
pMdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA; //Write SSDT
pulSSDTMapped = (PULONG)MmMapLockedPagesSpecifyCache(pMdl, KernelMode, MmNonCached, NULL, FALSE, NormalPagePriority);
if (pulSSDTMapped == NULL)
{
IoDeleteSymbolicLink(&uniSymLink);
IoDeleteDevice(pDevObj);
IoFreeMdl(pMdl);
return(STATUS_UNSUCCESSFUL);
}
pDevExt->pMdl = pMdl;
pDevExt->pulSSDTMapped = pulSSDTMapped;
ZwTerminateProcessReal = (ZWTERMINATEPROCESS)SYSTEM_SERVICE(ZwTerminateProcess);
pulSSDTMapped[SYSTEM_INDEX(ZwTerminateProcess)] = (PULONG)ZwTerminateProcessHook;
return(ntStatus);
}
VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
{
PDEVICE_OBJECT pDevObj = pDriverObj->DeviceObject;
PDEVICE_EXTENSION pDevExt = pDevObj->DeviceExtension;
PULONG pulSSDTMapped = pDevExt->pulSSDTMapped;
PMDL pMdl = pDevExt->pMdl;
pulSSDTMapped[SYSTEM_INDEX(ZwTerminateProcess)] = (PULONG)ZwTerminateProcessReal;
MmUnmapLockedPages(pulSSDTMapped, pMdl);
IoFreeMdl(pMdl);
while (pDevObj)
{
pDevExt = pDevObj->DeviceExtension;
pDevObj = pDevObj->NextDevice;
IoDeleteSymbolicLink(&pDevExt->uniSymLink);
IoDeleteDevice(pDevExt->pDevObj);
}
}
NTSTATUS DispatchDevOpen(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return(ntStatus);
}
NTSTATUS DispatchDevCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ulIoCode, ulBufLength, ulRtn, ulCounts = 0, ulIndex;
PVOID pvBuf;
NTSTATUS ntStatus;
ulIoCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
switch (ulIoCode)
{
case IOCTL_START_PROTECTION:
ulBufLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
pvBuf = pIrp->AssociatedIrp.SystemBuffer;
ulCounts = ulBufLength / sizeof (ULONG);
KdPrint(("Protection Numbers: %d\n"), ulCounts);
for (ulIndex = 0; ulIndex < ulCounts && ulIndex < C_MAXPROCNUMS; ulIndex++)
{
ulPIDs[ulIndex] = ((PULONG)pvBuf)[ulIndex];
KdPrint(("Index %d -- PID %d\n"), ulIndex, ulPIDs[ulIndex]);
}
ntStatus = STATUS_SUCCESS;
ulRtn = ulBufLength;
break;
default:
ntStatus = STATUS_INVALID_PARAMETER;
ulRtn = 0;
break;
}
pIrp->IoStatus.Status = ntStatus;
pIrp->IoStatus.Information = ulRtn;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return(ntStatus);
}
NTSTATUS ZwTerminateProcessHook(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus)
{
PEPROCESS pEProcessTarget = NULL;
int i;
HANDLE hID;
BOOLEAN bKillSelf = FALSE, bKillProtection = FALSE;
if (NT_SUCCESS(ObReferenceObjectByHandle(ProcessHandle, STANDARD_RIGHTS_READ, *PsProcessType, KernelMode, &pEProcessTarget, NULL)))
{
hID = PsGetProcessId(pEProcessTarget);
for (i = 0; i < C_MAXPROCNUMS; i++)
{
if((ULONG)hID == ulPIDs[ i ])
{
bKillProtection = TRUE;
if ((ULONG)PsGetCurrentProcessId() == ulPIDs[ i ])
bKillSelf = TRUE;
else
bKillSelf = FALSE;
break;
}
}
ObDereferenceObject(pEProcessTarget);
}
if (bKillProtection)
{
if (bKillSelf)
return(ZwTerminateProcessReal(ProcessHandle, ExitStatus));
else
return(STATUS_ACCESS_DENIED);
}
else
return(ZwTerminateProcessReal(ProcessHandle, ExitStatus));
}