SimpleDpack_C++编写shellcode压缩壳
本文为看雪论坛精华文章
看雪论坛作者ID:devseed
一、分析PE64结构
1、PE文件头总览
NT header包括file header和optional header;
optional header,末尾含有16个元素的data directory数组;
IMAGE_OPTIONAL_HEADER64,里面ImageBase、还有堆栈尺寸类型是ULONGLONG;
紧随着NT header的是各section的headers,数量为fi le header里面的NumberOfSections。
|DOS header // e_lfanew|NT header|file header // NumberOfSections, SizeOfOptionalHeader(x86=0xe0, x64=0xf0)|optional header|... //AddressOfEntryPoint(oep), ImageBase, SizeOfImage, SizeOfHeaders|data directory[16] //IMAGE_DIRECTORY_ENTRY_EXPORT, ..._IMPORT, ..._IAT|section headers[n]
2、DataDirectory
typedef struct _IMAGE_OPTIONAL_HEADER {...IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;typedef struct _IMAGE_DATA_DIRECTORY {DWORD VirtualAddress;DWORD Size;} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory, .edata#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory, .idata#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory , .rsrc#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory , .pdata#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table, .reloc#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data , 0#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of Global Ptr#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory , 线程局部存储#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table (.data)#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
AddressOfFunctions指向函数RVA表
AddressOfNames指向函数名RVA表(存储字符串指针)、
AddressOfNameOrdinals指向序号表。
typedef struct _IMAGE_EXPORT_DIRECTORY { //Export Directory TableDWORD Characteristics;DWORD TimeDateStamp;WORD MajorVersion;WORD MinorVersion;DWORD Name; // the name of the DLL, RVADWORD Base; // The starting ordinal number for exports in this image, usually 1DWORD NumberOfFunctions;DWORD NumberOfNames;DWORD AddressOfFunctions; // Export Address Table, RVA from base of imageDWORD AddressOfNames; // Export Name Pointer Table, RVADWORD AddressOfNameOrdinals; // Export Ordinal Table, RVA from base of image} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
(2)IMAGE_DIRECTORY_ENTRY_IMPORT
typedef struct _IMAGE_IMPORT_DESCRIPTOR {union {DWORD Characteristics; // 0 for terminating null import descriptorDWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)} DUMMYUNIONNAME;DWORD TimeDateStamp;DWORD ForwarderChain; //index of the first forwarder reference, -1 if noDWORD Name; // RVA to the name of dllDWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)} IMAGE_IMPORT_DESCRIPTOR;typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;typedef struct _IMAGE_THUNK_DATA32 {union {DWORD ForwarderString; // PBYTEDWORD Function; // PDWORD, va of the function, in ft, oatDWORD Ordinal;DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME, in oft} u1;} IMAGE_THUNK_DATA32;typedef struct _IMAGE_THUNK_DATA64 {union {ULONGLONG ForwarderString; // PBYTEULONGLONG Function; // PDWORD, va of the function, in ft, oatULONGLONG Ordinal;ULONGLONG AddressOfData; // PIMAGE_IMPORT_BY_NAME, in oft} u1;} IMAGE_THUNK_DATA64;typedef struct _IMAGE_IMPORT_BY_NAME { //in oftWORD Hint;CHAR Name[1]; // char *Name} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
(3)IMAGE_DIRECTORY_ENTRY_IAT
(4)IMAGE_DIRECTORY_ENTRY_BASERELOC
reloc内包含多个BASE_RELOCATION块。
每个BASE_RELOCATION块内头描述了此块reloc的VirtualAddress(RVA)和SizeOfBlock。
之后块内若干个两字节的TypeOffset,低12位为offset,高4位是type,64位也是两字节。
RVA+offset即为需要重定向基地址的位置。
typedef struct _IMAGE_BASE_RELOCATION { // it has multi base relocation block,DWORD VirtualAddress; // rva of this base relocation areaDWORD SizeOfBlock; //The total number of bytes in the base relocation block, including the Page RVA and Block Size fields and the Type/Offset fields that follow.// WORD TypeOffset[1];} IMAGE_BASE_RELOCATION; //Each base relocation block starts with this structtypedef struct TypeOffset // after one base_relation, it has multi typeoffset{WORD offset : 12; //偏移值WORD type : 4; //重定位属性(方式), 高4位// IMAGE_REL_BASED_ABSOLUTE 0 The base relocation is skipped,used to pad a block.// IMAGE_REL_BASED_HIGHLOW 3 The base relocation applies all 32 bits of the difference to the 32-bit field at offset. va = offset + base_rva + imagebase// IMAGE_REL_BASED_DIR64 10 for 64bit}TypeOffset,*PTypeOffset
3、Section header
这里SizeOfRawData(文件中的大小)可以为零(比如说动态生成的数据,区段之留个头声明,文件里不需要对应的数据),
SizeOfRawData必须是FileAlignment的整数倍,VirtualSize为实际内存空间(不包括MemoryAlign后的)
各区段之间在内存上不能有空隙(比如说我中间删除一个区段,修改了文件指针与内存指针,但是内存上两个区段地址没有接上,就没法运行了)。
typedef struct _IMAGE_SECTION_HEADER { //0x28 bytes, the last is all zeroBYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 8 bytes, null endunion {DWORD PhysicalAddress;DWORD VirtualSize;} Misc;DWORD VirtualAddress; //rva (, relative to the image base)DWORD SizeOfRawData; // The size of the initialized data on disk, in bytesDWORD PointerToRawData; // fileoffset of the section dataDWORD PointerToRelocations;DWORD PointerToLinenumbers; // for debug line numberWORD NumberOfRelocations;WORD NumberOfLinenumbers;DWORD Characteristics;//IMAGE_SCN_MEM_EXECUTE 0x20000000,IMAGE_SCN_MEM_READ 0x40000000, IMAGE_SCN_MEM_WRITE 0x80000000} IMAGE_SECTION_HEADER, *PIMAGE_SWECTION_HEADER;
4、编程实现解析PE文件头
PIMAGE_NT_HEADERS CPEinfo::getNtHeader(LPBYTE pPeBuf){PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pPeBuf;return (PIMAGE_NT_HEADERS)(pPeBuf + pDosHeader->e_lfanew);}PIMAGE_FILE_HEADER CPEinfo::getFileHeader(LPBYTE pPeBuf){return &getNtHeader(pPeBuf)->FileHeader;}PIMAGE_OPTIONAL_HEADER CPEinfo::getOptionalHeader(LPBYTE pPeBuf){return &getNtHeader(pPeBuf)->OptionalHeader;}PIMAGE_DATA_DIRECTORY CPEinfo::getImageDataDirectory(LPBYTE pPeBuf){PIMAGE_OPTIONAL_HEADER pOptionalHeader = getOptionalHeader(pPeBuf);return pOptionalHeader->DataDirectory;}PIMAGE_SECTION_HEADER CPEinfo::getSectionHeader(LPBYTE pPeBuf){PIMAGE_NT_HEADERS pNtHeader = getNtHeader(pPeBuf);return (PIMAGE_SECTION_HEADER)((LPBYTE)pNtHeader + sizeof(IMAGE_NT_HEADERS));}PIMAGE_IMPORT_DESCRIPTOR CPEinfo::getImportDescriptor(LPBYTE pPeBuf, bool bMemAlign = true){PIMAGE_DATA_DIRECTORY pImageDataDirectory = getImageDataDirectory(pPeBuf);DWORD rva = pImageDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;DWORD offset = bMemAlign ? rva: rva2faddr(pPeBuf, rva);return (PIMAGE_IMPORT_DESCRIPTOR)(pPeBuf + offset);}PIMAGE_EXPORT_DIRECTORY CPEinfo::getExportDirectory(LPBYTE pPeBuf, bool bMemAlign = true){PIMAGE_DATA_DIRECTORY pImageDataDirectory = getImageDataDirectory(pPeBuf);DWORD rva = pImageDataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;DWORD offset = bMemAlign ? rva : rva2faddr(pPeBuf, rva);return (PIMAGE_EXPORT_DIRECTORY)(pPeBuf + offset);}DWORD CPEinfo::getOepRva(LPBYTE pPeBuf){if (pPeBuf == NULL) return 0;if (isPe(pPeBuf) <= 0) return 0;return getOptionalHeader(pPeBuf)->AddressOfEntryPoint;}WORD CPEinfo::getSectionNum(LPBYTE pPeBuf){return getFileHeader(pPeBuf)->NumberOfSections;}
二、壳的数据结构设计
原来区段位置大小
压缩的缓存区位置大小、压缩类型
源程序的OEP、IAT
#include <Windows.h>#ifndef _DPACKPROC_H#define _DPACKPROC_H#define MAX_DPACKSECTNUM 16 // 最多可pack区段数量#include "lzma\lzmalib.h"typedef struct _DLZMA_HEADER{size_t RawDataSize;//原始数据尺寸(不含此头)size_t DataSize;//压缩后的数据大小char LzmaProps[LZMA_PROPS_SIZE];//原始lzma的文件头}DLZMA_HEADER, *PDLZMA_HEADER;//此处外围添加适用于dpack的lzma头typedef struct _DPACK_ORGPE_INDEX //源程序被隐去的信息,此结构为明文表示,地址全是rva{#ifdef _WIN64ULONGLONG ImageBase; //源程序基址#elseDWORD ImageBase; //源程序基址#endifDWORD OepRva; //原程序rva入口DWORD ImportRva; //导入表信息DWORD ImportSize;}DPACK_ORGPE_INDEX, * PDPACK_ORGPE_INDEX;#define DPACK_SECTION_RAW 0#define DPACK_SECTION_DLZMA 1typedef struct _DPACK_SECTION_ENTRY //源信息与压缩变换后信息索引表是{//假设不超过4gDWORD OrgRva; // OrgRva为0时则是不解压到原来区段DWORD OrgSize;DWORD DpackRva;DWORD DpackSize;DWORD Characteristics;DWORD DpackSectionType; // dpack区段类型}DPACK_SECTION_ENTRY, * PDPACK_SECTION_ENTRY;typedef struct _DPACK_SHELL_INDEX//DPACK变换头{union{PVOID DpackOepFunc; // 初始化壳的入口函数(放第一个元素方便初始化)DWORD DpackOepRva; // 加载shellcode后也许改成入口RVA};DPACK_ORGPE_INDEX OrgIndex;WORD SectionNum; //变换的区段数,最多MAX_DPACKSECTNUM区段DPACK_SECTION_ENTRY SectionIndex[MAX_DPACKSECTNUM]; //变换区段索引, 以全0结尾PVOID Extra; //其他信息,方便之后拓展}DPACK_SHELL_INDEX, * PDPACK_SHELL_INDEX;size_t dlzmaPack(LPBYTE pDstBuf, LPBYTE pSrcBuf, size_t srcSize);size_t dlzmaUnpack(LPBYTE pDstBuf, LPBYTE pSrcBuf, size_t srcSize);#endif
#include <Windows.h>#include "dpackType.h"size_t dlzmaPack(LPBYTE pDstBuf,LPBYTE pSrcBuf,size_t srcSize){size_t dstSize = -1; //最大的buffersize, 为0会出错size_t propSize = sizeof(DLZMA_HEADER);PDLZMA_HEADER pDlzmah=(PDLZMA_HEADER)pDstBuf;LzmaCompress(pDstBuf+sizeof(DLZMA_HEADER), &dstSize,pSrcBuf, srcSize,pDlzmah->LzmaProps, (size_t *)&propSize,-1 ,0, -1, -1, -1, -1, -1);pDlzmah->RawDataSize = srcSize;pDlzmah->DataSize = dstSize;return dstSize;}size_t dlzmaUnpack(LPBYTE pDstBuf, LPBYTE pSrcBuf, size_t srcSize){PDLZMA_HEADER pdlzmah = (PDLZMA_HEADER)pSrcBuf;size_t dstSize = pdlzmah->RawDataSize;//release版不赋初值会出错,由于debug将其赋值为cccccccc很大的数LzmaUncompress(pDstBuf, &dstSize,//此处必须赋最大值pSrcBuf + sizeof(DLZMA_HEADER), &srcSize,pdlzmah->LzmaProps, LZMA_PROPS_SIZE);return dstSize;}
三、壳的shellcode编写
分配解压后的内存(如果把区段头信息也删除了,需要自己分配)
解压缩各区段数据(暂不考虑TLS,rsrc的压缩)
初始化原始的IAT
跳转到原OEP
#ifdef _WIN64void dpackStart()#else__declspec(naked) void dpackStart()//此函数中不要有局部变量#endif{BeforeUnpack();MallocAll(NULL);UnpackAll(NULL);g_orgOep = g_dpackShellIndex.OrgIndex.ImageBase + g_dpackShellIndex.OrgIndex.OepRva;LoadOrigionIat(NULL);AfterUnpack();JmpOrgOep();}
1、分配解压内存
void MallocAll(PVOID arg){MEMORY_BASIC_INFORMATION mi = { 0 };HANDLE hProcess = GetCurrentProcess();HMODULE imagebase = GetModuleHandle(NULL);for (int i = 0; i < g_dpackShellIndex.SectionNum; i++){if (g_dpackShellIndex.SectionIndex[i].OrgSize == 0) continue;LPBYTE tVa = (LPBYTE)imagebase + g_dpackShellIndex.SectionIndex[i].OrgRva;DWORD tSize = g_dpackShellIndex.SectionIndex[i].OrgSize;VirtualQueryEx(hProcess, tVa, &mi, tSize);if(mi.State == MEM_FREE){DWORD flProtect = PAGE_EXECUTE_READWRITE;switch (g_dpackShellIndex.SectionIndex[i].Characteristics){case IMAGE_SCN_MEM_EXECUTE:flProtect = PAGE_EXECUTE;break;case IMAGE_SCN_MEM_READ:flProtect = PAGE_READONLY;break;case IMAGE_SCN_MEM_WRITE:flProtect = PAGE_READWRITE;break;}if(!VirtualAllocEx(hProcess, tVa, tSize, MEM_COMMIT, flProtect)){MessageBox(NULL,"Alloc memory failed", "error", NULL);ExitProcess(1);}}}}
2、解压区段
void UnpackAll(PVOID arg){DWORD oldProtect;#ifdef _WIN64ULONGLONG imagebase = g_dpackShellIndex.OrgIndex.ImageBase;#elseDWORD imagebase = g_dpackShellIndex.OrgIndex.ImageBase;#endiffor(int i=0; i<g_dpackShellIndex.SectionNum; i++){switch(g_dpackShellIndex.SectionIndex[i].DpackSectionType){case DPACK_SECTION_RAW:{if (g_dpackShellIndex.SectionIndex[i].OrgSize == 0) continue;VirtualProtect((LPVOID)(imagebase + g_dpackShellIndex.SectionIndex[i].OrgRva),g_dpackShellIndex.SectionIndex[i].OrgSize,PAGE_EXECUTE_READWRITE, &oldProtect);memcpy((void*)(imagebase + g_dpackShellIndex.SectionIndex[i].OrgRva),(void*)(imagebase + g_dpackShellIndex.SectionIndex[i].DpackRva),g_dpackShellIndex.SectionIndex[i].OrgSize);VirtualProtect((LPVOID)(imagebase + g_dpackShellIndex.SectionIndex[i].OrgRva),g_dpackShellIndex.SectionIndex[i].OrgSize,oldProtect, &oldProtect);break;}case DPACK_SECTION_DLZMA:{LPBYTE buf = new BYTE[g_dpackShellIndex.SectionIndex[i].OrgSize];if (!dlzmaUnpack(buf,(LPBYTE)(g_dpackShellIndex.SectionIndex[i].DpackRva + imagebase),g_dpackShellIndex.SectionIndex[i].DpackSize)){MessageBox(0, "unpack failed", "error", 0);ExitProcess(1);}VirtualProtect((LPVOID)(imagebase + g_dpackShellIndex.SectionIndex[i].OrgRva),g_dpackShellIndex.SectionIndex[i].OrgSize,PAGE_EXECUTE_READWRITE, &oldProtect);memcpy((void*)(imagebase + g_dpackShellIndex.SectionIndex[i].OrgRva),buf, g_dpackShellIndex.SectionIndex[i].OrgSize);VirtualProtect((LPVOID)(imagebase + g_dpackShellIndex.SectionIndex[i].OrgRva),g_dpackShellIndex.SectionIndex[i].OrgSize,oldProtect, &oldProtect);delete[] buf;break;}default:break;}}}
3、初始化源程序的IAT
void LoadOrigionIat(PVOID arg) // 因为将iat改为了壳的,所以要还原原来的iat{DWORD i,j;DWORD dll_num = g_dpackShellIndex.OrgIndex.ImportSize/sizeof(IMAGE_IMPORT_DESCRIPTOR);//导入dll的个数,含最后全为空的一项DWORD item_num=0;//一个dll中导入函数的个数,不包括全0的项DWORD oldProtect;HMODULE tHomule;//临时加载dll的句柄LPBYTE tName;//临时存放名字#ifdef _WIN64ULONGLONG tVa;//临时存放虚拟地址ULONGLONG imagebase = g_dpackShellIndex.OrgIndex.ImageBase;#elseDWORD tVa;//临时存放虚拟地址DWORD imagebase = g_dpackShellIndex.OrgIndex.ImageBase;#endifPIMAGE_IMPORT_DESCRIPTOR pImport=(PIMAGE_IMPORT_DESCRIPTOR)(imagebase+g_dpackShellIndex.OrgIndex.ImportRva);//指向第一个dllPIMAGE_THUNK_DATA pfThunk;//ftPIMAGE_THUNK_DATA poThunk;//oftPIMAGE_IMPORT_BY_NAME pFuncName;for(i=0;i<dll_num;i++){if(pImport[i].OriginalFirstThunk==0) continue;tName=(LPBYTE)(imagebase+pImport[i].Name);tHomule=LoadLibrary((LPCSTR)tName);pfThunk=(PIMAGE_THUNK_DATA)(imagebase+pImport[i].FirstThunk);poThunk=(PIMAGE_THUNK_DATA)(imagebase+pImport[i].OriginalFirstThunk);for(j=0;poThunk[j].u1.AddressOfData!=0;j++){}//注意个数。。。item_num=j;VirtualProtect((LPVOID)(pfThunk),item_num * sizeof(IMAGE_THUNK_DATA),PAGE_EXECUTE_READWRITE,&oldProtect);//注意指针位置for(j=0;j<item_num;j++){if((poThunk[j].u1.Ordinal >>31) != 0x1) //不是用序号{pFuncName=(PIMAGE_IMPORT_BY_NAME)(imagebase+poThunk[j].u1.AddressOfData);tName=(LPBYTE)pFuncName->Name;#ifdef _WIN64tVa = (ULONGLONG)GetProcAddress(tHomule, (LPCSTR)tName);#elsetVa = (DWORD)GetProcAddress(tHomule, (LPCSTR)tName);#endif}else{//如果此参数是一个序数值,它必须在一个字的低字节,高字节必须为0。#ifdef _WIN64tVa = (ULONGLONG)GetProcAddress(tHomule,(LPCSTR)(poThunk[j].u1.Ordinal & 0x0000ffff));#elsetVa = (DWORD)GetProcAddress(tHomule, (LPCSTR)(poThunk[j].u1.Ordinal & 0x0000ffff));#endif}if (tVa == NULL){MessageBox(NULL, "IAT load error!", "error", NULL);ExitProcess(1);}pfThunk[j].u1.Function = tVa;//注意间接寻址}VirtualProtect((LPVOID)(pfThunk),item_num * sizeof(IMAGE_THUNK_DATA),oldProtect,&oldProtect);}}
4、跳转到源OEP
#ifndef _WIN64__declspec(naked) void JmpOrgOep(){__asm{push g_orgOep;ret;}}#endif
四、加壳程序的编写
DWORD CSimpleDpack::packPe(const char* dllpath, int dpackSectionType)//加壳,失败返回0,成功返回pack数据大小{if (m_packpe.getPeBuf() == NULL) return 0;initDpackTmpbuf(); // 初始化pack bufDWORD packsize = packSection(dpackSectionType); // pack各区段DWORD shellsize = loadShellDll(dllpath); // 载入dll shellcodeDWORD packpeImgSize = m_packpe.getOptionalHeader()->SizeOfImage;DWORD shellStartRva = m_shellpe.getSectionHeader()[0].VirtualAddress;DWORD shellEndtRva = m_shellpe.getSectionHeader()[3].VirtualAddress; // rsrcadjustShellReloc(packpeImgSize); // reloc调整后全局变量g_dpackShellIndex的oep也变成之后adjustShellIat(packpeImgSize);initShellIndex(shellEndtRva); // 初始化dpack shell index,一定要在reloc之后, 因为reloc后这里的地址也变了makeAppendBuf(shellStartRva, shellEndtRva, packpeImgSize);adjustPackpeHeaders(0); // 调整要pack的pe头return packsize + shellEndtRva - shellStartRva;}
1、shellcode的处理
DWORD CPEedit::shiftReloc(LPBYTE pPeBuf, size_t oldImageBase, size_t newImageBase, DWORD offset, bool bMemAlign){//修复重定位,其实此处pShellBuf为hShell副本DWORD all_num = 0;DWORD sumsize = 0;auto pRelocEntry = &getImageDataDirectory(pPeBuf)[IMAGE_DIRECTORY_ENTRY_BASERELOC];while (sumsize < pRelocEntry->Size){auto pBaseRelocation = (PIMAGE_BASE_RELOCATION)(pPeBuf + sumsize +(bMemAlign ? pRelocEntry->VirtualAddress :rva2faddr(pPeBuf, pRelocEntry->VirtualAddress)));auto pRelocOffset = (PRELOCOFFSET)((LPBYTE)pBaseRelocation + sizeof(IMAGE_BASE_RELOCATION));DWORD item_num = (pBaseRelocation->SizeOfBlock -sizeof(IMAGE_BASE_RELOCATION)) / sizeof(RELOCOFFSET);for (int i = 0; i < item_num; i++){if (pRelocOffset[i].offset == 0) continue;DWORD toffset = pRelocOffset[i].offset + pBaseRelocation->VirtualAddress;if (!bMemAlign) toffset = rva2faddr(pPeBuf, toffset);// 新的重定位地址 = 重定位后的地址(VA)-加载时的镜像基址(hModule VA) + 新的镜像基址(VA) + 新代码基址RVA(前面用于存放压缩的代码)// 由于讲dll附加在后面,需要在dll shell中的重定位加上偏移修正#ifdef _WIN64*(PULONGLONG)(pPeBuf + toffset) += newImageBase - oldImageBase + offset; //重定向每一项地址#else//printf("%08lX -> ", *(PDWORD)(pPeBuf + toffset));*(PDWORD)(pPeBuf + toffset) += newImageBase - oldImageBase + offset; //重定向每一项地址//printf("%08lX\n", *(PDWORD)(pPeBuf + toffset));#endif}pBaseRelocation->VirtualAddress += offset; //重定向页表基址sumsize += sizeof(RELOCOFFSET) * item_num + sizeof(IMAGE_BASE_RELOCATION);all_num += item_num;}return all_num;}DWORD CPEedit::shiftOft(LPBYTE pPeBuf, DWORD offset, bool bMemAlign, bool bResetFt){auto pImportEntry = &getImageDataDirectory(pPeBuf)[IMAGE_DIRECTORY_ENTRY_IMPORT];DWORD dll_num = pImportEntry->Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);//导入dll的个数,含最后全为空的一项DWORD func_num = 0;//所有导入函数个数,不包括全0的项auto pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR) (pPeBuf +(bMemAlign ? pImportEntry->VirtualAddress :rva2faddr(pPeBuf, pImportEntry->VirtualAddress)));//指向第一个dllfor (int i = 0; i < dll_num; i++){if (pImportDescriptor[i].OriginalFirstThunk == 0) continue;auto pOFT = (PIMAGE_THUNK_DATA)(pPeBuf + (bMemAlign ?pImportDescriptor[i].OriginalFirstThunk:rva2faddr(pPeBuf, pImportDescriptor[i].OriginalFirstThunk)));auto pFT = (PIMAGE_THUNK_DATA)(pPeBuf + (bMemAlign ?pImportDescriptor[i].FirstThunk :rva2faddr(pPeBuf, pImportDescriptor[i].FirstThunk)));DWORD item_num = 0;for (int j = 0; pOFT[j].u1.AddressOfData != 0; j++){item_num++; //一个dll中导入函数的个数,不包括全0的项if ((pOFT[j].u1.Ordinal >> 31) != 0x1) //不是用序号{pOFT[j].u1.AddressOfData += offset;if (bResetFt) pFT[j].u1.AddressOfData = pOFT[j].u1.AddressOfData;}}pImportDescriptor[i].OriginalFirstThunk += offset;pImportDescriptor[i].FirstThunk += offset;pImportDescriptr[i].Name += offset;func_num += item_num;}return func_num;}
2、调整exe的PE头
void CSimpleDpack::adjustPackpeHeaders(DWORD offset){// 设置被加壳程序的信息, oep, reloc, iatif (m_pShellIndex == NULL) return;auto packpeImageSize = m_packpe.getOptionalHeader()->SizeOfImage;// m_pShellIndex->DpackOepFunc 之前已经reloc过了,变成了正确的va了(shelldll是release版)m_packpe.setOepRva((size_t)m_pShellIndex->DpackOepFunc -m_packpe.getOptionalHeader()->ImageBase + offset);m_packpe.getImageDataDirectory()[IMAGE_DIRECTORY_ENTRY_IMPORT] = {m_shellpe.getImageDataDirectory()[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + packpeImageSize + offset,m_shellpe.getImageDataDirectory()[IMAGE_DIRECTORY_ENTRY_IMPORT].Size };m_packpe.getImageDataDirectory()[IMAGE_DIRECTORY_ENTRY_IAT] = {m_shellpe.getImageDataDirectory()[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + packpeImageSize + offset,m_shellpe.getImageDataDirectory()[IMAGE_DIRECTORY_ENTRY_IMPORT].Size};m_packpe.getImageDataDirectory()[IMAGE_DIRECTORY_ENTRY_BASERELOC] = { 0,0 };// pe 属性设置m_packpe.getFileHeader()->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; //禁止基址随机化}
3、保存PE文件
DWORD CSimpleDpack::savePe(const char* path)//失败返回0,成功返回文件大小{/*pack区域放到后面,由于内存有对齐问题,只允许pack一整个区段先改pe头,再分配空间,支持若原来pe fileHeader段不够,添加段将区段头与区段分开考虑*/// dpack头初始化IMAGE_SECTION_HEADER dpackSect = {0};strcpy((char*)dpackSect.Name, ".dpack");dpackSect.Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE;dpackSect.VirtualAddress = m_dpackTmpbuf[m_dpackSectNum - 1].OrgRva;// 准备dpack bufDWORD dpackBufSize = 0;for (int i = 0; i < m_dpackSectNum; i++) dpackBufSize += m_dpackTmpbuf[i].DpackSize;LPBYTE pdpackBuf = new BYTE[dpackBufSize];LPBYTE pCurBuf = pdpackBuf;memcpy(pdpackBuf, m_dpackTmpbuf[m_dpackSectNum - 1].PackedBuf,m_dpackTmpbuf[m_dpackSectNum - 1].DpackSize); // 壳代码pCurBuf += m_dpackTmpbuf[m_dpackSectNum - 1].DpackSize;for (int i = 0; i < m_dpackSectNum -1 ; i++){memcpy(pCurBuf, m_dpackTmpbuf[i].PackedBuf,m_dpackTmpbuf[i].DpackSize);pCurBuf += m_dpackTmpbuf[i].DpackSize;}// 删除被压缩区段和写入peint remvoeSectIdx[MAX_DPACKSECTNUM] = {0};int removeSectNum = 0;for (int i = 0; i < m_packpe.getFileHeader()->NumberOfSections; i++){if (m_packSectMap[i] == true) remvoeSectIdx[removeSectNum++] = i;}m_packpe.removeSectionDatas(removeSectNum, remvoeSectIdx);m_packpe.appendSection(dpackSect, pdpackBuf, dpackBufSize);delete[] pdpackBuf;return m_packpe.savePeFile(path);}
五、x64适配
#ifdef _WIN64*(PULONGLONG)(pPeBuf + toffset) += newImageBase - oldImageBase + offset;#else//printf("%08lX -> ", *(PDWORD)(pPeBuf + toffset));*(PDWORD)(pPeBuf + toffset) += newImageBase - oldImageBase + offset;//printf("%08lX\n", *(PDWORD)(pPeBuf + toffset));#endif
extern g_orgOep:QWORD;AfterUnpack proto c;.codeJmpOrgOep PROCpush g_orgOep;ret;JmpOrgOep ENDPend
看雪ID:devseed
https://bbs.pediy.com/user-home-838741.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!