[笔记]Windows核心编程《番外篇》常用的NT API及使用示例

本文详细介绍了Windows NTAPI中几个关键函数的使用,如NtQueryInformationProcess、NtSetInformationProcess、ZwCreateThreadEx、NtQueryVirtualMemory,用于获取进程路径、设置进程属性、创建线程、查询内存信息等。示例代码展示了如何利用这些API获取PEB信息、绕过DEP、获取进程映像文件名和内存保护模式等。
摘要由CSDN通过智能技术生成

前言

NTAPI:泛指ntdll.dll模块不对外提供的API接口,大多是提供给内核层开发人员使用的。

一般通过动态载入库的方式(LoadLibrary + GetProcAddress)调用

注意:本文主要使用phlib库作为调用NTAPI的库

NtQueryInformationProcess函数

NtQueryInformationProcess
NtQueryInformationProcess用法

函数原型

作用:
通过ProcessInformationClass参数可以查找进程的不同信息,包括PEB信息、WOW64信息、子系统信息、imageFileName映像文件名信息等。

NTSYSCALLAPI
NTSTATUS
NTAPI
NtQueryInformationProcess(
    _In_ HANDLE ProcessHandle,
    _In_ PROCESSINFOCLASS ProcessInformationClass,
    _Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation,
    _In_ ULONG ProcessInformationLength,
    _Out_opt_ PULONG ReturnLength
    );

ProcessHandle:进程句柄
ProcessInformationClass:需要检索的进程信息类型,具体参数类型,见本节结尾“进程信息类型表”。

如果第二个参数是ProcessBasicInformation的话,则第三个参数必须为一个指针指向结构PROCESS_BASIC_INFORMATION:

ProcessInformation: 缓冲指针,保存进程信息,大小取决于进程信息类型。
ProcessInformationLength:以字节为单位的缓冲大小
ReturnLength:实际写入到缓冲的字节数

返回值
返回一个NTSTATUS成功或错误代码

附录

进程信息类型表

含义
ProcessBasicInformation 0返回PEB结构指针,检索判断特定进程是否正在被调试
ProcessDebugPort 7返回DWORD指针,检索获得当前进程的调试端口号
ProcessWow64Information 26判断进程是否运行在WOW64环境(WOW64指基于Win32的程序运行在x64系统上)
ProcessImageFileName 27返回Unicode字符串,包含进程的映像文件名
ProcessBreakOnTermination 29返回ULONG值, 判断是否这个进程被认为是重要进程的。(一般系统级别的exe都是重要进程)
ProcessSubsystemInformation 75返回SUBSYSTEM_INFORMATION_TYPE,指出进程的子系统类型。

当ProcessInformationClass为ProcessBasicInformation时:
缓冲区指针 应该指向 _PROCESS_BASIC_INFORMATION

typedef struct
{	  
	  DWORD ExitStatus; // 接收进程终止状态
      DWORD PebBaseAddress; // 接收PEB进程环境块地址
      DWORD AffinityMask; // 接收进程关联掩码
      DWORD BasePriority; // 接收进程的优先级类
      ULONG UniqueProcessId; // 接收进程ID
      ULONG InheritedFromUniqueProcessId; //接收父进程ID
} PROCESS_BASIC_INFORMATION;

示例:从PEB32获得进程路径

代码参考:
CSDN博主「MailSloter」的原创文章

/**
*
* 获得PEB的进程路径
*
*/

#include <phnt_windows.h>
#include <phnt.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>


BOOL GetProcessFullPathByProcessID(ULONG32 ProcessID, WCHAR* BufferData, ULONG BufferLegnth) {
	HANDLE	ProcessHandle = NULL; 
	ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessID);

	if (ProcessHandle == NULL) {
		return FALSE;
	}

	
	PROCESS_BASIC_INFORMATION	pbi = { 0 };
	SIZE_T	ReturnLength = 0;
	NTSTATUS status = NtQueryInformationProcess(ProcessHandle, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION),
		(PULONG)&ReturnLength);
	
	if (!NT_SUCCESS(status)) {
		CloseHandle(ProcessHandle);
		ProcessHandle = NULL;
		return FALSE;
	}

	PEB	Peb = { 0 };
	status = NtReadVirtualMemory(ProcessHandle, pbi.PebBaseAddress, &Peb, sizeof(PEB32), (SIZE_T*)&ReturnLength);

	if (!NT_SUCCESS(status)) {
		CloseHandle(ProcessHandle);
		ProcessHandle = NULL;
		return FALSE;
	}

	RTL_USER_PROCESS_PARAMETERS RtlUserProcessParameters = { 0 };
	status = NtReadVirtualMemory(ProcessHandle, Peb.ProcessParameters, &RtlUserProcessParameters,
		sizeof(RTL_USER_PROCESS_PARAMETERS), (SIZE_T*)&ReturnLength);

	if (!NT_SUCCESS(status)) {
		CloseHandle(ProcessHandle);
		ProcessHandle = NULL;
		return FALSE;
	}

	if (RtlUserProcessParameters.ImagePathName.Buffer != NULL)
	{
		ULONG v1 = 0;
		if (RtlUserProcessParameters.ImagePathName.Length < BufferLegnth)
		{
			v1 = RtlUserProcessParameters.ImagePathName.Length;
		}
		else
		{
			v1 = BufferLegnth - 10;
		}
		status = ReadProcessMemory(ProcessHandle, RtlUserProcessParameters.ImagePathName.Buffer,
			BufferData, v1, (SIZE_T*)&ReturnLength);
		if (!NT_SUCCESS(status)) 
		{
			CloseHandle(ProcessHandle);
			ProcessHandle = NULL;
			return FALSE;
		}
	}

	CloseHandle(ProcessHandle);
	return TRUE;
}

