概述:windows操作系统 ntdll.dll 学习笔记

ntdll是Windows操作系统核心的一个dll,里边集成了大多数 windows api 所需要的接口。学习 ntdll 有助于我们快速了解 windows API。

[toc]

本文部分内容由 AI 生成,经人工修订。

0x01、认识 ntdll

使用三方工具可以看到,ntdll的导出函数有两千多个,从基本的输入输出到内存操作都覆盖。本文主要记录在工作学习中使用到的相关导出接口,用多少写多少。

0x02、关键内存函数

NtAllocateVirtualMemory

分配虚拟内存,是 VirtualAlloc 的底层实现。

NTSTATUS NtAllocateVirtualMemory(
    HANDLE ProcessHandle,           // 进程句柄,-1表示当前进程
    PVOID *BaseAddress,             // 期望的基地址(输入/输出)
    ULONG_PTR ZeroBits,             // 地址对齐限制
    PSIZE_T RegionSize,             // 分配大小(输入/输出)
    ULONG AllocationType,           // 分配类型:MEM_COMMIT | MEM_RESERVE
    ULONG Protect                   // 内存保护属性:PAGE_READWRITE等
);

示例:

PVOID pBase = NULL;
SIZE_T size = 0x1000;
NTSTATUS status = NtAllocateVirtualMemory(
    (HANDLE)-1,
    &pBase,
    0,
    &size,
    MEM_COMMIT | MEM_RESERVE,
    PAGE_EXECUTE_READWRITE
);

NtFreeVirtualMemory

释放虚拟内存,是 VirtualFree 的底层实现。

NTSTATUS NtFreeVirtualMemory(
    HANDLE ProcessHandle,
    PVOID *BaseAddress,
    PSIZE_T RegionSize,
    ULONG FreeType                  // MEM_DECOMMIT | MEM_RELEASE
);

NtProtectVirtualMemory

修改内存保护属性,是 VirtualProtect 的底层实现。

NTSTATUS NtProtectVirtualMemory(
    HANDLE ProcessHandle,
    PVOID *BaseAddress,
    PSIZE_T NumberOfBytesToProtect,
    ULONG NewAccessProtection,
    PULONG OldAccessProtection
);

NtReadVirtualMemory / NtWriteVirtualMemory

读写进程内存,是 ReadProcessMemory / WriteProcessMemory 的底层实现。

NTSTATUS NtReadVirtualMemory(
    HANDLE ProcessHandle,
    PVOID BaseAddress,
    PVOID Buffer,
    SIZE_T NumberOfBytesToRead,
    PSIZE_T NumberOfBytesReaded
);
 
NTSTATUS NtWriteVirtualMemory(
    HANDLE ProcessHandle,
    PVOID BaseAddress,
    PVOID Buffer,
    SIZE_T NumberOfBytesToWrite,
    PSIZE_T NumberOfBytesWritten
);

NtQueryVirtualMemory

查询内存信息,是 VirtualQuery 的底层实现。

NTSTATUS NtQueryVirtualMemory(
    HANDLE ProcessHandle,
    PVOID BaseAddress,
    MEMORY_INFORMATION_CLASS MemoryInformationClass,
    PVOID MemoryInformation,
    SIZE_T MemoryInformationLength,
    PSIZE_T ReturnLength
);

0x03、模块与函数查找

LdrLoadDll

加载 DLL,是 LoadLibrary 的底层实现。

NTSTATUS LdrLoadDll(
    PWSTR DllPath,                  // DLL搜索路径
    PULONG DllCharacteristics,      // 特征标志
    PUNICODE_STRING DllName,        // DLL名称
    PVOID *DllHandle                // 返回模块句柄
);

LdrGetProcedureAddress

获取导出函数地址,是 GetProcAddress 的底层实现。

NTSTATUS LdrGetProcedureAddress(
    PVOID DllHandle,                // 模块句柄
    ANSI_STRING *ProcedureName,     // 函数名(可选)
    ULONG ProcedureNumber,          // 函数序号(可选)
    PVOID *ProcedureAddress         // 返回函数地址
);

示例:

HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
FARPROC pFunc = GetProcAddress(hNtdll, "NtAllocateVirtualMemory");
 
// 或使用 LdrGetProcedureAddress
UNICODE_STRING ustrDllName;
RtlInitUnicodeString(&ustrDllName, L"ntdll.dll");
PVOID hModule = NULL;
LdrLoadDll(NULL, NULL, &ustrDllName, &hModule);
 
ANSI_STRING asFuncName;
RtlInitAnsiString(&asFuncName, "NtAllocateVirtualMemory");
PVOID pFuncAddr = NULL;
LdrGetProcedureAddress(hModule, &asFuncName, 0, &pFuncAddr);

LdrGetDllHandle

获取已加载 DLL 的句柄,不增加引用计数。

NTSTATUS LdrGetDllHandle(
    PWSTR DllPath,
    PULONG DllCharacteristics,
    PUNICODE_STRING DllName,
    PVOID *DllHandle
);

0x04、线程函数

NtCreateThread

创建线程,是 CreateThread 的底层实现。

