-
-
[求助]滴水三期 壳子代码,请问大佬们,为啥会蓝屏啊
-
发表于: 2025-6-21 16:26 290
-
这里我试过了 加密,解密后拉伸,然后写到文件,没有问题,卸载过后,分配空间也没有问题,就是指定eip后蓝屏
void* shell::decryption(void* pePtr) {
// 读取这个壳程序最后的一个节
// 解密这个最后一个节
// 读取这个最后一个节,拉伸,并获取关键信息
// 开启一个新进程,挂起
// 获取线程上下文
// 卸载外壳程序的文件镜像(ZwUnmapViewOfSection)
// 在指定的位置(src的ImageBase)申请指定大小(src的SizeOfImage)的内存(VirtualAllocEx)
// 如果创建失败,查看src是否包含重定位表,如果包含重定位表,就在任意位置申请(src的SizeOfImage)大小的内存,然后修复重定位表.
// 如果在指定位置申请内存失败,并且没有重定位表的数据,直接返回失败.
// 如果内存申请成功,将新的数据复制到内存中
// 修正运行环境的基址和入口地址
// 恢复运行
IMAGE_SECTION_HEADER* sectionStart = getSectionHeadStart(pePtr);
DWORD numberOfSection = getNumberOfSections(pePtr);
IMAGE_SECTION_HEADER* lastSectionStart = sectionStart + (numberOfSection - 1);
char* decodeSrcPtr = (char*)((DWORD)pePtr + lastSectionStart->PointerToRawData);
DWORD encodeSrcSize = lastSectionStart->SizeOfRawData >= lastSectionStart->Misc.VirtualSize ? lastSectionStart->SizeOfRawData : lastSectionStart->Misc.VirtualSize;
// 解密
for (int i = 0; i < encodeSrcSize; i++) {
*(decodeSrcPtr + i) ^= 0x66;
}
// 拉伸
void* srcPePtr = stretchFile((void*)decodeSrcPtr);
// 挂起方式启动一个EXE
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi;
si.cb = sizeof(STARTUPINFO);
TCHAR szBuffer[256] = TEXT("C:\\Windows\\SysWOW64\\notepad.exe");
CreateProcess(szBuffer, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
CONTEXT contx;
contx.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &contx);
// 卸载
typedef LONG(WINAPI* ZWUNMAPVIEWOFSECTION)(HANDLE, PVOID);
ZWUNMAPVIEWOFSECTION ZwUnmapViewOfSection =
(ZWUNMAPVIEWOFSECTION)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "ZwUnmapViewOfSection");
PVOID baseAddress = GetRemoteImageBase(pi.hProcess);
LONG status = ZwUnmapViewOfSection(pi.hProcess, baseAddress);
if (status != 0) {
std::cout << "ZwUnmapViewOfSection failed, status: " << std::hex << status << "\n";
return NULL;
}
std::cout << "映像卸载成功,现在可以 VirtualAllocEx + WriteProcessMemory 写入你解密的PE 了。\n";
DWORD srcImageBase = getImageBase(srcPePtr);
DWORD fullSize = getSizeOfImage(srcPePtr);
DWORD oep = getOEPPoint(srcPePtr);
//分配空间
LPVOID allocAddress = VirtualAllocEx(
pi.hProcess, // pi 是 PROCESS_INFORMATION 中的进程句柄
(LPVOID)srcImageBase, // 建议分配的地址(可以是 imageBase 或 NULL)
fullSize, // 映像大小(从原始 PE 文件读取)
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE // 可读可写可执行
);
if (!allocAddress) {
std::cout << "VirtualAllocEx 失败,错误码:" << GetLastError() << std::endl;
// 判断是否有重定位表,如果有重定位表,就随机分配空间,没有重定位表直接报错
//分配空间
allocAddress = VirtualAllocEx(
pi.hProcess, // pi 是 PROCESS_INFORMATION 中的进程句柄
NULL, // 建议分配的地址(可以是 imageBase 或 NULL) NULL 是随机分配
fullSize, // 映像大小(从原始 PE 文件读取)
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE // 可读可写可执行
);
boolean flag = rebuildRelocationTable(srcPePtr, (DWORD)allocAddress);
if (flag == FALSE) {
return NULL;
}
}
else {
std::cout << "在目标进程中分配内存成功,地址:" << allocAddress << std::endl;
}
// 写入新开辟的内存中
SIZE_T bytesWritten = 0;
BOOL success = WriteProcessMemory(pi.hProcess, // 目标进程句柄
allocAddress,// 远程进程中要写入的地址
srcPePtr,// 本地缓冲区(解密后的原始 PE 文件)
fullSize, // 要写入的字节数(完整映像)
&bytesWritten);
if (!success || bytesWritten != fullSize) {
std::cout << "写入失败,写入字节:" << bytesWritten << " / " << fullSize << std::endl;
return NULL;
}
else {
std::cout << "写入成功,已经将原始程序写入目标进程!" << std::endl;
}
FlushInstructionCache(pi.hProcess, allocAddress, fullSize);
// 修正 Eip(或 Rip)到新的入口
DWORD_PTR newBase = (DWORD_PTR)allocAddress;
#ifdef _WIN64
contx.Rip = (DWORD64)newBase + oep;
#else
contx.Eip = (DWORD)newBase + oep;
contx.Eax = (DWORD)newBase + oep;
#endif
// 把修改后的上下文设置回线程
SetThreadContext(pi.hThread, &contx);
// 恢复挂起线程
ResumeThread(pi.hThread);
}
赞赏
赞赏
雪币:
留言: