欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[翻译]调用内核中未公开的API(x64平台)

[翻译]调用内核中未公开的API(x64平台)

   昨天晚上发现这篇文章不错,适合新手看,所以就翻译了下,第一次翻译翻译不当的地方,希望大家指出来啊
===============================
     这篇有别于其他的复杂深入研究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;

}

上传的附件 图片2.png
图片3.png
图片4.png
图片5.png
图片6.png
图片7.png
图片8.png
1.png
图片1.png
图片9.png

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功