本文所贴出的PoC代码将告诉你如何通过CreateProcess创建一个傀儡进程(称之为可执行程序A),并把dwCreationFlags设置为CREATE_SUSPENDED,然后把另一个可执行程序(称之为可执行程序B)的内容加载到所创建的进程空间中,最终借用傀儡进程(A)的外壳来执行可执行程序B的内容。同时这段代码也会告诉你如何手工对Win32可执行程序进行重定位处理,以及如何从进程空间中取消已经映射的EXE映像。
在Windows操作系统下,通过给CreateProcess传递一个CREATE_SUSPENDED参数可以使得被创建的进程处于挂起状态,此时EXE的映像会被加载到进程空间中,但是并不会立即被执行,除非调用ResumeThread。在调用ResumeThread之前,可以通过调用ReadProcessMemory和WriteProcessMemory这样的API来读写进程空间的内容,这使得这样一种情形成为可能:通过读取另一个可执行文件的内容来覆盖刚创建的处于挂起状态的进程的空间,然后通过原始进程的空间来执行第二个可执行程序的内容。可以通过如下的步骤来完成这个过程:
通过给CreateProcess传递CREATE_SUSPENDED参数来创建一个处于挂起状态的进程(该进程为可执行程序A的一次执行)
通过调用GetThreadContext来获取被挂起进程的CONTEXT。其中ebx寄存器指向进程的PEB,eax寄存器的值为进程的入口点地址。
通过PEB来获取进程的基地址,如[EBX + 8]
把第二个可执行程序(B)加载到内存中并进行对齐处理,如果文件对齐尺寸和内存对齐尺寸不一致的话,这个操作是必须的。
如果可执行程序B和进程A有相同基地址并且B的内存镜像尺寸小于进程A的内存镜像尺寸,那么只需要简单的调用WriteProcessMemory来把可执行程序B的镜像写到进程A的内存空间之中,并从基地址开始执行即可。
否则的话,需要通过调用ZwUnmapViewOfSection(由ntdll.dll导出)来取消可执行程序A的映像映射,然后通过VirtualAllocEx在进程A中为可执行程序B分配足够的空间。调用VirtualAllocEx的时候,必须提供可执行程序B的基址以确保所分配的空间是从指定的位置开始的。然后把可执行程序B的镜像复制到进程A的内存空间并从分配的空间的起始地址开始执行。
如果ZwUnmapViewOfSection操作失败,而可执行程序B时可重定位的(存在重定位表),那么可以再进程A的内存空间的任意位置为B分配足够的空间,然后基于所分配的空间进行为B进行重定位处理,然后把可执行程序B的镜像复制到进程A的内存空间并从分配的空间的起始地址开始执行。
把进程A的PEB中的基地址改为可执行程序B的基地址。
把线程上下文中的EAX寄存器的值设置为可执行程序B的入口点。
通过调用SetThreadContext来设置线程的上下文。
通过调用ResumeThread来执行被挂起的进程。
PoC展示的技术点:
手工对可执行程序进行重定位处理(如果存在有重定位表的话)。
使用ZwUnmapViewOfSection取消原始EXE的内存映像的映射。
使用ReadProcessMemory和WriteProcessMemory来读写别的进程的内存空间。
通过修改进程的PEB来修改其基地址的值。
使用方法:傀儡进程默认为calc.exe(计算器),命令行下输入 loadEXE.exe
int main(int argc, char* argv[])
{
if(argc != 2)
{
printf("\nUsage: %s <EXE filename>\n", argv[0]);
return 1;
}
FILE *fp = fopen(argv[1], "rb");
if(fp)
{
MZHeader mzH;
PE_Header peH;
PE_ExtHeader peXH;
SectionHeader *secHdr;
if(readPEInfo(fp, &mzH, &peH, &peXH, &secHdr))
{
int imageSize = calcTotalImageSize(&mzH, &peH, &peXH, secHdr);
//printf("Image Size = %X\n", imageSize);
LPVOID ptrLoc = VirtualAlloc(NULL, imageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if(ptrLoc)
{
//printf("Memory allocated at %X\n", ptrLoc);
loadPE(fp, &mzH, &peH, &peXH, secHdr, ptrLoc);
doFork(&mzH, &peH, &peXH, secHdr, ptrLoc, imageSize);
}
else
printf("Allocation failed\n");
}
fclose(fp);
}
else
printf("\nCannot open the EXE file!\n");
return 0;
}