NTSTATUS NtCreateThread(
    PHANDLE ThreadHandle,           // 返回线程句柄
    ACCESS_MASK DesiredAccess,      // 访问权限
    POBJECT_ATTRIBUTES ObjectAttributes,
    HANDLE ProcessHandle,           // 目标进程句柄
    PCLIENT_ID ClientId,            // 返回线程ID
    PCONTEXT ThreadContext,         // 线程上下文
    PVOID InitialTeb,               // 初始TEB
    BOOLEAN CreateSuspended         // 是否挂起创建
);

NtCreateThreadEx

扩展版的线程创建函数,支持更多参数。

NTSTATUS NtCreateThreadEx(
    PHANDLE ThreadHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    HANDLE ProcessHandle,
    LPTHREAD_START_ROUTINE StartRoutine,
    PVOID Argument,
    ULONG CreateFlags,              // 标志位
    ULONG_PTR ZeroBits,
    SIZE_T StackSize,
    SIZE_T MaximumStackSize,
    PPSS_ATTRIBUTE_LIST AttributeList
);

NtTerminateThread

终止线程。

NTSTATUS NtTerminateThread(
    HANDLE ThreadHandle,
    NTSTATUS ExitStatus
);

NtQueueApcThread

向线程队列添加 APC(异步过程调用)。

NTSTATUS NtQueueApcThread(
    HANDLE ThreadHandle,
    PVOID ApcRoutine,
    PVOID ApcArgument1,
    PVOID ApcArgument2,
    PVOID ApcArgument3
);

0x05、进入内核函数

Nt函数与Zw函数

ntdll 中每个系统调用都有两个导出:Nt*Zw*。在用户模式下,它们指向相同的系统服务存根;在内核模式下,行为有所不同:

  • Nt系列:原系统服务入口,会进行参数验证
  • Zw系列:内核模式下的”干净”调用路径,假设参数已验证
// 用户模式下,两者等价
NtCreateFile == ZwCreateFile  // 都通过 syscall 进入内核

系统调用机制

ntdll 中的系统调用通过 syscall(x64)或 sysenter(x86)指令进入内核:

x64 系统调用存根示例:

ntdll!NtCreateFile:
00007FF9`12340000 4C8BD1          mov     r10, rcx
00007FF9`12340003 B855000000      mov     eax, 55h      ; 系统服务号
00007FF9`12340008 0F05            syscall
00007FF9`1234000A C3              ret

KiFastSystemCall / KiIntSystemCall

早期的系统调用入口(32位系统):

// Windows XP/2003 等旧系统
__declspec(naked) void KiFastSystemCall() {
    __asm {
        mov edx, esp
        sysenter
    }
}

关键系统调用号(System Service Number)

系统调用号因 Windows 版本而异,可通过 NtQuerySystemInformation 获取:

函数Win10 19041Win11 22000
NtCreateFile0x550x57
NtReadFile0x060x06
NtWriteFile0x080x08
NtAllocateVirtualMemory0x180x18

动态获取系统调用号:

// 从 ntdll 的导出函数中解析 syscall 号
ULONG GetSyscallNumber(PCHAR FuncName) {
    HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    PUCHAR pFunc = (PUCHAR)GetProcAddress(hNtdll, FuncName);
    
    // x64: mov eax, 0xXX
    if (pFunc[0] == 0x4C && pFunc[1] == 0x8B && pFunc[2] == 0xD1) {
        // mov r10, rcx
        if (pFunc[3] == 0xB8) {
            return *(ULONG*)(pFunc + 4);
        }
    }
    return 0;
}

0x06、进程函数

NtCreateProcess

创建进程。

NTSTATUS NtCreateProcess(
    PHANDLE ProcessHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    HANDLE ParentProcess,
    BOOLEAN InheritObjectTable,
    HANDLE SectionHandle,
    HANDLE DebugPort,
    HANDLE ExceptionPort
);

NtTerminateProcess

终止进程。

NTSTATUS NtTerminateProcess(
    HANDLE ProcessHandle,
    NTSTATUS ExitStatus
);

NtOpenProcess

打开进程获取句柄。

NTSTATUS NtOpenProcess(
    PHANDLE ProcessHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    PCLIENT_ID ClientId              // 进程ID
);

0x07、其他常用函数

RtlInitUnicodeString / RtlInitAnsiString

初始化字符串结构。

VOID RtlInitUnicodeString(
    PUNICODE_STRING DestinationString,
    PCWSTR SourceString
);
 
VOID RtlInitAnsiString(
    PANSI_STRING DestinationString,
    PCSTR SourceString
);

NtQuerySystemInformation

查询系统信息,功能强大。

NTSTATUS NtQuerySystemInformation(
    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
);

常用信息类:

  • SystemProcessInformation - 进程列表
  • SystemModuleInformation - 内核模块列表
  • SystemHandleInformation - 系统句柄列表

NtQueryInformationProcess

查询进程信息。

NTSTATUS NtQueryInformationProcess(
    HANDLE ProcessHandle,
    PROCESSINFOCLASS ProcessInformationClass,
    PVOID ProcessInformation,
    ULONG ProcessInformationLength,
    PULONG ReturnLength
);

0x08、参考链接

ntdll导出接口说明

2024年8月1日09:27:57 更新

除了导出接口,也应该了解到系统调用表