昨天晚上发现这篇文章不错,适合新手看,所以就翻译了下,第一次翻译翻译不当的地方,希望大家指出来啊
===============================
这篇有别于其他的复杂深入研究windows内核文章。确切的说,阐述的是怎样访问那些出现在ntosknl中却没有被公开的API,如果你从r3层回溯一个windows api调用过程,将会发现将会结束点是类似这样子的(Win8 X64)。
其中r10寄存器的值是第一个参数,eax中保存的是windows SSDT 的索引值(Windows internal syscall table)。需要注意的是上述代码是在x64运行一个 native x64 应用程序的情况。X86是通过NTDLL中的 KiFastSystemCall来实现一个syscall请求的.WOW64子系统通过创建一套机制使得x86应用程序可以跑在x64系统上。当syscall指令执行,对于NtOPenFile来说最终下面的代码会被执行(位于ntoskrnl中)。这实际上是一个对IoCreateFile的封装
另外,需要注意的是,这个过程有许多的细节,syscall指令不只是简单直接的请求调Native Api,而是经过许多的例程,这些例程负责建立起陷阱框架(trp frames)并且执行访问权限核查流程,然后才到达native api执行流程。用于驱动的开发的导出的native 内核api 也有类似的过程,但是其机理不是很复杂,在内核中为每一个ZW* 函数提供了一个轻量封装的版本即Nt*,下面是一个例子
这个过程通过创建栈帧,屏蔽内核中断(cli),保存标志位,随后用KiServiceLinkAge 函数分支执行返回指令。最后,Syscall 索引号0x31被放入eax中,并跳入KiServiceInteral例程中,这个例程,位于其他的例程之间,负责设置正确的PreviousMode并且遍历windows syscall 表(SSDT),之后调用Nt* 版的API。
===================================
然而这些与本文主旨有何关系?答案是虽然内核为内核和驱动开发人员导出了成千上万的api,但是还有许多非常有价值的API提供了非常酷的功能(例如 ZwSuspendProcess/ZwResumeProcess, ZwReadVirtualMemory/ZwWriteVirtualMemory等。)但是这些函数没有被公开。这篇文章的目的就是接触这类API,这里有一些事情需要解决:
1.需要找到ntosknl 基址与其映像大小。
2.确定这些syscall,这里使用常规的方法来请求这些syscall
3.另外,要获取相关的API地址,(例如通过为了获取合法的进程句柄在内核中枚举目标进程(通过调用 ZwSuspend/ZwResume ))
===========
这三点,第一点相对来说简单点,但是同样依赖于没有公开的一些特性。获取内核地址仅仅是调用未公开的API ZwQuerySystemInformation,其中SYSTEM_INFORMATION_CLASS 结构体将会返回一个指针指向SYSTEM_MODULE_INFORMATION结构体,这个结构体包含了加载的模块的数量,之后是变长的数组SYSTEM_MODULE 的指针,值得一提的是 NtInternals documentation 中关于这个结构体的说明有一些过时,首先这两个域的数据类型为ULONG-PTR而不是32bit的 ULONG,查找内核基址和映像的大小就是简单的遍历SYSTEM_MODULE 数组,查找内核名字,代码如下
上面的函数返回PSYSTEM_MODULE ,它指向的是内核信息块。现在内核基地址与映像大小知道了,接下来就是找到请求未公开的syscall的方法,因为所有的未公开的Zw* 调用等同于简单分装之后调用KiSystemService,现在展示下请求这个调用过程(通过创建一个功能上相同的模板)
在未分页的内存中,修补正确的地址(NullStub 替换了KiServiceLinkage)。然后请求KiSystemService(这里是通过移动64bit绝对地址到栈中,然后返回它),一旦在运行时完成修补,这个值就替换为相应的函数指针的值,然后像调用普通函数一样调用它,这里是该过程的一个展示
调用这个api的过程如下
在执行这些操作之前,KiServiceInternal 的地址需要获取,这样就可以很好得修补这些地址。某种角度上这就是为什么找到基地址的这么重要了。获取这个地址是通过扫描ntoskrnl内存KiServiceInternal 特征码来实现的,这个特征码必须充分的长而且比较特殊,但是也不能太长,因为这样就会花费更多的时间来查找,下面是该过程
这些工作都做完了,那么我们现在就开始调用任何我们想调用的Syscall。
======================
下面是一个例子,展示了怎样在目标进程虚拟地址空间中写入数据。这个进程将名字赋值给OpenProcess然后获取对应进程的EPROCESS块并且打开该进程的一个句柄,将这个句柄与未公开的api关联起来(比如进程操作之类(ZwProtectVirtualMemory/ZwWriteVirtualMemory)),一个内部未公开的函PsGetNextProcess)需要被获取,用它来帮助枚举进程。
#include "stdafx.h"
#include "Undocumented.h"
#include
#ifdef __cplusplus
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);
#endif
pPsGetProcessImageFileName PsGetProcessImageFileName;
pPsGetProcessSectionBaseAddress PsGetProcessSectionBaseAddress;
pPsGetNextProcess PsGetNextProcess;
pZwSuspendProcess ZwSuspendProcess;
pZwResumeProcess ZwResumeProcess;
pZwProtectVirtualMemory ZwProtectVirtualMemory;
pZwWriteVirtualMemory ZwWriteVirtualMemory;
pKiSystemService KiSystemService;
#ifdef _M_IX86
__declspec(naked) VOID SyscallTemplate(VOID) {
__asm {
/*B8 XX XX XX XX */ mov eax, 0xC0DE
/*8D 54 24 04 */ lea edx, [esp + 0x4]
/*9C */ pushfd
/*6A 08 */ push 0x8
/*FF 15 XX XX XX XX*/ call KiSystemService
/*C2 XX XX */ retn 0xBBBB
}
}
#elif defined(_M_AMD64)
BYTE NullStub = 0xC3;
BYTE SyscallTemplate[] =
{
0x48, 0x8B, 0xC4, /*mov rax, rsp*/
0xFA, /*cli*/
0x48, 0x83, 0xEC, 0x10, /*sub rsp, 0x10*/
0x50, /*push rax*/
0x9C, /*pushfq*/
0x6A, 0x10, /*push 0x10*/
0x48, 0xB8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, /*mov rax, NullStubAddress*/
0x50, /*push rax*/
0xB8, 0xBB, 0xBB, 0xBB, 0xBB, /*mov eax, Syscall*/
0x68, 0xCC, 0xCC, 0xCC, 0xCC, /*push LowBytes*/
0xC7, 0x44, 0x24, 0x04, 0xCC, 0xCC, 0xCC, 0xCC, /*mov [rsp+0x4], HighBytes*/
0xC3 /*ret*/
};
#endif
PVOID FindFunctionInModule(IN CONST BYTE *Signature,
IN ULONG SignatureSize,
IN PVOID KernelBaseAddress,
IN ULONG ImageSize) {
BYTE *CurrentAddress = 0;
ULONG i = 0;
DbgPrint("+ Scanning from %p to %p\n", KernelBaseAddress, (ULONG_PTR)KernelBaseAddress + ImageSize);
CurrentAddress = (BYTE *)KernelBaseAddress;
DbgPrint("+ Scanning from %p to %p\n", KernelBaseAddress, (ULONG_PTR)KernelBaseAddress + ImageSize);
CurrentAddress = (BYTE *)KernelBaseAddress;
for(i = 0; i < ImageSize; ++i) {
if(RtlCompareMemory(CurrentAddress, Signature, SignatureSize) == SignatureSize) {
DbgPrint("+ Found function at %p\n", CurrentAddress);
return (PVOID)CurrentAddress;
}
++CurrentAddress;
}
return NULL;
}
NTSTATUS ResolveFunctions(IN PSYSTEM_MODULE KernelInfo) {
UNICODE_STRING PsGetProcessImageFileNameStr = {0};
UNICODE_STRING PsGetProcessSectionBaseAddressStr = {0};
#ifdef _M_IX86
CONST BYTE PsGetNextProcessSignature[] =
{
0x8B, 0xFF, 0x55, 0x8B, 0xEC, 0x51, 0x83, 0x65,
0xFC, 0x00, 0x56, 0x57, 0x64, 0xA1, 0x24, 0x01, 0x00, 0x00,
0x8B, 0xF0, 0xFF, 0x8E, 0xD4, 0x00, 0x00, 0x00, 0xB9, 0xC0,
0x38, 0x56, 0x80, 0xE8, 0xB4, 0xEE, 0xF6, 0xFF, 0x8B, 0x45,
0x08, 0x85, 0xC0
};
#elif defined(_M_AMD64)
CONST BYTE PsGetNextProcessSignature[] =
{
0x48, 0x89, 0x5C, 0x24, 0x08, 0x48, 0x89, 0x6C, 0x24, 0x10,
0x48, 0x89, 0x74, 0x24, 0x18, 0x57, 0x41, 0x54, 0x41, 0x55,
0x41, 0x56, 0x41, 0x57, 0x48, 0x83, 0xEC, 0x20, 0x65, 0x48,
0x8B, 0x34, 0x25, 0x88, 0x01, 0x00, 0x00, 0x45, 0x33, 0xED,
0x48, 0x8B, 0xF9, 0x66, 0xFF, 0x8E, 0xC6, 0x01, 0x00, 0x00,
0x4D, 0x8B, 0xE5, 0x41, 0x8B, 0xED, 0x41, 0x8D, 0x4D, 0x11,
0x33, 0xC0,
};
#endif
#ifdef _M_IX86
CONST BYTE KiSystemServiceSignature[] =
{
0x6A, 0x00, 0x55, 0x53, 0x56, 0x57, 0x0F, 0xA0, 0xBB, 0x30,
0x00, 0x00, 0x00, 0x66, 0x8E, 0xE3, 0x64, 0xFF, 0x35, 0x00,
0x00, 0x00, 0x00
};
#elif defined(_M_AMD64)
CONST BYTE KiSystemServiceSignature[] =
{
0x48, 0x83, 0xEC, 0x08, 0x55, 0x48, 0x81, 0xEC, 0x58, 0x01,
0x00, 0x00, 0x48, 0x8D, 0xAC, 0x24, 0x80, 0x00, 0x00, 0x00,
0x48, 0x89, 0x9D, 0xC0, 0x00, 0x00, 0x00, 0x48, 0x89, 0xBD,
0xC8, 0x00, 0x00, 0x00, 0x48, 0x89, 0xB5, 0xD0, 0x00, 0x00,
0x00, 0xFB, 0x65, 0x48, 0x8B, 0x1C, 0x25, 0x88, 0x01, 0x00,
0x00
};
#endif
RtlInitUnicodeString(&PsGetProcessImageFileNameStr, L"PsGetProcessImageFileName");
RtlInitUnicodeString(&PsGetProcessSectionBaseAddressStr, L"PsGetProcessSectionBaseAddress");
PsGetProcessImageFileName = (pPsGetProcessImageFileName)MmGetSystemRoutineAddress(&PsGetProcessImageFileNameStr);
if(PsGetProcessImageFileName == NULL) {
DbgPrint("- Could not find PsGetProcessImageFileName\n");
return STATUS_UNSUCCESSFUL;
}
DbgPrint("+ Found PsGetProcessImageFileName at %p\n", PsGetProcessImageFileName);
PsGetProcessSectionBaseAddress = (pPsGetProcessSectionBaseAddress)MmGetSystemRoutineAddress(&PsGetProcessSectionBaseAddressStr);
if(PsGetProcessSectionBaseAddress == NULL) {
DbgPrint("- Could not find PsGetProcessSectionBaseAddress\n");
return STATUS_UNSUCCESSFUL;
}
DbgPrint("+ Found PsGetProcessSectionBaseAddress at %p\n", PsGetProcessSectionBaseAddress);
PsGetNextProcess = (pPsGetNextProcess)FindFunctionInModule(PsGetNextProcessSignature,
sizeof(PsGetNextProcessSignature), KernelInfo->ImageBaseAddress, KernelInfo->ImageSize);
if(PsGetNextProcess == NULL) {
DbgPrint("- Could not find PsGetNextProcess\n");
return STATUS_UNSUCCESSFUL;
}
DbgPrint("+ Found PsGetNextProcess at %p\n", PsGetNextProcess);
KiSystemService = (pKiSystemService)FindFunctionInModule(KiSystemServiceSignature,
sizeof(KiSystemServiceSignature), KernelInfo->ImageBaseAddress, KernelInfo->ImageSize);
if(KiSystemService == NULL) {
DbgPrint("- Could not find KiSystemService\n");
return STATUS_UNSUCCESSFUL;
}
DbgPrint("+ Found KiSystemService at %p\n", KiSystemService);
return STATUS_SUCCESS;
}
VOID OnUnload(IN PDRIVER_OBJECT DriverObject) {
DbgPrint("+ Unloading\n");
}
PSYSTEM_MODULE GetKernelModuleInfo(VOID) {
PSYSTEM_MODULE SystemModule = NULL;
PSYSTEM_MODULE FoundModule = NULL;
ULONG_PTR SystemInfoLength = 0;
PVOID Buffer = NULL;
ULONG Count = 0;
ULONG i = 0;
ULONG j = 0;
//Other names for WinXP
CONST CHAR *KernelNames[] = { "ntoskrnl.exe", "ntkrnlmp.exe", "ntkrnlpa.exe", "ntkrpamp.exe" };
//Perform error checking on the calls in actual code
(VOID)ZwQuerySystemInformation(SystemModuleInformation, &SystemInfoLength, 0, &SystemInfoLength);
Buffer = ExAllocatePool(NonPagedPool, SystemInfoLength);
(VOID)ZwQuerySystemInformation(SystemModuleInformation, Buffer, SystemInfoLength, NULL);
Count = ((PSYSTEM_MODULE_INFORMATION)Buffer)->ModulesCount;
for(i = 0; i < Count; ++i) {
SystemModule = &((PSYSTEM_MODULE_INFORMATION)Buffer)->Modules[i];
for(j = 0; j < sizeof(KernelNames) / sizeof(KernelNames[0]); ++j) {
if(strstr((LPCSTR)SystemModule->Name, KernelNames[j]) != NULL) {
FoundModule = (PSYSTEM_MODULE)ExAllocatePool(NonPagedPool, sizeof(SYSTEM_MODULE));
RtlCopyMemory(FoundModule, SystemModule, sizeof(SYSTEM_MODULE));
ExFreePool(Buffer);
return FoundModule;
}
}
}
DbgPrint("Could not find the kernel in module list\n");
return NULL;
}
PEPROCESS GetEPROCESSFromName(IN CONST CHAR *ImageName) {
PEPROCESS ProcessHead = PsGetNextProcess(NULL);
PEPROCESS Process = PsGetNextProcess(NULL);
CHAR *ProcessName = NULL;
do {
ProcessName = PsGetProcessImageFileName(Process);
DbgPrint("+ Currently looking at %s\n", ProcessName);
if(strstr(ProcessName, ImageName) != NULL) {
DbgPrint("+ Found the process -- %s\n", ProcessName);
return Process;
}
Process = PsGetNextProcess(Process);
} while(Process != NULL && Process != ProcessHead);
DbgPrint("- Could not find %s\n", ProcessName);
return NULL;
}
HANDLE GetProcessIdFromEPROCESS(PEPROCESS Process) {
return PsGetProcessId(Process);
}
HANDLE OpenProcess(IN CONST CHAR *ProcessName, OUT OPTIONAL PEPROCESS *pProcess) {
HANDLE ProcessHandle = NULL;
CLIENT_ID ClientId = {0};
OBJECT_ATTRIBUTES ObjAttributes = {0};
PEPROCESS EProcess = GetEPROCESSFromName(ProcessName);
NTSTATUS Status = STATUS_UNSUCCESSFUL;
if(EProcess == NULL) {
return NULL;
}
InitializeObjectAttributes(&ObjAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
ObjAttributes.ObjectName = NULL;
ClientId.UniqueProcess = GetProcessIdFromEPROCESS(EProcess);
ClientId.UniqueThread = NULL;
Status = ZwOpenProcess(&ProcessHandle, PROCESS_ALL_ACCESS, &ObjAttributes, &ClientId);
if(!NT_SUCCESS(Status)) {
DbgPrint("- Could not open process %s. -- %X\n", ProcessName, Status);
return NULL;
}
if(pProcess != NULL) {
*pProcess = EProcess;
}
return ProcessHandle;
}
PVOID CreateSyscallWrapper(IN LONG Index, IN SHORT NumParameters) {
#ifdef _M_IX86
SIZE_T StubLength = 0x15;
PVOID Buffer = ExAllocatePool(NonPagedPool, StubLength);
BYTE *SyscallIndex = ((BYTE *)Buffer) + sizeof(BYTE);
BYTE *Retn = ((BYTE *)Buffer) + (0x13 * (sizeof(BYTE)));
RtlCopyMemory(Buffer, SyscallTemplate, StubLength);
NumParameters = NumParameters * sizeof(ULONG_PTR);
RtlCopyMemory(SyscallIndex, &Index, sizeof(LONG));
RtlCopyMemory(Retn, &NumParameters, sizeof(SHORT));
return Buffer;
#elif defined(_M_AMD64)
PVOID Buffer = ExAllocatePool(NonPagedPool, sizeof(SyscallTemplate));
BYTE *NullStubAddress = &NullStub;
BYTE *NullStubAddressIndex = ((BYTE *)Buffer) + (14 * sizeof(BYTE));
BYTE *SyscallIndex = ((BYTE *)Buffer) + (24 * sizeof(BYTE));
BYTE *LowBytesIndex = ((BYTE *)Buffer) + (29 * sizeof(BYTE));
BYTE *HighBytesIndex = ((BYTE *)Buffer) + (37 * sizeof(BYTE));
ULONG LowAddressBytes = ((ULONG_PTR)KiSystemService) & 0xFFFFFFFF;
ULONG HighAddressBytes = ((ULONG_PTR)KiSystemService >> 32);
RtlCopyMemory(Buffer, SyscallTemplate, sizeof(SyscallTemplate));
RtlCopyMemory(NullStubAddressIndex, (PVOID)&NullStubAddress, sizeof(BYTE *));
RtlCopyMemory(SyscallIndex, &Index, sizeof(LONG));
RtlCopyMemory(LowBytesIndex, &LowAddressBytes, sizeof(ULONG));
RtlCopyMemory(HighBytesIndex, &HighAddressBytes, sizeof(ULONG));
return Buffer;
#endif
}
VOID InitializeSyscalls(VOID) {
#ifdef _M_IX86
ZwSuspendProcess = (pZwSuspendProcess)CreateSyscallWrapper(0x00FD, 1);
ZwResumeProcess = (pZwResumeProcess)CreateSyscallWrapper(0x00CD, 1);
ZwProtectVirtualMemory = (pZwProtectVirtualMemory)CreateSyscallWrapper(0x0089, 5);
ZwWriteVirtualMemory = (pZwWriteVirtualMemory)CreateSyscallWrapper(0x0115, 5);
#elif defined(_M_AMD64)
ZwSuspendProcess = (pZwSuspendProcess)CreateSyscallWrapper(0x017A, 1);
ZwResumeProcess = (pZwResumeProcess)CreateSyscallWrapper(0x0144, 1);
ZwProtectVirtualMemory = (pZwProtectVirtualMemory)CreateSyscallWrapper(0x004D, 5);
ZwWriteVirtualMemory = (pZwWriteVirtualMemory)CreateSyscallWrapper(0x0037, 5);
#endif
}
VOID FreeSyscalls(VOID) {
ExFreePool(ZwSuspendProcess);
ExFreePool(ZwResumeProcess);
ExFreePool(ZwProtectVirtualMemory);
ExFreePool(ZwWriteVirtualMemory);
}
PVOID GetProcessBaseAddress(IN PEPROCESS Process) {
return PsGetProcessSectionBaseAddress(Process);
}
NTSTATUS WriteToProcessAddress(IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN BYTE *NewBytes, IN SIZE_T NewBytesSize) {
ULONG OldProtections = 0;
SIZE_T BytesWritten = 0;
SIZE_T NumBytesToProtect = NewBytesSize;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
//Needs error checking
Status = ZwSuspendProcess(ProcessHandle);
Status = ZwProtectVirtualMemory(ProcessHandle, &BaseAddress, &NumBytesToProtect, PAGE_EXECUTE_READWRITE, &OldProtections);
Status = ZwWriteVirtualMemory(ProcessHandle, BaseAddress, NewBytes, NewBytesSize, &BytesWritten);
Status = ZwProtectVirtualMemory(ProcessHandle, &BaseAddress, &NumBytesToProtect, OldProtections, &OldProtections);
Status = ZwResumeProcess(ProcessHandle);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) {
PSYSTEM_MODULE KernelInfo = NULL;
PEPROCESS Process = NULL;
HANDLE ProcessHandle = NULL;
PVOID BaseAddress = NULL;
BYTE NewBytes[0x100] = {0};
NTSTATUS Status = STATUS_UNSUCCESSFUL;
DbgPrint("+ Driver successfully loaded\n");
DriverObject->DriverUnload = OnUnload;
KernelInfo = GetKernelModuleInfo();
if(KernelInfo == NULL) {
DbgPrint("Could not find kernel module\n");
return STATUS_UNSUCCESSFUL;
}
DbgPrint("+ Found kernel module.\n"
"+ Name: %s -- Base address: %p -- Size: %p\n", KernelInfo->Name,
KernelInfo->ImageBaseAddress, KernelInfo->ImageSize);
if(!NT_SUCCESS(ResolveFunctions(KernelInfo))) {
return STATUS_UNSUCCESSFUL;
}
InitializeSyscalls();
ProcessHandle = OpenProcess("notepad.exe", &Process);
if(ProcessHandle == NULL) {
return STATUS_UNSUCCESSFUL;
}
BaseAddress = GetProcessBaseAddress(Process);
if(BaseAddress == NULL) {
return STATUS_UNSUCCESSFUL;
}
DbgPrint("Invoking\n");
RtlFillMemory(NewBytes, sizeof(NewBytes), 0x90);
(VOID)WriteToProcessAddress(ProcessHandle, BaseAddress, NewBytes, sizeof(NewBytes));
DbgPrint("+ Done\n");
ExFreePool(KernelInfo);
FreeSyscalls();
ZwClose(ProcessHandle);
return STATUS_SUCCESS;
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工
作,每周日13:00-18:00直播授课