int main() {
	BOOL bOk = FALSE;
	ULONG32 ProcessID = 0;
	WCHAR   BufferData[MAX_PATH] = { 0 };
	//定义完整路径数组,
	//windows规定存放完整路径的数组最大为260个字节;
	printf("Input Process ID\r\n");
	scanf_s("%d", &ProcessID);
	bOk = GetProcessFullPathByProcessID(ProcessID, BufferData, MAX_PATH);
	//用自定义函数实现从进程ID得到进程完整路径的过程(进程ID,完整路径(存放的数组名),数组长度)

	std::cout << bOk << std::endl;
	if (bOk == TRUE)
	{
		printf("%S\r\n", BufferData);
		//BufferData双字,故用大S输出字符串;
	}

	system("pause");
	return 0;
}

NtSetInformationProcess函数

参考 SetProcessInformation function msdn

设置指定进程的信息

函数原型

BOOL SetProcessInformation(
  [in] HANDLE                    hProcess,
  [in] PROCESS_INFORMATION_CLASS ProcessInformationClass,
       LPVOID                    ProcessInformation,
  [in] DWORD                     ProcessInformationSize
);

示例:使进程跳过DEP检测 关闭DEP

代码可参考

ZwCreateThreadEx函数

函数原型

NtQueryVirtualMemory

解密NtQueryVirtualMemory
msdn NtQueryVirtualMemor

查询指定进程的某个虚拟地址控件所在的内存对象的一些信息。

函数原型

NTSTATUS NTAPI NtQueryVirtualMemory(
          IN HANDLE               	ProcessHandle,     			//目标进程句柄
          IN PVOID                	BaseAddress,     			//目标内存地址
          IN MEMORY_INFORMATION_CLASS 	MemoryInformationClass,  		//查询内存信息的类别
          OUT PVOID               	Buffer,       				//用于存储获取到的内存信息的结构地址
          IN ULONG                	Length,       				//Buffer的最大长度
          OUT PULONG              	ResultLength OPTIONAL); 		//存储该函数处理返回的信息的长度的ULONG的地址
);

第一个参数是目标进程的句柄;
第二个参数是要查询的内存地址;
第四个参数是根据第三个参数选用不同的结构去接收内存信息的地址。
第五个和第六个参数为Buffer长度,和函数处理结果返回的长度。

第三个参数MEMORY_INFORMATION_CLASS 表示需要获取内存的信息的类型

类型有以下几种:

//MEMORY_INFORMATION_CLASS定义
typedef enum _MEMORY_INFORMATION_CLASS
{
 MemoryBasicInformation,     		//内存基本信息
 MemoryWorkingSetInformation,   	//工作集信息
 MemoryMappedFilenameInformation   	//内存映射文件名信息
} MEMORY_INFORMATION_CLASS;

MemoryBasicInformation

内存基本信息

typedef struct _MEMORY_BASIC_INFORMATION {
    PVOID 		BaseAddress;
    PVOID 		AllocationBase;
    DWORD 		AllocationProtect;
    SIZE_T 		RegionSize;
    DWORD 		State;
    DWORD 		Protect;
    DWORD 		Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

代码使用可参考 DllFinder Process::ListModulesVQ

MemoryWorkingSetInformation

工作集信息

typedef struct _MEMORY_WORKING_SET_INFORMATION {
	ULONG		SizeOfWorkingSet;
	DWORD		WsEntries[ANYSIZE_ARRAY];
} MEMORY_WORKING_SET_INFORMATION, *PMEMORY_WORKING_SET_INFORMATION;

MemoryMappedFilenameInformation

内存映射文件名信息

#define _MAX_OBJECT_NAME 1024/sizeof(WCHAR)

typedef struct _MEMORY_MAPPED_FILE_NAME_INFORMATION {
 UNICODE_STRING Name;
 WCHAR     Buffer[_MAX_OBJECT_NAME];
} MEMORY_MAPPED_FILE_NAME_INFORMATION, *PMEMORY_MAPPED_FILE_NAME_INFORMATION; 

示例:获取进程映像文件名

示例:查询内存保护模式类型 PAGE_EXECUTE_READWRITE

int main() {
	DWORD dwProcessId = 33700;
	HANDLE hProcess;
	NTSTATUS status = PhNtOpenProcess(&hProcess,
		PROCESS_QUERY_INFORMATION |
		PROCESS_VM_READ, dwProcessId);
	if (!NT_SUCCESS(status)) {
		status = PhNtOpenProcess(&hProcess,
			PROCESS_QUERY_INFORMATION, dwProcessId);
		if (!NT_SUCCESS(status))
			return -1;
	}

	void* pBaseAddres = NULL;

	MEMORY_BASIC_INFORMATION basicInfo;
	while (NT_SUCCESS(NtQueryVirtualMemory(
		hProcess, pBaseAddres, MemoryBasicInformation, &basicInfo,
		sizeof(MEMORY_BASIC_INFORMATION), NULL))) {
		ULONG dwState = basicInfo.State;

		DWORD protectStatus = basicInfo.Protect;

		if (protectStatus == PAGE_EXECUTE_READWRITE) {
			printf("memery PAGE_EXECUTE_READWRITE exist! \n");
			break;
		}

		pBaseAddres = PTR_ADD_OFFSET(pBaseAddres, basicInfo.RegionSize);
	}

	return 0;
}

NtReadVirtualMemory

函数原型

读取进程内存内容

NTSTATUS
NtReadVirtualMemory (
     IN HANDLE ProcessHandle,//进程句柄
     IN PVOID BaseAddress,//基地址
     OUT PVOID Buffer,//输出的内容
     IN SIZE_T BufferSize,//输出大小
     OUT PSIZE_T NumberOfBytesRead OPTIONAL//读取字节数
)

示例:简单用法

int main() {
	int adrnum = 0;
	DWORD dwPid = 26392;
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, dwPid);
	NtReadVirtualMemory(hProcess, (PVOID)0x90000, (LPVOID)&adrnum, 4, 0);
	CloseHandle(hProcess);
	printf("%d\n", adrnum);

	system("pause");
	return 0;
}

NtQuerySystemInformation

查询到特定的系统信息

函数原型

NTSYSCALLAPI
NTSTATUS
NTAPI
NtQuerySystemInformation(
    _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
    _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
    _In_ ULONG SystemInformationLength,
    _Out_opt_ PULONG ReturnLength
    );

第一个参数 SystemInformationClass: 所要查询的系统信息的类型
第二个参数 SystemInformation:输出的系统信息内容的指针

第三个参数 SystemInformationLength:接受长度

第四个参数 ReturnLength:实际返回长度

示例:查询系统基本信息

SYSTEM_BASIC_INFORMATION systemBasicInformation = {};

NTSTATUS status = NtQuerySystemInformation(
	SystemBasicInformation, &systemBasicInformation, sizeof(SYSTEM_BASIC_INFORMATION), NULL);

NtQueryInformationThread

检索指定的线程信息。

函数原型

NTSYSCALLAPI
NTSTATUS
NTAPI
NtQueryInformationThread(
    _In_ HANDLE ThreadHandle,//线程句柄
    _In_ THREADINFOCLASS ThreadInformationClass, //查询线程信息的类型
    _Out_writes_bytes_(ThreadInformationLength) PVOID ThreadInformation, //接受线程信息的指针
    _In_ ULONG ThreadInformationLength,//线程信息长度
    _Out_opt_ PULONG ReturnLength//返回长度
    );

示例:获得线程起始地址

#include <phnt_windows.h>
#include <phnt.h>
#include <stdio.h>

int main()
{		
		DWORD dwThreadId = GetCurrentThreadId();

		HANDLE hThread = NULL;
		hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, dwThreadId);
		if (!hThread)
		{
			return -1;
		}

		DWORD dwStaAddr = NULL;
		DWORD dwReturnLength = 0;

		NTSTATUS status = NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress,
			&dwStaAddr, sizeof(dwStaAddr), &dwReturnLength);

		if (!NT_SUCCESS(status))
		{
			return -1;
		}

		printf("0x%p", dwStaAddr);

		return 0;
}

示例:通过线程获得进程ID

THREAD_BASIC_INFORMATION threadBasicInfo;
NtQueryInformationThread(hThread, ThreadBasicInformation, &threadBasicInfo, sizeof(threadBasicInfo), &dwReturnLength);
printf("processId:%d", threadBasicInfo.ClientId.UniqueProcess);

总结

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二进制怪兽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值