前言
源于滴水3期的项目要求,一个基于傀儡进程的壳框架,在写壳的过程中遇到了不少问题,如果不自己来写一遍的话是记不住这些错误的
归根结底,纸上得来终觉浅,绝知此事要躬行。
因为是个壳框架,所以不写加密解密的具体实现,只写框架的设计思路
暂未完成对于VirtualAllocEx申请空间失败的处理
只对32位固定地址程序有效
设计思路


1、读取主模块的数据
2、解密:得到原来的PE文件
3、以挂起的形式创建进程:CreateProcess
要创建的进程,就是壳子本身!
4、获取外壳程序的Context,后面要用
5、卸载外壳程序.
6、在指定的位置分配空间:位置就是src的ImageBase ,大小就是Src的SizeOfImage
7、如果成功,将Src的PE文件拉伸 复制到该空间中
8、如果申请空间失败,但有重定位表:在任意位置申请空间,然后将PE文件拉伸、复制、修复重定位表。
9、如果第6步申请空间失败,并且还没有重定位表,直接返回:失败.
10、修改外壳程序的Context:
将Context的ImageBase 改成 Src的ImageBase
将Context的OEP 改成 Src的OEP
11、设置Context 并恢复主线程
12、终止外壳程序,解壳过程结束.
错误处理
这个错误处理为什么要写在前面?
因为遇到的错误才是真正对我有帮助的,如果顺风顺水到会让我干到不安,这是错误处理,也是项目经验,不论做什么事情,错误处理终究是最重要的
1、C05错误:
这是我遇到的第一个问题,这个问题源于ASLR地址随机化,卸载进程用到的NtUnmapViewOfSection会因安全而触发C05错误,解决方法就是关闭地址随机化
2、加壳后没有报错,但也没有任何显示: 这个问题排查了好久,网上也找不到什么线索,检查进程发现有一条线程的转发量极高,且有一个模块地址为0xCDCDCDCD,猜测debug版内存空间默认填写int3中断,编译为release版后解决 但这治标不治本,我回到debug版将申请的空间memwet为0后解决了这个问题,但很明显这不是问题的关键,我怀疑是某个数组溢出导致的,等我找到问题所在再补充
3、NtUnmapViewOfSection卸载的时候第二个参数是什么: 第二个参数应该是远程进程的ImageBase,不一定是PE文件中的0x400000,可能是其他加载地址,不能直接写0x400000,需要ReadProcessMemory(contxPacker.Ebx + 8)获取远程进程的ImageBase
核心代码
由于是基于傀儡进程的壳,所以需要额外的加壳器将目标程序加密后写入壳源
加壳器
加壳器的设计思路就是将目标程序加密后直接加入壳源的新增节,这部分没什么难点,具体代码卸载文章末尾处
壳源
在这一块我想了许久,壳源是如何将目标程序成功加载的
在前面的课程中重点提到了以挂起方式创建进程,项目中流程也提到了这一步,但如何具体实现?
我想了个词来描述这个过程:偷天换日
因为这个壳的思路就是用目标程序的内存镜像替换掉宿主的映像并把线程上下文改为新的入口点,从而让宿主进程最终运行被替换的程序代码
那么需要解决的问题就具体化了:
- 目标程序是否需要拉伸后再复制到宿主进程的内存空间中
- 以什么方式创建进程,进程信息如何获取
- 如何卸载原宿主进程
- 如何修改进程的上下文
- 如何将目标程序的内存镜像复制到准确的空间(目标程序的ImageBase+OEP)中
- 如何恢复宿主进程的线程
这里直接给出答案:
- 是
- 以挂起(CREATE_SUSPENDED)方式创建进程,通过STARTUPINFO和PROCESS_INFORMATION结构体获取进程信息
NtUnmapViewOfSection函数可以卸载宿主进程的映像CONTEXT结构体存储线程的上下文信息,其中CONTEXT.Eax指向OEP,CONTEXT.Ebx+8指向ImageBaseVirtualAllocEx可以在指定的位置分配内存,WriteProcessMemory可以将目标程序的内存镜像复制到该空间中ResumeThread恢复执行线程
代码实现
- 以挂起方式创建进程,并保存进程信息
STARTUPINFO siPackerProc = { 0 }; // 进程启动信息
PROCESS_INFORMATION piPackerProc; // 进程信息
siPackerProc.cb = sizeof(siPackerProc); //这一步必须初始化
if (CreateProcess(
NULL, // 指向一个NULL结尾的、用来指定可执行模块的宽字节字符串
szExeFullPath, // 命令行字符串
NULL, // 指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。
NULL, // 如果lpProcessAttributes参数为空(NULL),那么句柄不能被继承。<同上>
false,// 指示新进程是否从调用进程处继承了句柄。
CREATE_SUSPENDED, // 指定附加的、用来控制优先类和进程的创建的标
// CREATE_NEW_CONSOLE 新控制台打开子进程
// CREATE_SUSPENDED 子进程创建后挂起,直到调用ResumeThread函数
NULL, // 指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境
NULL, // 指定子进程的工作路径
&siPackerProc, // 决定新进程的主窗体如何显示的STARTUPINFO结构体
&piPackerProc // 接收新进程的识别信息的PROCESS_INFORMATION结构体
))
{
MessageBox(0, TEXT("挂起创建程序成功!!!"), 0, 0);
CONTEXT contxPacker;
contxPacker.ContextFlags = CONTEXT_FULL;
GetThreadContext(piPackerProc.hThread, &contxPacker);
//获取入口点
DWORD dwEntryPoint = contxPacker.Eax;
//获取ImageBase
char* baseAddress = (CHAR*)contxPacker.Ebx + 8;
memset(szBuffer, 0, 256);
if (!ReadProcessMemory(piPackerProc.hProcess, baseAddress, szBuffer, 4, NULL))
{
MessageBox(0, "ReadProcessMemory failed", "Error", 0);
return FALSE;
}
ResumeThread(piPackerProc.hThread); // 恢复执行
}
- 卸载原宿主进程的映像
在这里出现问题了,我无法直接使用NtUnmapViewOfSection函数,该函数未文档化,需要查阅一些资料
有点麻烦,需要从ntdll.dll中导出该函数,自己创建 NtUnmapViewOfSection 原型(用户态通过 ntdll 导出),我直接照抄文章中的代码了
// 创建 NtUnmapViewOfSection 原型
typedef ULONG(WINAPI* PFNNtUnmapViewOfSection)(IN HANDLE ProcessHandle, IN PVOID BaseAddress);
HMODULE hNtModule = GetModuleHandle("ntdll.dll");
if (hNtModule == NULL)
{
hNtModule = LoadLibrary("ntdll.dll");
if (hNtModule == NULL)
{
MessageBox(0, "ntdll find faied!!!", 0, 0);
return FALSE;
}
}
PFNNtUnmapViewOfSection pfnNtUnmapViewOfSection = (PFNNtUnmapViewOfSection)GetProcAddress(hNtModule, "NtUnmapViewOfSection");
if (pfnNtUnmapViewOfSection == NULL)
{
return FALSE;
}
pfnNtUnmapViewOfSection(piPackerProc.hProcess, (PVOID)baseAddress);
- 在指定位置分配内存,并将目标程序的内存镜像复制到该空间中
使用VirtualAllocEx函数分配内存,并使用WriteProcessMemory函数将目标程序的内存镜像复制到该空间中
VirtualAllocEx会返回申请的内存的起始地址
- setp1:在指定位置分配内存
LPVOID lpPuppetProcessBaseAddr = nullptr;
lpPuppetProcessBaseAddr = VirtualAllocEx(
piPackerProc.hProcess,
(LPVOID)SrcStruct.pNTHeader->OptionalHeader.ImageBase,
SrcStruct.pNTHeader->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
if (lpPuppetProcessBaseAddr == nullptr) {
MessageBox(0, "重新分配内存 failed", "Error", 0);
return FALSE;
}
- setp2:将文件拉伸并写入内存
FileToImage(lpSrcBuffer, SrcStruct, &lpSrcImageBuffer);
nRet = WriteProcessMemory(piPackerProc.hProcess, lpPuppetProcessBaseAddr, lpSrcImageBuffer, SrcStruct.pNTHeader->OptionalHeader.SizeOfImage, NULL);
if (!nRet)
{
MessageBox(0, "WriteProcessMemory", "Error", 0);
return FALSE;
}
- 修改外壳程序的Context
将Context的ImageBase(Ebx+8) 改成 Src的ImageBase 将Context的OEP(Eax) 改成 Src的OEP
PVOID pImageBase = (PVOID)SrcStruct.pNTHeader->OptionalHeader.ImageBase;
nRet = WriteProcessMemory(piPackerProc.hProcess, (LPVOID)(contxPacker.Ebx + 8), &pImageBase, sizeof(PVOID), NULL);
if (!nRet)
{
MessageBox(0, "修改ImageBase失败", "Error", 0);
return FALSE;
}
contxPacker.Eax = SrcStruct.pNTHeader->OptionalHeader.ImageBase + SrcStruct.pNTHeader->OptionalHeader.AddressOfEntryPoint;
- 设置Context 并恢复主线程
if (!SetThreadContext(piPackerProc.hThread, &contxPacker)) {
MessageBox(NULL, "SetThreadContext 失败", "Error", MB_OK);
return FALSE;
}
ResumeThread(piPackerProc.hThread); // 恢复执行
- 释放申请的空间
if(lpPackerBuffer) delete[] static_cast<BYTE*>(lpPackerBuffer);
if(lpSrcBuffer) delete[] static_cast<BYTE*>(lpSrcBuffer);
if(lpSrcImageBuffer) delete[] static_cast<BYTE*>(lpSrcImageBuffer);
完整代码
加壳器
#include <Windows.h>
#include <iostream>
#include <vector>
struct PEstruct {
PIMAGE_DOS_HEADER pDosHeader = nullptr;
PIMAGE_NT_HEADERS pNTHeader = nullptr;
PIMAGE_FILE_HEADER pFileHeader = nullptr;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = nullptr;
PIMAGE_SECTION_HEADER pSectionHeader = nullptr;
};
PEstruct* pPackerStruct = nullptr;
PEstruct* pNewStruct = nullptr;
DWORD dwPackerSize = NULL;
DWORD dwSrcSize = NULL;
DWORD dwNewSize = NULL;
static inline DWORD align_up(DWORD x, DWORD a) {
return (a == 0) ? x : ((x + a - 1) / a) * a;
}
DWORD BufferToFile(IN LPCSTR lpszFile, IN LPVOID pFileBuffer, IN DWORD FileSize) {
FILE* pFile = fopen(lpszFile, "wb");
if (!pFile) {
printf("创建文件失败\n");
return 0;
}
int nWriteNum = fwrite(pFileBuffer, 1, FileSize, pFile);
if (nWriteNum == 0) {
fclose(pFile);
printf("写入文件失败\n");
return 0;
}
else printf("写入0x%x字节\n", nWriteNum);
fclose(pFile);
}
DWORD RvaToRaw(IN DWORD Rva, IN PEstruct* pFEStruct) {
// 遍历节表寻找Rva在哪个节里
for (int i = 0; i < pFEStruct->pFileHeader->NumberOfSections; i++) {
if (pFEStruct->pSectionHeader[i].VirtualAddress <= Rva && pFEStruct->pSectionHeader[i].VirtualAddress + pFEStruct->pSectionHeader[i].SizeOfRawData >= Rva) {
return Rva - pFEStruct->pSectionHeader[i].VirtualAddress + pFEStruct->pSectionHeader[i].PointerToRawData;
}
}
return -1;
}
BOOL ReadFileToBuffer(IN TCHAR* szFilePath, OUT LPVOID* WriteBuffer, OUT DWORD& nSize)
{
LPVOID pTempBuffer = nullptr;
int nFileSize = NULL;
int ret = NULL;
// 读取文件
FILE* pFile = fopen(szFilePath, "rb");
if (!pFile)
{
MessageBox(NULL, "fopen", "Error", NULL);
return FALSE;
}
ret = fseek(pFile, 0, SEEK_END);
if (ret != 0) {
MessageBox(NULL, "fseek", "Error", NULL);
fclose(pFile);
return FALSE;
}
nFileSize = ftell(pFile);
if (nFileSize == 0) {
MessageBox(NULL, "ftell", "Error", NULL);
fclose(pFile);
return FALSE;
}
ret = fseek(pFile, 0, SEEK_SET);
if (ret != 0) {
MessageBox(NULL, "fseek", "Error", NULL);
fclose(pFile);
return FALSE;
}
pTempBuffer = new BYTE[nFileSize];
if (pTempBuffer == nullptr) {
MessageBox(NULL, "new", "Error", NULL);
fclose(pFile);
return FALSE;
}
size_t nRead = fread(pTempBuffer, 1, nFileSize, pFile);
if (nRead != nFileSize) {
MessageBox(NULL, "fread", "Error", NULL);
delete[] static_cast<BYTE*>(pTempBuffer);
fclose(pFile);
return FALSE;
}
fclose(pFile);
nSize = nFileSize;
*WriteBuffer = pTempBuffer;
return TRUE;
}
VOID Analysis(IN LPVOID pFileBuffer, OUT PEstruct** pPEstruct) {
// PE头开始分析
PEstruct* pTempPEStruct = nullptr;
pTempPEStruct = new PEstruct();
pTempPEStruct->pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
//pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
if (pTempPEStruct->pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
std::cout << "不是有效的MZ标志" << std::endl;
return;
}
pTempPEStruct->pNTHeader = PIMAGE_NT_HEADERS((DWORD)pFileBuffer + pTempPEStruct->pDosHeader->e_lfanew);
if (pTempPEStruct->pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
std::cout << "不是有效的PE标志" << std::endl;
}
pTempPEStruct->pFileHeader = &pTempPEStruct->pNTHeader->FileHeader;
pTempPEStruct->pOptionHeader = &pTempPEStruct->pNTHeader->OptionalHeader;
pTempPEStruct->pSectionHeader = PIMAGE_SECTION_HEADER((DWORD)pTempPEStruct->pOptionHeader + pTempPEStruct->pFileHeader->SizeOfOptionalHeader);
*pPEstruct = pTempPEStruct;
}
VOID AddSrcToPacker(IN LPVOID pPackerBuffer, IN LPVOID pSrcBuffer,OUT LPVOID* pNewBuffer)
{
LPVOID pTempNewBuffer = nullptr;
DWORD PackerAlignment = 0;
DWORD dwFileAlignment = NULL;
DWORD dwSectionAlignment = NULL;
DWORD lastVirtEnd = NULL;
if (80 > pPackerStruct->pOptionHeader->SizeOfHeaders -
(pPackerStruct->pDosHeader->e_lfanew + 24
+ pPackerStruct->pFileHeader->SizeOfOptionalHeader
+ 40 * pPackerStruct->pFileHeader->NumberOfSections)) {
printf("节表空间不足\n");
return;
}
dwFileAlignment = pPackerStruct->pOptionHeader->FileAlignment;
dwSectionAlignment = pPackerStruct->pOptionHeader->SectionAlignment;
dwNewSize = align_up(dwPackerSize + align_up(dwSrcSize, pPackerStruct->pOptionHeader->FileAlignment), dwFileAlignment);
pTempNewBuffer = new BYTE[dwNewSize];
if (!pTempNewBuffer) {
return;
}
memset(pTempNewBuffer, 0, dwNewSize);
memcpy(pTempNewBuffer, pPackerBuffer, dwPackerSize);
memcpy((void*)((DWORD)pTempNewBuffer + dwPackerSize), pSrcBuffer, dwSrcSize);
IMAGE_SECTION_HEADER NewSection;
memset(&NewSection, 0, sizeof(NewSection));
lastVirtEnd = pPackerStruct->pSectionHeader[pPackerStruct->pFileHeader->NumberOfSections - 1].Misc.VirtualSize
? pPackerStruct->pSectionHeader[pPackerStruct->pFileHeader->NumberOfSections - 1].VirtualAddress + pPackerStruct->pSectionHeader[pPackerStruct->pFileHeader->NumberOfSections - 1].Misc.VirtualSize
: pPackerStruct->pSectionHeader[pPackerStruct->pFileHeader->NumberOfSections - 1].VirtualAddress + pPackerStruct->pSectionHeader[pPackerStruct->pFileHeader->NumberOfSections - 1].SizeOfRawData;
BYTE Name[8] = "NewSec";
DWORD Characteristics = 0x60000020;
memcpy(NewSection.Name, Name, 8);
NewSection.Misc.VirtualSize = dwSrcSize;
NewSection.VirtualAddress = align_up(lastVirtEnd, dwSectionAlignment);
NewSection.SizeOfRawData = align_up(dwSrcSize, dwFileAlignment);
NewSection.PointerToRawData = align_up(pPackerStruct->pSectionHeader[pPackerStruct->pFileHeader->NumberOfSections - 1].SizeOfRawData + pPackerStruct->pSectionHeader[pPackerStruct->pFileHeader->NumberOfSections - 1].PointerToRawData,dwFileAlignment);
NewSection.Characteristics = Characteristics;
memcpy((BYTE*)pTempNewBuffer + pPackerStruct->pDosHeader->e_lfanew + 24 + pPackerStruct->pFileHeader->SizeOfOptionalHeader + 40 * pPackerStruct->pFileHeader->NumberOfSections, &NewSection, sizeof(NewSection));
Analysis(pTempNewBuffer, &pNewStruct);
pNewStruct->pOptionHeader->SizeOfImage = pPackerStruct->pOptionHeader->SizeOfImage + align_up(dwSrcSize, pPackerStruct->pOptionHeader->SectionAlignment);
pNewStruct->pFileHeader->NumberOfSections++;
*pNewBuffer = pTempNewBuffer;
}
int main() {
TCHAR szPackerFullPath[MAX_PATH];
TCHAR szSrcFullPath[MAX_PATH];
pPackerStruct = new PEstruct;
LPVOID pPackerBuffer = nullptr;
LPVOID pSrcBuffer = nullptr;
LPVOID pNewBuffer = nullptr;
// 判断是否申请空间成功
/*
code
*/
//确定路径
printf("请选择壳源的绝对路径:\n");
scanf("%s", szPackerFullPath);
printf("请选择程序的绝对路径:\n");
scanf("%s", szSrcFullPath);
ReadFileToBuffer(szPackerFullPath, &pPackerBuffer, dwPackerSize);
ReadFileToBuffer(szSrcFullPath, &pSrcBuffer, dwSrcSize);
Analysis(pPackerBuffer, &pPackerStruct);
AddSrcToPacker(pPackerBuffer, pSrcBuffer,&pNewBuffer);
BufferToFile("D:\\Note\\C\\DTdebug\\壳\\测试文件\\out.exe", pNewBuffer, dwNewSize);
system("pause");
}
壳源
#include <Windows.h>
#include <iostream>
struct PEstruct {
PIMAGE_DOS_HEADER pDosHeader = nullptr;
PIMAGE_NT_HEADERS pNTHeader = nullptr;
PIMAGE_SECTION_HEADER pSectionHeader = nullptr;
PIMAGE_EXPORT_DIRECTORY pExportTable = nullptr;
};
typedef ULONG(WINAPI* PFNNtUnmapViewOfSection)(IN HANDLE ProcessHandle, IN PVOID BaseAddress);
BOOL AnalysisPE(IN LPVOID pFileBuffer,OUT PEstruct* pStruct)
{
pStruct->pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(pFileBuffer);
pStruct->pNTHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(reinterpret_cast<uint8_t*>(pFileBuffer) + pStruct->pDosHeader->e_lfanew);
pStruct->pSectionHeader = reinterpret_cast<PIMAGE_SECTION_HEADER>(reinterpret_cast<uint8_t*>(&pStruct->pNTHeader->OptionalHeader) + pStruct->pNTHeader->FileHeader.SizeOfOptionalHeader);
return TRUE;
}
BOOL FileToImage(IN LPVOID pFileBuffer,IN PEstruct Struct,OUT LPVOID* ImageBuffer)
{
LPVOID pTempImageBuffer;
DWORD dwImageSize = Struct.pNTHeader->OptionalHeader.SizeOfImage;
pTempImageBuffer = new BYTE[dwImageSize];
if (pTempImageBuffer == nullptr)
{
MessageBox(0, "FileToImage 重新分配内存 failed", "Error", 0);
return FALSE;
}
memset(pTempImageBuffer, 0, dwImageSize);
memcpy(pTempImageBuffer, pFileBuffer, Struct.pNTHeader->OptionalHeader.SizeOfHeaders);
for (int i = 0; i < Struct.pNTHeader->FileHeader.NumberOfSections; i++) {
memcpy((void*)((DWORD)pTempImageBuffer + Struct.pSectionHeader[i].VirtualAddress), (void*)((DWORD)Struct.pDosHeader + Struct.pSectionHeader[i].PointerToRawData), Struct.pSectionHeader[i].SizeOfRawData);
}
*ImageBuffer = pTempImageBuffer;
pTempImageBuffer = nullptr;
return TRUE;
}
BOOL ReadFileToBuffer(IN TCHAR* szFilePath, OUT LPVOID* WriteBuffer, OUT int& nSize)
{
LPVOID pTempBuffer = nullptr;
int nFileSize = NULL;
int ret = NULL;
// 读取文件
FILE* pFile = fopen(szFilePath, "rb");
if (!pFile)
{
MessageBox(NULL, "fopen", "Error", NULL);
return FALSE;
}
ret = fseek(pFile, 0, SEEK_END);
if (ret != 0) {
MessageBox(NULL, "fseek", "Error", NULL);
fclose(pFile);
return FALSE;
}
nFileSize = ftell(pFile);
if (nFileSize == 0) {
MessageBox(NULL, "ftell", "Error", NULL);
fclose(pFile);
return FALSE;
}
ret = fseek(pFile, 0, SEEK_SET);
if (ret != 0) {
MessageBox(NULL, "fseek", "Error", NULL);
fclose(pFile);
return FALSE;
}
pTempBuffer = new BYTE[nFileSize];
if (pTempBuffer == nullptr) {
MessageBox(NULL, "new", "Error", NULL);
fclose(pFile);
return FALSE;
}
memset(pTempBuffer, 0, nFileSize);
size_t nRead = fread(pTempBuffer, 1, nFileSize, pFile);
if (nRead != nFileSize) {
MessageBox(NULL, "fread", "Error", NULL);
delete[] static_cast<BYTE*>(pTempBuffer);
fclose(pFile);
return FALSE;
}
fclose(pFile);
nSize = nFileSize;
*WriteBuffer = pTempBuffer;
pTempBuffer = nullptr;
return TRUE;
}
BOOL InjectProcess()
{
int nPackerFileSize = NULL;
int nSrcFileSize = NULL;
char szBuffer[256];
int nRet = 0;
PEstruct PackerStruct = {0};
PEstruct SrcStruct = { 0 };
TCHAR szExeFullPath[MAX_PATH] = {0};
LPVOID lpPackerBuffer = nullptr;
LPVOID lpSrcBuffer = nullptr;
LPVOID lpSrcBegin = nullptr;
LPVOID lpSrcImageBuffer = nullptr;
LPVOID lpPuppetProcessBaseAddr = nullptr;
STARTUPINFO siPackerProc = { 0 }; // 进程启动信息
PROCESS_INFORMATION piPackerProc = {}; // 进程信息
CONTEXT contxPacker = {};
//strcpy(szExeFullPath, "D:\\Note\\C\\DTdebug\\壳\\测试文件\\out.exe");
// 1、读取主模块的数据(壳源程序路径)
GetModuleFileName(NULL, szExeFullPath, MAX_PATH);
if (!ReadFileToBuffer(szExeFullPath, &lpPackerBuffer, nPackerFileSize)) {
return FALSE;
}
//strcpy(szExeFullPath, "D:\\Note\\C\\DTdebug\\壳\\测试文件\\out.exe");
AnalysisPE(lpPackerBuffer, &PackerStruct);
// 2、解密:得到原来的PE文件
// 先检查是否是加壳程序
if (0 != strcmp((char*)PackerStruct.pSectionHeader[PackerStruct.pNTHeader->FileHeader.NumberOfSections - 1].Name, "NewSec"))
{
MessageBox(NULL, "无可执行程序", "Error", NULL);
return FALSE;
}
nSrcFileSize = PackerStruct.pSectionHeader[PackerStruct.pNTHeader->FileHeader.NumberOfSections - 1].SizeOfRawData;
lpSrcBegin = reinterpret_cast<LPVOID>(reinterpret_cast<DWORD>(lpPackerBuffer) + PackerStruct.pSectionHeader[PackerStruct.pNTHeader->FileHeader.NumberOfSections - 1].PointerToRawData);
lpSrcBuffer = new BYTE[nSrcFileSize];
if (lpSrcBuffer == nullptr) {
MessageBox(NULL, "new lpSrcBuffer", "Error", NULL);
return FALSE;
}
memset(lpSrcBuffer, 0, nSrcFileSize);
memcpy(lpSrcBuffer, lpSrcBegin, nSrcFileSize);
// 3、以挂起的形式创建进程,获取进程的ImageBase和AddressOfEntryPoint
siPackerProc.cb = sizeof(siPackerProc); //这一步必须初始化
if (CreateProcess(
NULL, // 指向一个NULL结尾的、用来指定可执行模块的宽字节字符串
szExeFullPath, // 命令行字符串
NULL, // 指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被子进程继承。
NULL, // 如果lpProcessAttributes参数为空(NULL),那么句柄不能被继承。<同上>
false,// 指示新进程是否从调用进程处继承了句柄。
CREATE_SUSPENDED, // 指定附加的、用来控制优先类和进程的创建的标
// CREATE_NEW_CONSOLE 新控制台打开子进程
// CREATE_SUSPENDED 子进程创建后挂起,直到调用ResumeThread函数
NULL, // 指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境
NULL, // 指定子进程的工作路径
&siPackerProc, // 决定新进程的主窗体如何显示的STARTUPINFO结构体
&piPackerProc // 接收新进程的识别信息的PROCESS_INFORMATION结构体
))
{
//MessageBox(0, TEXT("挂起创建程序成功!!!"), 0, 0);
contxPacker.ContextFlags = CONTEXT_FULL;
GetThreadContext(piPackerProc.hThread, &contxPacker);
//获取入口点
DWORD dwEntryPoint = contxPacker.Eax;
//获取ImageBase
char* baseAddress = (CHAR*)contxPacker.Ebx + 8;
memset(szBuffer, 0, 256);
if (!ReadProcessMemory(piPackerProc.hProcess, baseAddress, szBuffer, 4, NULL))
{
MessageBox(0, "ReadProcessMemory failed", "Error", 0);
return FALSE;
}
// 5、卸载外壳程序
HMODULE hNtModule = GetModuleHandle("ntdll.dll");
if (hNtModule == NULL)
{
hNtModule = LoadLibrary("ntdll.dll");
if (hNtModule == NULL)
{
MessageBox(0, "ntdll find faied!!!", 0, 0);
return FALSE;
}
}
PFNNtUnmapViewOfSection pfnNtUnmapViewOfSection = (PFNNtUnmapViewOfSection)GetProcAddress(hNtModule, "NtUnmapViewOfSection");
if (pfnNtUnmapViewOfSection == NULL)
{
MessageBox(0, "GetProcAddress NtUnmapViewOfSection failed", "Error", 0);
return FALSE;
}
pfnNtUnmapViewOfSection(piPackerProc.hProcess, (PVOID) * (DWORD*)szBuffer);
// 6、在指定的位置分配空间:位置就是src的ImageBase 大小就是Src的SizeOfImage
AnalysisPE(lpSrcBuffer, &SrcStruct);
lpPuppetProcessBaseAddr = VirtualAllocEx(
piPackerProc.hProcess,
(LPVOID)SrcStruct.pNTHeader->OptionalHeader.ImageBase,
SrcStruct.pNTHeader->OptionalHeader.SizeOfImage,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE
);
if (lpPuppetProcessBaseAddr == nullptr) {
MessageBox(0, "重新分配内存 failed", "Error", 0);
return FALSE;
}
// 将目标程序拉伸后用WriteProcessMemory存到lpPuppetProcessBaseAddr
FileToImage(lpSrcBuffer, SrcStruct, &lpSrcImageBuffer);
nRet = WriteProcessMemory(piPackerProc.hProcess, lpPuppetProcessBaseAddr, lpSrcImageBuffer, SrcStruct.pNTHeader->OptionalHeader.SizeOfImage, NULL);
if (!nRet)
{
MessageBox(0, "WriteProcessMemory", "Error", 0);
return FALSE;
}
// 10、修改外壳程序的Context:
PVOID pImageBase = (PVOID)SrcStruct.pNTHeader->OptionalHeader.ImageBase;
nRet = WriteProcessMemory(piPackerProc.hProcess, (LPVOID)(contxPacker.Ebx + 8), &pImageBase, sizeof(PVOID), NULL);
if (!nRet)
{
MessageBox(0, "修改ImageBase失败", "Error", 0);
return FALSE;
}
contxPacker.Eax = SrcStruct.pNTHeader->OptionalHeader.ImageBase + SrcStruct.pNTHeader->OptionalHeader.AddressOfEntryPoint;
if (!SetThreadContext(piPackerProc.hThread, &contxPacker)) {
MessageBox(NULL, "SetThreadContext 失败", "Error", MB_OK);
return FALSE;
}
ResumeThread(piPackerProc.hThread); // 恢复执行
//MessageBox(0, "载入成功", 0, 0);
if(lpPackerBuffer) delete[] static_cast<BYTE*>(lpPackerBuffer);
if(lpSrcBuffer) delete[] static_cast<BYTE*>(lpSrcBuffer);
if(lpSrcImageBuffer) delete[] static_cast<BYTE*>(lpSrcImageBuffer);
}
else MessageBox(0, TEXT("挂起创建程序失败!!!"), 0, 0);
return TRUE;
}
int WINAPI WinMain(HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
)
{
return InjectProcess();
}