-
-
[原创]记录下自己学习PE的日子-[滴水三期加壳项目-解密部分]
-
发表于:
2020-4-6 20:58
7724
-
[原创]记录下自己学习PE的日子-[滴水三期加壳项目-解密部分]
最近在学习滴水三期里面的PE, 在win32讲完之后会有一个项目, 就是对软件加壳C语言代码的实现, 本人菜鸡一个,高手勿喷
上面图片解密软件的大概的步骤
代码实现:
1.获取当前程路径
//获取当前程序运行路径
char FilePath[255] = {0};
GetModuleFileName(NULL, FilePath, 255);
2.然后读成文件buffer
LPVOID fileBuffer;
DWORD size = 0;
size = ReadFileBuffer(FilePath, &fileBuffer);
3.因为在最后一个节是我自己添加的, 那就直接把最后一个节的所有数据返回来 然后进行解密
(1)GetSrcSource(fileBuffer,&srcSource); 函数实现
//获取源数据
DWORD GetSrcSource(IN LPVOID fileBuffer, OUT LPVOID* OutFileBuffer) {
//初始化数据
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)fileBuffer;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)fileBuffer + pDosHeader->e_lfanew);
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);//第一个节位置
PIMAGE_SECTION_HEADER pLastHeader = pSectionHeader + pNtHeader->FileHeader.NumberOfSections - 1;//最后一个节位置
*OutFileBuffer = (LPVOID)((DWORD)fileBuffer + pLastHeader->PointerToRawData);
return pLastHeader->SizeOfRawData;
}
(2)DecodeSource(srcSize, (PBYTE)srcSource, &srcSource);函数实现
/*
解密函数
参数
文件大小
源文件
写出文件
*/
VOID DecodeSource(DWORD size, PBYTE source, OUT LPVOID buffer) {
for (DWORD i = 0; i < size; i++) {
source[i] ^= 8888;
}
}
4.以上步骤做完之后得到了原来被加密的程序(src), 这个时候拉伸内存
LPVOID srcImageBuffer;
FileToImage(srcSource, &srcImageBuffer);
5.已挂起的方式创建进程(傀儡进程)
(1)路径是一开始或者自身的路径
//创建进程 记得恢复进程
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
//以挂起的方式创建进程
CreateProcess(
NULL, // name of executable module
FilePath, // command line string
NULL, // SD
NULL, // SD
FALSE, // handle inheritance option
CREATE_SUSPENDED, // creation flags
NULL, // new environment block
currentDirectory, // current directory name
&si, // startup information
&pi // process information
);
6.获取刚刚创建线程Context结构 并或者它的入口点 和 imgebase
CONTEXT contx;
contx.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &contx);
//获取入口点
DWORD dwEntryPoint = contx.Eax;
//获取ImageBase
char* baseAddress = (CHAR *)contx.Ebx + 8;
DWORD imageBase = 0;
SIZE_T byteSize = 0;
//因为属于别人程序所以使用这个读
ReadProcessMemory(pi.hProcess, baseAddress, &imageBase, 4, &byteSize);
7.卸载自身外壳程序 方便一会 写入src程序
(1)因为使用的这个函数 头文件里面没办函 所以用函数指针的形式使用
(2)使用这个函数时候传入的是刚刚创建的进程句柄
typedef long NTSTATUS;
typedef NTSTATUS(__stdcall *pfnZwUnmapViewOfSection)(
IN HANDLE ProcessHandle,
IN LPVOID BaseAddress
);
pfnZwUnmapViewOfSection ZwUnmapViewOfSection;
ZwUnmapViewOfSection = (pfnZwUnmapViewOfSection)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "ZwUnmapViewOfSection");
if (!ZwUnmapViewOfSection)
{
::TerminateThread(pi.hThread, 2);
::WaitForSingleObject(pi.hThread, INFINITE);
return 0;
}
//卸载文件外壳镜像
DWORD a = 0;
a = ZwUnmapViewOfSection(pi.hProcess, (PVOID)imageBase);
8.获取src拉伸后的需要的值, 并在傀儡进程空间中申请内存
(1)申请的空间位置 用过去到的imagebase位置
(2)大小用获取到的sizeOfImage
//获取src imagebase sizeofimage
DWORD srcImageBase = 0;
DWORD srcSizeOfImage = 0;
DWORD srcOEP = 0;
GetImageBase(srcImageBuffer, &srcImageBase,&srcSizeOfImage,&srcOEP);
LPVOID status = NULL;
//指定区域分配地址
status = VirtualAllocEx(pi.hProcess,
(LPVOID)srcImageBase, srcSizeOfImage,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
9.如果上一步内存没有申请到需要 判断这个src有没有重定位表, 有的话就在别的地方申请, 没有的话就解密失败
ps: 如果在其他地方还是申请空间失败那就拉倒吧 洗洗睡吧
修复完事之后再重新获取一遍src基本的pe数据
代码当时写的只求能运行好使 , 所以写的很是辣鸡 = =
if (status == NULL) {
//判断有没有重定位
if (isRelocation(srcImageBuffer)) {
::TerminateThread(pi.hThread, 2);
::WaitForSingleObject(pi.hThread, INFINITE);
return 0;
}
status = VirtualAllocEx(pi.hProcess,
NULL, srcSizeOfImage,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (status == 0) {
::TerminateThread(pi.hThread, 2);
::WaitForSingleObject(pi.hThread, INFINITE);
return 0;
}
//修复重定位
ModificationBaseRel(srcImageBuffer, (DWORD)status);
GetImageBase(srcImageBuffer, &srcImageBase, &srcSizeOfImage, &srcOEP);
}
ModificationBaseRel(srcImageBuffer, (DWORD)status);修复重定位代码
//修复重定位
VOID ModificationBaseRel(IN LPVOID ImageBuffer, DWORD newImageBase) {
PIMAGE_DOS_HEADER pDosHeader = NULL; //DOs 头
PIMAGE_NT_HEADERS pNTHeader = NULL; //NT头
PIMAGE_FILE_HEADER pFileHeader = NULL; // 标准PE头
PIMAGE_OPTIONAL_HEADER pOptionHerader = NULL; // 可选PE头
PIMAGE_SECTION_HEADER pSectionHeader = NULL; // 节表
PIMAGE_BASE_RELOCATION pBaseRelocation = NULL; //重定位表
pDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + sizeof(DWORD));
pOptionHerader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHerader + pFileHeader->SizeOfOptionalHeader);
pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pDosHeader + pOptionHerader->DataDirectory[5].VirtualAddress);
pOptionHerader->ImageBase = newImageBase;
int index = 0;
while (pBaseRelocation->VirtualAddress != 0) {
int count = (pBaseRelocation->SizeOfBlock - 8) / 2;
PWORD addr = (PWORD)((DWORD)pBaseRelocation + 8);
for (int i = 0; i < count; i++) {
DWORD height4 = addr[i] >> 12;
if (height4 == 3) {
DWORD low12 = addr[i] & 0x0fff;
DWORD rva = pBaseRelocation->VirtualAddress + low12;
PDWORD addr = (PDWORD)((DWORD)ImageBuffer + rva);
*addr = *addr - pOptionHerader->ImageBase + newImageBase;
}
}
index++;
pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock);
}
}
10.不管是上面是正常执行了, 还是修复重定位了, 到了这一步就需要把src程序贴到傀儡进程中 然后设置context结构 让它跑起来
(1)这些操作完事之后记得把线程恢复了
(2)oep + iamgebase 才是程序真正的基址
//将src复制到申请成功的地址中
SIZE_T srcResult = 0;
WriteProcessMemory(pi.hProcess, status, srcImageBuffer, srcSizeOfImage, &srcResult);
//修正context结构
SIZE_T oepResult = 0;
WriteProcessMemory(pi.hProcess, baseAddress, &srcImageBase, sizeof(DWORD), &oepResult);
contx.Eax = srcOEP + srcImageBase;
SetThreadContext(pi.hThread, &contx);
//记得恢复线程
ResumeThread(pi.hThread);
11.如果运行成功了, 程序会正常打开该exe的, 如果失败就检查一下, 看看context结构对不对, 修改完该结构千万别忘了使用SetThreadContext(pi.hThread,&contx);
ps: 当时忘记写这个导致程序直接崩了, 然后 在算 Eax(入口点)的位置的时候忘记 + iamgebase 了 反正各种小细节忘了一个 程序都不会跑起来
时间:2020/04/01 (记录下学习过程, 明年回来看看自己当时多垃圾)
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2020-4-7 10:34
被清风qfccc编辑
,原因: 发现文章中错误的地方