X64下有patchguard,所以先讨论x86的
思路
首先找到函数地址,如果是导出函数,可以使用MmGetSystemRoutineAddress,如果未导出加载符号通过特征码
实现新新函数替换原函数
恢复函数
关于系统从应用层进入内核,xp前是int2e,之后32位是sysenter,64是syscall
https://bbs.pediy.com/thread-226254.htm
Ntdll封装了API的调用函数,Kernel32.dll中的API通过Ntdll.dll时完成参数的检查再调用一个中断,int2e,sysenter,syscall进入内核,服务号放在eax,栈放在eDx中(我记不请回头看下),根据存放在 EAX 中的索引值来在 SSDT 数组中调用指定的服务即Nt*系列函数
Zw*和NT*
在ntdll.dll这两函数完全一样,进入内核Ntosknl后一个做参数校验,一个不管,ssdt表放的是NT*,zw*是对Nt封装,zw是内核使用的函数,应用层直接去NT,Zw*会把Kthread中PreviousMode设置为KernelMode,再调用Nt*,所以不会参数检查。内核如果直接调用NT*,这里是userMode。
SSDTHOOK思路
第一行代码一般是索引号给eax(x64不在第一条,需要反汇编后定位指令),所以可以定位这个操作码,比如(mov eax,012h,eaxy一个字节,012h四个字节)。所以函数地址+1,就是下标,转换公式
索引号=*(DWORD*)((usigned char *)fun+1)
或者解析PE文件,找出导出表里这个函数下标。
x86下ssdt表存放的函数的绝对地址:
funaddr=KeSer*Des*+4*index
x64下存的是函数地址和ssdt表首地址的相对偏移,并且左移了4位。
funaddr=KeSer*Des+(KeSer*Des*+4*index)>>4
#pragma pack(1)typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;} ServiceDescriptorTableEntry_t,*PServiceDescriptorTableEntry_t;#pragma pack()
导入:
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
然后定义一个宏吧 给一个Zw的地址 返回它在SSDT对应函数的地址
引用:
——function是NT对应的Zw函数(zw+1就是指令地址号了)
#define SYSTEMSERVICE(_function) KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)]
#define SDT SYSTEMSERVICE
使用就是SDT(ZwCreatefile)
然后找到函数地址---保存原地址--替换地址 当然函数要与原始函数一致
typedef NTSTATUS (*ZWCREATESECTION)(
OUT PHANDLE SectionHandle,
IN ULONG DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize OPTIONAL,
IN ULONG PageAttributess,
IN ULONG SectionAttributes,
IN HANDLE FileHandle OPTIONAL );
//定义函数指针保存原函数地址,用于恢复
static ZWCREATESECTION OldZwCreateSection;
NTSTATUS NTAPI HOOK_NtCreateSection(PHANDLE SectionHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PLARGE_INTEGER SectionSize,
ULONG Protect,
ULONG Attributes,
HANDLE FileHandle)
{
return OldZwCreateSection(SectionHandle,
DesiredAccess,
ObjectAttributes,
SectionSize,
Protect,
Attributes,
FileHandle);
}
进行HOOK
恢复Hook
移除HOOK 这部很容易蓝屏 建议还是使用DeviceIControl进行卸载 (写一个恢复函数 然后通过下发请求调用)
如果非要卸载可以使用引用计数。
关CRO对多核无效,应该使用MDL方式修改内存。
NTSTATUS RtlSuperCopyMemory(IN VOID UNALIGNED *Dst,
IN CONST VOID UNALIGNED *Src,
IN ULONG Length)
{
//MDL是一个对物理内存的描述,负责把虚拟内存映射到物理内存
PMDL pmdl = IoAllocateMdl(Dst, Length, 0, 0, NULL);//分配mdl
if(pmdl==NULL)
return STATUS_UNSUCCESSFUL;
MmBuildMdlForNonPagedPool(pmdl);//build mdl
unsigned int *Mapped = (unsigned int *)MmMapLockedPages(pmdl, KernelMode);//锁住内存
if(!Mapped){
IoFreeMdl(pmdl);
return STATUS_UNSUCCESSFUL;
}
KIRQL kirql = KeRaiseIrqlToDpcLevel();
RtlCopyMemory(Mapped, Src, Length);
KeLowerIrql(kirql);
MmUnmapLockedPages((PVOID)Mapped,pmdl);
IoFreeMdl(pmdl);
return STATUS_SUCCESS;
}
//安装:
OldZwLoadDriver = SDT(ZwLoadDriver);
ULONG hookAddr = (ULONG) Hook_ZwLoadDriver;
RtlSuperCopyMemory(&SDT(ZwLoadDriver), &hookAddr, 4);
//卸载:
ULONG oldAddr = (ULONG)OldZwLoadDriver;
RtlSuperCopyMemory(&SDT(ZwLoadDriver), &oldAddr, 4);
一个使用MDL的ssdtHOOK例子
#include <ntddk.h>
#include <ntimage.h>
#pragma pack(1)
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase; //Used only in checked build
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
#define SYSTEMSERVICE(_function) KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)]
#define SDT SYSTEMSERVICE
#define KSDT KeServiceDescriptorTable
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT DeviceObject;
PKEVENT Event;
BOOLEAN bPCreate;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
// 全局设备对象
PDEVICE_OBJECT g_pDeviceObject;
UNICODE_STRING g_RegPath;
typedef NTSTATUS(*ZWCREATESECTION)(
OUT PHANDLE SectionHandle,
IN ULONG DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize OPTIONAL,
IN ULONG PageAttributess,
IN ULONG SectionAttributes,
IN HANDLE FileHandle OPTIONAL);
//定义函数指针保存原函数地址,用于恢复
static ZWCREATESECTION OldZwCreateSection;
NTSTATUS RtlSuperCopyMemory(IN VOID UNALIGNED *Dst,
IN CONST VOID UNALIGNED *Src,
IN ULONG Length)
{
//MDL是一个对物理内存的描述,负责把虚拟内存映射到物理内存
PMDL pmdl = IoAllocateMdl(Dst, Length, 0, 0, NULL);//分配mdl
if (pmdl == NULL)
return STATUS_UNSUCCESSFUL;
MmBuildMdlForNonPagedPool(pmdl);//build mdl
unsigned int *Mapped = (unsigned int *)MmMapLockedPages(pmdl, KernelMode);//锁住内存
if (!Mapped) {
IoFreeMdl(pmdl);
return STATUS_UNSUCCESSFUL;
}
KIRQL kirql = KeRaiseIrqlToDpcLevel();
RtlCopyMemory(Mapped, Src, Length);
KeLowerIrql(kirql);
MmUnmapLockedPages((PVOID)Mapped, pmdl);
IoFreeMdl(pmdl);
return STATUS_SUCCESS;
}
NTSTATUS NTAPI HOOK_NtCreateSection(PHANDLE SectionHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PLARGE_INTEGER SectionSize,
ULONG Protect,
ULONG Attributes,
HANDLE FileHandle)
{
return OldZwCreateSection(SectionHandle,
DesiredAccess,
ObjectAttributes,
SectionSize,
Protect,
Attributes,
FileHandle);
}
void StartHook(void)
{
//获取未导出的服务函数索引号
OldZwCreateSection = SDT(ZwCreateSection);
ULONG hookAddr = (ULONG)HOOK_NtCreateSection;
RtlSuperCopyMemory(&SDT(ZwCreateSection), &hookAddr, 4);
return;
}
void RemoveHook(void)
{
ULONG oldAddr = (ULONG)OldZwCreateSection;
RtlSuperCopyMemory(&SDT(ZwCreateSection), &oldAddr, 4);
return;
}
void UnloadDriver(IN PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING uszDeviceString;
NTSTATUS ntStatus;
//移除挂接
RemoveHook();
IoDeleteDevice(DriverObject->DeviceObject);
RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\ITSys");
IoDeleteSymbolicLink(&uszDeviceString);
}
NTSTATUS DispatchIoCtrl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
switch (irpStack->Parameters.DeviceIoControl.IoControlCode)
{
default:
break;
}
Irp->IoStatus.Status = ntStatus;
// 设置返回给用户层程序的数据的字节数
if (ntStatus == STATUS_SUCCESS)
Irp->IoStatus.Information = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
else
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
NTSTATUS DispatchCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DispatchClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS rc;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
rc = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return rc;
}
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
NTSTATUS ntStatus;
UNICODE_STRING uszDriverString;
UNICODE_STRING uszDeviceString;
UNICODE_STRING uszEventString;
PDEVICE_OBJECT pDeviceObject;
PDEVICE_EXTENSION extension;
// 初始化设备对象名
RtlInitUnicodeString(&uszDriverString, L"\\Device\\ITSys");
// 创建并初始化对象
ntStatus = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
&uszDriverString,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&pDeviceObject
);
if (ntStatus != STATUS_SUCCESS)
return ntStatus;
extension = pDeviceObject->DeviceExtension;
RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\ITSys");
// 创建用户可见连接名称
ntStatus = IoCreateSymbolicLink(&uszDeviceString, &uszDriverString);
if (ntStatus != STATUS_SUCCESS)
{
// 创建失败,删除对象并返回错误值
IoDeleteDevice(pDeviceObject);
return ntStatus;
}
// 赋值全局设备对象指针
// Assign global pointer to the device object for use by the callback functions
g_pDeviceObject = pDeviceObject;
// 设置所有可用的DeviceIoControl的处理IRP的函数
DriverObject->DriverUnload = UnloadDriver;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoCtrl;
#if DBG
KdPrint(("RegistryPath : %ws\n", RegistryPath->Buffer));
#endif
//SDT挂接
StartHook();
return ntStatus;
}
使用结果图