修改指定进程PEB中路径和命令行信息实现进程伪装

ilovehim

发布日期: 2019-01-23 19:44:48 浏览量: 600
评分:
star star star star star star star star star_border star_border
*转载请注明来自write-bug.com

背景

所谓的进程伪装,指的修改任意一个指定进程的信息,是它的信息在系统中的显示是另一个进程的信息,这样看来,指定的进程就像是被伪装的进程一样,因为进程信息相同,但实际上,它还是原来的进程,做着原来的进程操作。

本文就是要介绍进程伪装的技术,实现在 32 位系统和 64 位系统上的进程伪装。现在,我就把实现过程和原理整理成文档,分享给大家。

函数介绍

NtQueryInformationProcess 函数

[NtQueryInformationProcess可能在Windows的未来版本中更改或不可用]
获取指定进程的信息。

函数声明

  1. NTSTATUS WINAPI NtQueryInformationProcess(
  2. _In_ HANDLE ProcessHandle,
  3. _In_ PROCESSINFOCLASS ProcessInformationClass,
  4. _Out_ PVOID ProcessInformation,
  5. _In_ ULONG ProcessInformationLength,
  6. _Out_opt_ PULONG ReturnLength
  7. );

参数

  • ProcessHandle [in]
    要获取信息的进程的句柄。

  • ProcessInformationClass [in]
    要获取的进程信息的类型。 该参数可以是PROCESSINFOCLASS枚举中的以下值之一:

VALUE MEANING
ProcessBasicInformation 检索指向PEB结构的指针,该结构可用于确定是否正在调试指定的进程,以及系统用于标识指定进程的唯一值
ProcessDebugPort 获取作为进程调试器的端口号的DWORD_PTR值。 非零值表示该进程正在Ring3调试器的控制下运行
ProcessWow64Information 确定进程是否在WOW64环境中运行(WOW64是允许基于Win32的应用程序在64位Windows上运行的x86模拟器)
ProcessImageFileName 检索包含该进程的映像文件名称的UNICODE_STRING值
ProcessBreakOnTermination 检索指示进程是否被视为关键的ULONG值。注意可以在具有SP3的Windows XP中启动该值。 从Windows 8.1开始,应该使用IsProcessCritical
ProcessSubsystemInformation 检索指示进程的子系统类型的SUBSYSTEM_INFORMATION_TYPE值。 ProcessInformation参数指向的缓冲区应足够大以容纳单个SUBSYSTEM_INFORMATION_TYPE枚举
  • ProcessInformation [out]
    指向由调用应用程序提供的缓冲区的指针,函数写入请求的信息。 所写信息的大小取决于ProcessInformationClass参数的数据类型:

  • PROCESS_BASIC_INFORMATION
    当ProcessInformationClass参数为ProcessBasicInformation时,ProcessInformation参数指向的缓冲区应足够大,以容纳具有以下布局的单个PROCESS_BASIC_INFORMATION结构:

    1. typedef struct _PROCESS_BASIC_INFORMATION {
    2. PVOID Reserved1;
    3. PPEB PebBaseAddress;
    4. PVOID Reserved2[2];
    5. ULONG_PTR UniqueProcessId;
    6. PVOID Reserved3;
    7. } PROCESS_BASIC_INFORMATION;

    UniqueProcessId成员指向该过程的系统唯一标识符。 最好使用GetProcessId函数来检索这些信息。
    PebBaseAddress成员指向PEB结构。
    该结构的其他成员保留供操作系统内部使用。

  • ProcessInformationLength [in]
    ProcessInformation参数指向的缓冲区的大小(以字节为单位)。

  • ReturnLength [out,optional]
    指向变量的指针,其中函数返回所请求信息的大小。 如果函数成功,这是由ProcessInformation参数指向缓冲区的信息的大小,但是如果缓冲区太小,这是成功接收信息所需的最小缓冲区大小。

返回值

  • 该函数返回一个NTSTATUS成功或错误代码。
  • NTSTATUS错误代码的形式和意义在DDK中提供的Ntstatus.h头文件中列出,并在DDK文档中介绍,内核模式驱动程序架构/设计指南/驱动程序编程技术/日志记录错误。

注意

  • NtQueryInformationProcess函数及其返回的结构在操作系统内部,并可能会从一个版本的Windows更改为另一个版本。 为了保持应用程序的兼容性,最好使用ProcessInformationClass参数描述中提到的公共函数。
  • 如果您使用NtQueryInformationProcess,请通过运行时动态链接访问该函数。 如果功能已被更改或从操作系统中删除,这将使您的代码有机会正常响应。 但签名变更可能无法检测。
  • 此功能没有关联的导入库。 您必须使用LoadLibrary和GetProcAddress函数动态链接到Ntdll.dll。

实现原理

进程伪装的原理不是很复杂,就是修改指定进程的进程环境块 PEB 中的进程路径以及命令行信息,即可达到进程伪装的效果。

