记录下自己学习PE的日子-[滴水三期加壳项目-解密部分]
2020-04-09 15:11:39 Author: bbs.pediy.com(查看原文) 阅读量:268 收藏

[原创]记录下自己学习PE的日子-[滴水三期加壳项目-解密部分]

2天前 508

[原创]记录下自己学习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安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!

最后于 2天前 被清风qfccc编辑 ,原因: 发现文章中错误的地方


文章来源: https://bbs.pediy.com/thread-258657.htm
如有侵权请联系:admin#unsafe.sh