目录
进程伪装
进程伪装,其实就是通过修改peb里的进程名,可以将当前进程的信息指向一个存在甚至是不存在的进程。比如当前以a.exe运行,在运行后修改peb里的进程名为b.exe,那么他人想要获取当前进程的路径时,获取到的就是b.exe,然而我们在本地还是a.exe,因此就无法正确获取到我们的进程信息了,这就达到了伪装的目的。关于peb(Process Envirorment Block Structure)详情可以查看微软文档:peb
使用API
NtQueryInformationProcess
ReadProcessMemory
WriteProcessMemory
OpenProcess
LoadGetModuleHandle
GetProcAddress
注意NtQueryInformationProcess是隐藏api,并未导出。所以需要从ntdll.dll中动态获取。
基本实现步骤
1:使用OpenProcess打开需要伪装的进程(需使用PROCESS_ALL_ACCESS权限打开)
2:从ntdll.dll中动态获取到NtQueryInformationProcess函数
3:使用NtQueryInformationProcess获取进程的pbi信息
4:使用ReadProcessMemory获取进程的peb信息和进程参数信息(包含进程路径的长度、命令行长度等)
5:使用WriteProcessMemory修改进程的路径和命令行信息
代码实现
disguise_process.h
#ifndef DISGUISE_PROCESS_H_
#define DISGUISE_PROCESS_H_
#include <windows.h>
/**
* 功能:伪造进程路径,进程命令行
* 参数:process_id:需要伪造的进程id,当前进程使用::GetCurrentProcessId()
* image_path:需要伪造的进程路径,不需要传null
* command_line:需要伪造的命令行参数,不需要传null
* 返回值:成功返回true,失败返回false
*/
bool DisguiseProcess(const DWORD process_id, const wchar_t* image_path, const wchar_t* command_line);
#endif // DISGUISE_PROCESS_H_
disguise_process.cpp
#include "disguise_process.h"
#include <winternl.h>
typedef
NTSTATUS(WINAPI* pfnNtQueryInformationProcess)
(HANDLE ProcessHandle, ULONG ProcessInformationClass,
PVOID ProcessInformation, UINT32 ProcessInformationLength,
UINT32* ReturnLength);
bool DisguiseProcess(const DWORD process_id, const wchar_t* image_path, const wchar_t* command_line) {
// 打开进程获取句柄
HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id);
if (hProcess == nullptr)
return false;
do {
pfnNtQueryInformationProcess NtQueryInformationProcess = nullptr;
PROCESS_BASIC_INFORMATION pbi = { 0 };
// 需要通过 GetModuleHandle、GetProcessAddress 从 ntdll.dll 中获取地址
NtQueryInformationProcess = (pfnNtQueryInformationProcess)::GetProcAddress(
::GetModuleHandle(L"ntdll.dll"), "NtQueryInformationProcess");
if (NtQueryInformationProcess == nullptr)
break;
// 获取指定进程的基本信息
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), nullptr);
if (!NT_SUCCESS(status))
break;
PEB peb = { 0 };
RTL_USER_PROCESS_PARAMETERS Param = { 0 };
/*
注意在读写其他进程的时候,注意要使用ReadProcessMemory/WriteProcessMemory进行操作,
每个指针指向的内容都需要获取,因为指针只能指向本进程的地址空间,必须要读取到本进程空间。
要不然一直提示位置访问错误!
*/
::ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), nullptr);
::ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), nullptr);
if (command_line) {
USHORT usCmdLen = (::wcslen(command_line) + 1) * sizeof(wchar_t);
::WriteProcessMemory(hProcess, Param.CommandLine.Buffer, command_line, usCmdLen, nullptr);
::WriteProcessMemory(hProcess, &Param.CommandLine.Length, &usCmdLen, sizeof(usCmdLen), nullptr);
}
if (image_path) {
USHORT usPathLen = (::wcslen(image_path) + 1) * sizeof(wchar_t);
::WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, image_path, usPathLen, nullptr);
::WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &usPathLen, sizeof(usPathLen), nullptr);
}
} while (0);
::CloseHandle(hProcess);
return true;
}
使用示例
调用DisguiseProcess传入pid、进程路径名、进程命令行即可,如果需要伪装当前进程,可以使用GetCurrentProcessId获取当前进程的pid。
#include <windows.h>
#include "disguise_process.h"
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
void main() {
wchar_t szPath[MAX_PATH] = { 0 };
::GetModuleFileName(NULL, szPath, MAX_PATH);
::PathRemoveFileSpec(szPath);
::PathAppend(szPath, L"Hash.exe");
STARTUPINFOW si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(STARTUPINFOW);
BOOL succeed = ::CreateProcess(
szPath,
L"",
nullptr,
nullptr,
FALSE,
CREATE_SUSPENDED,
nullptr,
nullptr,
&si,
&pi);
if (!succeed)
return;
DisguiseProcess(pi.dwProcessId, L"Test.exe", L"Test.exe");
::ResumeThread(pi.hThread);
}
注意事项
需要注意,由于peb结构所限制,修改后的进程名长度和命令行长度必须小于等于之前的,否则会出现一些未知问题。