具体实现原理分析如下:

  • 首先,我们根据进程的 PID 号打开指定进程,并获取进程的句柄。

  • 然后,我们要从 ntdll.dll 中获取 NtQueryInformationProcess 函数的导出地址,因为 NtQueryInformationProcess 函数没有关联导入库,所以只能动态获取,这个函数是这个程序功能实现的关键步骤。

  • 接着,使用 NtQueryInformationProcess 函数获取指定进程的进程基本信息 PROCESS_BASIC_INFORMATION,并从中获取指定进程的进程环境块 PEB。

  • 然后,我们就可以根据进程环境块中的 ProcessParameters,获取指定进程的 RTL_USER_PROCESS_PARAMETERS 信息,因为PEB的路径信息、命令行信息存储在这个结构体中。

  • 最后,我们开始对指定进程 PEB 中路径信息、命令行信息进行更改,实现进程伪装。

编码实现

  1. // 修改指定进程的进程环境块PEB中的路径和命令行信息, 实现进程伪装
  2. BOOL DisguiseProcess(DWORD dwProcessId, wchar_t *lpwszPath, wchar_t *lpwszCmd)
  3. {
  4. // 打开进程获取句柄
  5. HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
  6. if (NULL == hProcess)
  7. {
  8. ShowError("OpenProcess");
  9. return FALSE;
  10. }
  11. typedef_NtQueryInformationProcess NtQueryInformationProcess = NULL;
  12. PROCESS_BASIC_INFORMATION pbi = { 0 };
  13. PEB peb = { 0 };
  14. RTL_USER_PROCESS_PARAMETERS Param = { 0 };
  15. USHORT usCmdLen = 0;
  16. USHORT usPathLen = 0;
  17. // 需要通过 LoadLibrary、GetProcessAddress 从 ntdll.dll 中获取地址
  18. NtQueryInformationProcess = (typedef_NtQueryInformationProcess)::GetProcAddress(
  19. ::LoadLibrary("ntdll.dll"), "NtQueryInformationProcess");
  20. if (NULL == NtQueryInformationProcess)
  21. {
  22. ShowError("GetProcAddress");
  23. return FALSE;
  24. }
  25. // 获取指定进程的基本信息
  26. NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
  27. if (!NT_SUCCESS(status))
  28. {
  29. ShowError("NtQueryInformationProcess");
  30. return FALSE;
  31. }
  32. /*
  33. 注意在读写其他进程的时候,注意要使用ReadProcessMemory/WriteProcessMemory进行操作,
  34. 每个指针指向的内容都需要获取,因为指针只能指向本进程的地址空间,必须要读取到本进程空间。
  35. 要不然一直提示位置访问错误!
  36. */
  37. // 获取指定进程进本信息结构中的PebBaseAddress
  38. ::ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);
  39. // 获取指定进程环境块结构中的ProcessParameters, 注意指针指向的是指定进程空间中
  40. ::ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL);
  41. // 修改指定进程环境块PEB中命令行信息, 注意指针指向的是指定进程空间中
  42. usCmdLen = 2 + 2 * ::wcslen(lpwszCmd);
  43. ::WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, usCmdLen, NULL);
  44. ::WriteProcessMemory(hProcess, &Param.CommandLine.Length, &usCmdLen, sizeof(usCmdLen), NULL);
  45. // 修改指定进程环境块PEB中路径信息, 注意指针指向的是指定进程空间中
  46. usPathLen = 2 + 2 * ::wcslen(lpwszPath);
  47. ::WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, usPathLen, NULL);
  48. ::WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &usPathLen, sizeof(usPathLen), NULL);
  49. return TRUE;
  50. }

程序测试

我们运行一个测试程序 Demon Memory.exe,先使用 Process Explorer 查看它的进程信息:

现在,我们运行我们的进程伪装程序,修改上述的测试程序 Demon Memory.exe 进程的PEB进程环境块信息,实现进程伪装。程序执行提示成功,我们再运行 Process Explorer 程序查看进程信息,发现测试程序 Demon Memory.exe 进程信息成功伪装了 explorer.exe 资源管理器进程。

总结

这个程序的原理不难理解,但是需要重点注意一个地方,这个地方我在开发这个程序的时候,也犯了相同的错误,就是:一定要区分指针指向的是指定进程的空间还是自己本程序的空间,对于指向其它进程空间,则一律使用 ReadProcessMemory 和 WriteProcessMemory 函数进行数据读写。

同时,也要注意,如果程序运行在 64 位系统上,那么程序就要编译为 64 位程序;如果程序运行在 32 位系统上,则程序要编译为 32 位程序,否则程序不能伪装成功。

参考

参考自《Windows黑客编程技术详解》一书

上传的附件 cloud_download DisguiseProcess_Test.7z ( 271.61kb, 6次下载 )

发送私信

岁月极美,在于它必然的流逝

12
文章数
11
评论数
最近文章
eject