C++实现简单加密壳 您所在的位置:网站首页 elf加壳工具 C++实现简单加密壳

C++实现简单加密壳

2023-09-08 16:08| 来源: 网络整理| 查看: 265

文章目录 概述加壳思路加壳步骤1. 添加一个新的节区用于添加壳代码,即将dll内容复制过来2. 在dll中保存的壳代码必须保存的信息3. 处理程序的TLS4. 根据新添加的区段位置修正dll中的重定位信息修正dll重定位信息的方式如下:构建TLS重定位表结构 4. 压缩文件5. 将dll的信息拷贝到新的节中6. 设置程序的新的OEP 壳代码的实现总结

概述

类似上篇手动加壳的流程,可以使用程序实现一个加壳器,加壳步骤和手动加壳类似。这里使用C++实现一个简单的加密壳,实现以下的功能

实现源程序的区段的压缩加密代码段加密IAT运行密码简单反调试简单处理TLS支持重定位 加壳思路

将壳代码写成一个dll,所有壳的功能代码都添加到这个dll中,dll不必具备执行功能,仅仅用于提供壳的代码。实现一个加壳器,将源程序和壳dll都读入到内存中,通过修正dll中的相关数据,然后将dll中的数据拷贝到新增加的节中,然后修正OEP到壳的入口点,即可实现简单的壳。

加壳步骤 1. 添加一个新的节区用于添加壳代码,即将dll内容复制过来 将源文件读入内存将dll以不启动的方式加载到内存中拷贝dll的.text段的描述表追加到源文件的区段描述符后面NtHeader.FileHeader.NumberOfSections+1NtHeader.OptionalHeader.sizeOfImage = newSection.virtualadderss+newSection.virtualSize扩展源文件堆内存空间为sizeOfImage,多余部分用以保存dll的.text节 部分代码如下: void CMyPack::addSection(LPCSTR newSectionName, LPCSTR dllSecName) { // 判断当前的节表是否可以添加一项 if (!CheckSectionGap(m_FileBase)) { // 特殊情况,需要移动节的位置 // 这里没有考虑该特殊情况,可以将DosHeader+0x3c位置的值修改下,然后将后面的所有内容往上提,腾出空间 } // 空间足够,直接将dll的对应节表拷贝过来 auto newSec = &SecHeder(m_FileBase)[FileHeader(m_FileBase)->NumberOfSections]; PIMAGE_SECTION_HEADER lastSec = newSec - 1; FileHeader(m_FileBase)->NumberOfSections += 1;//节数量加一 // 拷贝节表 memcpy(newSec, GetSection(dllSecName, m_DllBase), sizeof(IMAGE_SECTION_HEADER)); // 修改新节的名称 memcpy(newSec->Name, newSectionName, 7); // 修改新添加的节的属性,读写执行,包含数据和代码 newSec->Characteristics = 0xE00000E0; // 修改新添加的节的RVA和FOA,根据最后一个节的信息计算得出 newSec->VirtualAddress = lastSec->VirtualAddress + alignData(lastSec->Misc.VirtualSize, OptHeader(m_FileBase)->SectionAlignment); newSec->PointerToRawData = lastSec->PointerToRawData + alignData(lastSec->SizeOfRawData, OptHeader(m_FileBase)->FileAlignment); // 将代码段按内存页0x1000对齐,方便后面在重定位表中添加信息 newSec->SizeOfRawData = alignData(newSec->SizeOfRawData, 0x1000); // 重新分配空间,用于拷贝新的节 m_FileSize = newSec->PointerToRawData + newSec->SizeOfRawData; m_FileBase = (DWORD)realloc((LPVOID)m_FileBase, m_FileSize); } 2. 在dll中保存的壳代码必须保存的信息

dll中导出一个结构体变量,在加壳中使用GetProcAddress获取该结构体的地址,然后将源程序的相关信息填充到该结构体对应的字段中,即完成信息的存储,用于壳代码的正常执行。如下所示:

保存原始OEP–>用于壳代码交还执行权限保存区段描述符表–>用于壳模拟加载原始程序保存原始的imageBase–>用于壳代码修复原始程序的重定位保存原始程序数据目录表的重定位表的rva–>用于壳代码修复重定位保存源程序数据目录表的导入表中的RVA–>用于壳代码模拟修复IAT并且加密保存用户输入的密码–>用于实现密码判断 结构体定义如下: typedef struct _SECDATA{ DWORD sizeOfRawData = 0; DWORD pointerToRawData = 0; DWORD characteristic = 0; }SECDATA, *PSECDATA; typedef struct _SHAREDATA{ // TLS函数表的地址 DWORD tlsfnTab = 0; // TLS索引的地址 DWORD pTlsIndex = 0; // 运行密码 char pwd[20]; // 原始的区段信息 SECDATA secData[96]; // 压缩后的数据保存的rva DWORD compressDataRva = 0; // 压缩后的文件大小 DWORD compressFileSize = 0; // 压缩前的文件大小 DWORD oldFileSize = 0; // 原始imageBase DWORD oldImageBase = 0; // 原始oep DWORD oldOEP = 0; // 加密代码段的rva DWORD textRva = 0; // 加密的大小 DWORD textSize = 0; // 加密的key char xorKey = 0; // 重定位的rva DWORD relocRva = 0; // 获取导入表rva DWORD impRva = 0; }SHAREDATA, *PSHAREDATA; 3. 处理程序的TLS

TLS为本地线程存储机制,主要是为了解决多线程同步问题的,同内核对象解决线程同步问题不同,TLS主要实现是将统一变量在每个线程的独立空间中拷贝一份,即每个线程对统一变量的操作仅仅对当前线程产生影响。对于软件安全,因为TLS的回调函数会在main函数之前就被执行,因此经常被用作反调试手段。 本例对于源程序中TLS处理的方式为: 考虑到在系统加载时就需要将TLS的data构造好构系统使用,所以这里将原始的TLS数据拷贝到壳代码空间中,然后预留index空间和callBackFn结构的空间,手动在壳中构建一张新的TLS表结构,然后将数据目录表中的TLS指向壳中新构建的表,新构建的回调函数是壳中的一个函数FunA,然后在系统加载加壳后的程序时,就会调用壳的TLS回调函数,壳的回调函数根据相应标志位判断是否循环调用原始程序的TLS回调,模拟系统调用。

预留一段空间,用于保存TLS用到的数据+TLS表结构+index+callBackFn结构拷贝TLS数据到预留空间手动构建一个新的TLS表述表,将相应数据指向新的位置追加index追加callBackFn结构保存原始的函数表的位置,用于壳回调的循环调用 部分代码如下: void CMyPack::TlsHandle() { /* TLS 数据目录表的结构 typedef struct _IMAGE_TLS_DIRECTORY32 { DWORD StartAddressOfRawData; DWORD EndAddressOfRawData; DWORD AddressOfIndex; // PDWORD DWORD AddressOfCallBacks; // PIMAGE_TLS_CALLBACK * DWORD SizeOfZeroFill; union { DWORD Characteristics; struct { DWORD Reserved0 : 20; DWORD Alignment : 4; DWORD Reserved1 : 8; } DUMMYSTRUCTNAME; } DUMMYUNIONNAME; } IMAGE_TLS_DIRECTORY32; 当线程创建时,系统会自动在进程的虚拟空间中分配一段内存空间拷贝原始的 RawData中的数据到该空间中,在以后的使用中就会进使用自身的空间的内容 index的用处?? */ /* 处理方式: 对于RawData要在程序运行之前告知操作系统,不然后面就不能使用了 所以: 1. 将TLS节拷贝出来 2. 修改数据目录表的指向 3. 保存函数表rva到共享数据空间中 4. 自己构造一个函数,判断解压标志位,如果已经解压,就调用真实的函数 需要在dll中导出一个函数,用作tls的回调函数 */ // 不存在tls直接返回 if (OptHeader(m_FileBase)->DataDirectory[9].VirtualAddress == 0) { return; } // 获取TLS表的地址 PIMAGE_TLS_DIRECTORY pTls = (PIMAGE_TLS_DIRECTORY)( rvaToFoa(OptHeader(m_FileBase)->DataDirectory[9].VirtualAddress) + m_FileBase); // 计算存放TLS所需要的空间,按文件对齐 /* 预留空间包括: 1.TLS数据内容 end- start 2.TLS表的大小 sizeof(IMAGE_TLS_DIRECTORY32) 3.index位置 4字节 4.回调函数位置 8字节(包含结尾) */ DWORD tlsDataTabSize = alignData(sizeof(IMAGE_TLS_DIRECTORY32) + pTls->EndAddressOfRawData - pTls->StartAddressOfRawData + 8+8, OptHeader(m_FileBase)->FileAlignment); // 保存tls表存放的位置,用于后面计算重定位 m_tlsDataOffset = GetSection(".pack", m_FileBase)->SizeOfRawData; // 保存原始的函数表和索引的地址 m_pShareData->tlsfnTab = pTls->AddressOfCallBacks - OptHeader(m_FileBase)->ImageBase; m_pShareData->pTlsIndex = pTls->AddressOfIndex - OptHeader(m_FileBase)->ImageBase; // 扩充最后一个节的大小 加上TLS数据的大小 m_FileSize = GetSection(".pack", m_FileBase)->PointerToRawData + GetSection(".pack", m_FileBase)->SizeOfRawData+tlsDataTabSize; m_FileBase = (DWORD)realloc((LPVOID)m_FileBase, m_FileSize); // 修改函数表VA的指向,用于HOOK TLS回调 DWORD pTlsCall = (DWORD)GetProcAddress((HMODULE)m_DllBase, "_TlsHandle@12");//这里为什么出现了名称粉碎?? pTls->AddressOfCallBacks = pTlsCall - GetSection(".text", m_DllBase)->VirtualAddress - m_DllBase + GetSection(".pack", m_FileBase)->VirtualAddress + OptHeader(m_FileBase)->ImageBase; // 拷贝TLS数据 char *src = (char *)(rvaToFoa(pTls->StartAddressOfRawData - OptHeader(m_FileBase)->ImageBase) + m_FileBase); char *dest = (char *)(GetSection(".pack", m_FileBase)->PointerToRawData + GetSection(".pack", m_FileBase)->SizeOfRawData + m_FileBase); DWORD dataSize = pTls->EndAddressOfRawData - pTls->StartAddressOfRawData; memcpy(dest, src, dataSize); // 拷贝TLS表,这里将数据进行4字节对齐 dest = (char *)(GetSection(".pack", m_FileBase)->PointerToRawData + m_FileBase + GetSection(".pack", m_FileBase)->SizeOfRawData + alignData(dataSize,4)); src = (char *)pTls; memcpy(dest, src, sizeof(IMAGE_TLS_DIRECTORY32)); // 修改数据目录表的TLS的指向 OptHeader(m_FileBase)->DataDirectory[9].VirtualAddress = GetSection(".pack", m_FileBase)->VirtualAddress + GetSection(".pack", m_FileBase)->SizeOfRawData + alignData(dataSize, 4); // 修改pack节的大小 GetSection(".pack", m_FileBase)->SizeOfRawData += tlsDataTabSize; GetSection(".pack", m_FileBase)->Misc.VirtualSize = GetSection(".pack",m_FileBase)->SizeOfRawData; // 修改sizeofimage的大小 OptHeader(m_FileBase)->SizeOfImage = GetSection(".pack", m_FileBase)->VirtualAddress + GetSection(".pack", m_FileBase)->SizeOfRawData; } 4. 根据新添加的区段位置修正dll中的重定位信息

为了使加完壳之后的程序支持重定位,就必须使加完壳之后的程序的重定位表完整,因为是先运行的壳代码,所以需要让系统帮助修复壳代码的重定位,然后壳代码中再去修正源程序的重定位。 思路如下: 系统加载–>根据加壳软件的重定位表修正壳的重定位–>壳代码根据原始程序的重定位表修复原始程序的重定位 这里重要的一点就是我们要将加壳后程序的数据目录表的重定位项指向新添加区段的重定位表,所以我们需要将dll中的重定位表按照新添加区段的位置修正后拷贝到新添加区段中,让系统加载时对壳代码重定位。

修正dll重定位信息的方式如下:

修正重定位表项指向的实际内容 因为我们将.text区段追加到了原始程序的区段后面,所以这就已经相当于将一个区段进行了重定位,代码中用到的绝对地址已经发生了变化。 具体修正公式为:根据区段内偏移重新计算位置 新地址 = 源地址 - 加载基址 - 区段首地址 + 新区段首地址 + 被加壳程序的加载基址

修正重定位表结构中的内存分页首地址 因为内存分页已经不是从dll中的.text段开始的了,拷贝过来之后,前面还有很多区段,试想一下如果页地址不修复,假设第一项为0x1000分页处,修复壳代码的重定位时就找到了源程序的数据处,所以这里的页地址应当加上新区段的首地址才是修正当前区段。 修正公式如下: 新的页基址 = 旧的页基址+NewSec.virtualAdddress 注意:因为dll的代码区段为不可写状态,如果要修改重定位表指向的代码处,需要修改内存页属性,使用VirtualProtect。

构建TLS重定位表结构

因为TLS表中的所有信息都是VA,所以都是需要重定位的项,我们需要在dll的重定位表中添加这几个重定位项目,以实现TLS数据的重定位。 思路如下:

遍历原始程序的重定位表,遍历出TLS数据的重定位项目,根据重定位表的end和start进行位置判断,如果重定位表中的地址出现在此范围内,则将其拷贝出来添加TLS表中的start、end、index、callBackFnTab的重定位项,因为其保存的都是VA,所以这里我们将其重定位追加到dll的重定位表项的后面

部分代码如下: 构造TLS重定位部分

// 用于获取TLS段中的数据 DWORD CMyPack::TlsDataRelData(DWORD &relocSize) { // 如果不存在TLS表 if (OptHeader(m_FileBase)->DataDirectory[9].VirtualAddress == 0) { relocSize = 0; return 0; } // 获取TLS表的位置 PIMAGE_TLS_DIRECTORY pTls = (PIMAGE_TLS_DIRECTORY)( rvaToFoa(OptHeader(m_FileBase)->DataDirectory[9].VirtualAddress) + m_FileBase); // 获取重定位表的位置 PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)(GetSection(".reloc",m_FileBase)->PointerToRawData+ + m_FileBase); // 计算TLS数据所占的空间,按极限计算,防止TLS中数据为指针 DWORD dataSize = alignData(pTls->EndAddressOfRawData - pTls->StartAddressOfRawData,4);//对齐大小 DWORD tempSize = pTls->EndAddressOfRawData - pTls->StartAddressOfRawData;//实际大小 // 开辟堆空间保存TLS重定位数据 BYTE *relocTab = (BYTE*)calloc(dataSize + 0x0c, sizeof(BYTE)); DWORD imageBase = OptHeader(m_FileBase)->ImageBase; // 找到tlsdata段的数据重定位表的偏移 while (((pReloc->VirtualAddress + imageBase) != pTls->StartAddressOfRawData) && (pReloc->VirtualAddress != 0)) { pReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pReloc + pReloc->SizeOfBlock); } // 用于保存总共拷贝的字节数,用于填充sizeofBlock,这里仅考虑了VS在编译时会将.tls编译为一个单独的节的情况 // 当然考虑通用性也可以从start开始解析重定位表的每一项,判断当前项的地址是否超出了end DWORD cpyedSize = 0; while (((pReloc->VirtualAddress + imageBase) EndAddressOfRawData) && (pReloc->VirtualAddress!=0)) { // 将内存页直接修改到新添加的区段的页上 DWORD tempPageRva = pReloc->VirtualAddress; pReloc->VirtualAddress = m_tlsDataOffset + GetSection(".pack", m_FileBase)->VirtualAddress; memcpy(relocTab + cpyedSize, (BYTE*)pReloc, pReloc->SizeOfBlock); pReloc->VirtualAddress = tempPageRva; cpyedSize += pReloc->SizeOfBlock; pReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pReloc + pReloc->SizeOfBlock); } *(WORD*)&relocTab[cpyedSize] = (dataSize & 0x7ff) | 0x3000; //start *(WORD*)&relocTab[cpyedSize+2] = ((dataSize+4) & 0x7ff) | 0x3000; //end *(WORD*)&relocTab[cpyedSize+4] = ((dataSize+8) & 0x7ff) | 0x3000; //index *(WORD*)&relocTab[cpyedSize+6] = ((dataSize+0xc) & 0x7ff) | 0x3000; //callbackFnTab *(WORD*)&relocTab[cpyedSize + 8] = ((dataSize + sizeof(IMAGE_TLS_DIRECTORY)+4) & 0x7ff) | 0x3000; //CallBackFn *(WORD*)&relocTab[cpyedSize + 0x0c] = 0; // FnEndFlag relocSize = cpyedSize + 6 * 2; // 修正重定位表结构的sizeofBlock *(DWORD *)&relocTab[4] = relocSize; // 修改TLS表中的前两个字节的内容 指向新的VA pTls->StartAddressOfRawData = GetSection(".pack", m_FileBase)->VirtualAddress + m_tlsDataOffset + imageBase; pTls->EndAddressOfRawData = pTls->StartAddressOfRawData + tempSize; // 修改TLS表的index字段指向新的VA至最后位置 pTls->AddressOfIndex = GetSection(".pack", m_FileBase)->VirtualAddress + m_tlsDataOffset + imageBase + dataSize + sizeof(IMAGE_TLS_DIRECTORY32); // 修改TLS数据中的CallBackFunTab的位置在Index后面,共占用8个字节 DWORD tempFunAddr = pTls->AddressOfCallBacks; // 修改tab的指向 *(DWORD*)(GetSection(".pack", m_FileBase)->PointerToRawData + m_tlsDataOffset + m_FileBase + dataSize +sizeof(IMAGE_TLS_DIRECTORY32)+ 4) = tempFunAddr; // 修改tab的位置 pTls->AddressOfCallBacks = pTls->AddressOfIndex + 4; // 构造tab的结尾标志 *(DWORD*)(GetSection(".pack", m_FileBase)->PointerToRawData + m_tlsDataOffset + m_FileBase + sizeof(IMAGE_TLS_DIRECTORY32) + dataSize + 8) = 0; // 返回新构建的重定位表 return (DWORD)relocTab; }

合成整个重定位表的部分代码:

void CMyPack::fixReloc(DWORD dllBase) { // 需要添加重定位的个数,返回指针中保存了构建后的重定位表,并且返回大小 DWORD relocSize = 0; DWORD relocData = 0; relocData = TlsDataRelData(relocSize); // 定义重定位表结构体 struct TypeOffset { WORD Offset : 12; WORD Type : 4; }; // 得到dll重定位表的位置VA PIMAGE_BASE_RELOCATION relocBase = (PIMAGE_BASE_RELOCATION)(Relocation(dllBase) + dllBase); // 第1次修复,修复dll中的重定位处的内容,相当于将dll重定位到pack节处 while (relocBase->VirtualAddress) { LPVOID pageAddrVA = (LPVOID)(dllBase + relocBase->VirtualAddress); DWORD oldProtect = 0; // 这里存在BUG,如果重定位表的最后一项的地址横跨两页就会引发写入异常 // 可以先得出最后一项偏移,(last+4-virtualAddress)SizeOfBlock - 8) / 2; TypeOffset * typeoffset = (TypeOffset*)(relocBase + 1); for (DWORD i = 0; i DWORD addr = typeoffset[i].Offset + relocBase->VirtualAddress + dllBase; *(DWORD *)addr = *(DWORD *)addr - dllBase - GetSection(".text", dllBase)->VirtualAddress + GetSection(".pack", m_FileBase)->VirtualAddress + OptHeader(m_FileBase)->ImageBase; } } relocBase = (PIMAGE_BASE_RELOCATION)((DWORD)relocBase + relocBase->SizeOfBlock); VirtualProtect(pageAddrVA, 0x1000, oldProtect, &oldProtect); } /*需要拷贝原始stub部分的重定位信息,然后修正数据目录表的指向*/ // 直接将重定位表拷贝到代码的后面,设置大小直接相加 // 修改数据目录表中的重定位表的指向 OptHeader(m_FileBase)->DataDirectory[5].VirtualAddress = GetSection(".pack", m_FileBase)->VirtualAddress + GetSection(".pack", m_FileBase)->SizeOfRawData; // 保存旧的壳代码节的大小 DWORD oldRawSize = GetSection(".pack", m_FileBase)->SizeOfRawData; // 设置节的新大小,包含dll重定位大小和新构建的TLS重定位的大小 DWORD tempSize = alignData( OptHeader(m_DllBase)->DataDirectory[5].Size + relocSize,0x200); // 重新修正文件大小,加上新添加过来的dll的重定位信息的大小 GetSection(".pack", m_FileBase)->SizeOfRawData += tempSize; // 修正VirtualSize的大小,和文件大小进行对齐 GetSection(".pack", m_FileBase)->Misc.VirtualSize = GetSection(".pack", m_FileBase)->SizeOfRawData; // 修正数据目录表的大小 OptHeader(m_FileBase)->DataDirectory[5].Size =OptHeader(m_DllBase)->DataDirectory[5].Size+relocSize; // 修正dll的stub重定位表的页的位置 relocBase = (PIMAGE_BASE_RELOCATION)(Relocation(dllBase) + dllBase); DWORD oldProtect = 0; // 设置新的页偏移,还是要重复修正一遍,因为修正时修正的内容要进行改变,上面是修正了节的偏移,这里修正基址的偏移 DWORD newPageAddrOffset = GetSection(".pack", m_FileBase)->VirtualAddress - GetSection(".text",m_DllBase)->VirtualAddress; while (relocBase->VirtualAddress) { VirtualProtect((LPVOID)relocBase, relocBase->SizeOfBlock, PAGE_READWRITE, &oldProtect); relocBase->VirtualAddress += newPageAddrOffset; relocBase = (PIMAGE_BASE_RELOCATION)((DWORD)relocBase + relocBase->SizeOfBlock); VirtualProtect((LPVOID)relocBase, relocBase->SizeOfBlock, oldProtect, &oldProtect); } // 以下为对TLS重定位的修复 if (relocData != 0) { // 找到最后一块重定位表 relocBase = (PIMAGE_BASE_RELOCATION)(Relocation(dllBase) + dllBase); while (((PIMAGE_BASE_RELOCATION)((DWORD)relocBase + relocBase->SizeOfBlock))->VirtualAddress != 0) { relocBase = (PIMAGE_BASE_RELOCATION)((DWORD)relocBase + relocBase->SizeOfBlock); } } // 对空间进行扩容 m_FileSize = GetSection(".pack", m_FileBase)->PointerToRawData + GetSection(".pack", m_FileBase)->SizeOfRawData; m_FileBase = (DWORD)realloc((LPVOID)m_FileBase, m_FileSize); // 拷贝重定位表到程序的新添加的节中 BYTE *dest = (BYTE*)(GetSection(".pack", m_FileBase)->PointerToRawData + oldRawSize + m_FileBase); BYTE *src = (BYTE*)(GetSection(".reloc", m_DllBase)->VirtualAddress + m_DllBase); memcpy(dest, src, GetSection(".reloc", m_DllBase)->SizeOfRawData); // 将TLS重定位信息追加到拷贝过来的dll重定位信息的后面 if (relocData != 0) { memcpy(dest + OptHeader(m_DllBase)->DataDirectory[5].Size , (BYTE*)relocData, relocSize); } // 设置sizeofimage OptHeader(m_FileBase)->SizeOfImage = GetSection(".pack", m_FileBase)->VirtualAddress + GetSection(".pack", m_FileBase)->Misc.VirtualSize; } 4. 压缩文件

使用lz4压缩库,将除去壳代码节的所有文件进行压缩,这里有一个问题,就是全部压缩后造成系统无法正确解析程序资源,可能造成错误无法运行,但是此处我尝试了全部压缩之后没有出现问题,不过图标无法显示,如果想要显示图标可以不压缩图标所在区段。 思路如下: 将所有区段压缩后追加到新添加节的后面,并且在壳代码的数据区解压时必要的信息,用于后面的解压 具体步骤:

申请空间,保存lz4库压缩后的数据在壳的数据区保存解压需要的数据申请新空间,构建新的PE文件,仅仅保留壳的节和PE头 部分代码如下: void CMyPack::compressFile() { // 直接压缩全面所有的节,然后拷贝到.pack节的后面 // 1. 新申请一段内存空间,用于压缩前面所有的区段 int file_size = GetSection(".pack", m_FileBase)->PointerToRawData - OptHeader(m_FileBase)->SizeOfHeaders; // 预估最大空间 int compress_size = LZ4_compressBound(file_size); char *tempMem = new char[compress_size](); // 2. 开始压缩文件数据(函数返回压缩后的大小) // 加密代码段 // BUG : 要在拷贝之前加密代码段,而且此时dll已经拷贝到当前文件中了,所以需要重新定位shareData结构体的位置 // 然后将异或的数据写到共享数据段中 // 重新计算共享数据的位置 int dest_size = LZ4_compress( (char *)(OptHeader(m_FileBase)->SizeOfHeaders+m_FileBase),/*压缩前的数据*/ tempMem, /*压缩后的数据*/ file_size/*文件原始大小*/); m_pShareData->oldFileSize = file_size; // 保存压缩之后的文件的大小 m_pShareData->compressFileSize = dest_size; // 保存压缩后的数据在壳中保存的rva,用于解压时定位 m_pShareData->compressDataRva = GetSection(".pack", m_FileBase)->VirtualAddress + GetSection(".pack", m_FileBase)->SizeOfRawData; // 4. 扩充最后一个节,用于拷贝压缩后的数据到当前节中 m_FileSize += alignData(dest_size, 0x200); m_FileBase = (DWORD)realloc((LPVOID)m_FileBase, m_FileSize); // 5. 拷贝文件到新申请的空间中 char *dest = (char *)GetSection(".pack", m_FileBase)->PointerToRawData + GetSection(".pack", m_FileBase)->SizeOfRawData+m_FileBase; memcpy(dest, tempMem, alignData(dest_size, 0x200)); // 6. 释放临时空间,申请新的空间,用于构建之压缩之后的文件 delete[]tempMem; // 仅仅保留PE头和仅有的一个壳的节 DWORD newFileSize = OptHeader(m_FileBase)->SizeOfHeaders + GetSection(".pack", m_FileBase)->SizeOfRawData + alignData(dest_size, 0x200); char *newFileBase = new char[newFileSize](); // 8. 保存原始节表的信息,修正PE头中的节表,将除去节表的所有的节表的文件偏移和大小都修改为零,这样就系统会在加载时预留空间,又不占文件大小 WORD secNum = FileHeader(m_FileBase)->NumberOfSections - 1;//除去壳的节 // 修改其他节的文件偏移和文件大小以及节的属性 for (WORD i = 0; i DWORD pStart = (DWORD)GetProcAddress((HMODULE)m_DllBase, "start"); pStart = pStart - m_DllBase - GetSection(".text", m_DllBase)->VirtualAddress + GetSection(".pack", m_FileBase)->VirtualAddress; OptHeader(m_FileBase)->AddressOfEntryPoint = (DWORD)pStart; } 壳代码的实现

针对上面的加壳器,对应的壳代码,即对应的dll不再具体分析 大致流程如下:

为了实现位置无关代码,模仿shellcode通过PEB模块链获取API的方式获取用到的API将上面的压缩文件,即壳代码中的保存的原始数据解压缩,按照原始节表将各节拷贝到指定位置,即模拟系统加载原PE文件获取原始重定位信息,模拟系统修复重定位获取原始IAT信息,模拟系统IAT修复,并申请空间写入shellcode进行IAT加密跳转回原始OEP,交还控制权 完整代码如下: // dllStub.cpp : 定义 DLL 应用程序的导出函数。 // #include "stdafx.h" #include /* 壳代码: 在源程序运行之前对源程序进行的操作 1. 解密相关数据 1.1 需要相关数据的rva 1.2 需要相关数据的大小 2. 修复重定位 2.1 直接在原始程序中对重定位进行加密 2.2 需要在壳代码中保存重定位的rva 2.3 获取当前加载的地址 2.4 修改页属性 2.5 对数据进行重定位 3. 修复IAT 3.1 加密源程序IAT 3.2 在壳代码中保存导入表结构体 3.3 实现字符串hash,自己实现GetProcAddress 4. 跳转回原始OEP 4.1 需要保存原始OEP的rva 4.2 获取加载基址,得到OEP的va 4.3 直接调回原始OEP */ #include #include "lz4.h" #pragma comment(linker, "/merge:.data=.text") #pragma comment(linker, "/merge:.rdata=.text") #pragma comment(linker, "/section:.text,RWE") typedef struct _SECDATA{ DWORD sizeOfRawData = 0; DWORD pointerToRawData = 0; DWORD characteristic = 0; }SECDATA, *PSECDATA; typedef struct _SHAREDATA{ // TLS函数表的地址 DWORD tlsfnTab = 0; // TLS索引的地址 DWORD pTlsIndex = 0; // 运行密码 char pwd[20]; // 原始的区段信息 SECDATA secData[96]; // 压缩后的数据保存的rva DWORD compressDataRva = 0; // 压缩后的文件大小 DWORD compressFileSize = 0; // 压缩前的文件大小 DWORD oldFileSize = 0; // 原始imageBase DWORD oldImageBase = 0; // 原始oep DWORD oldOEP = 0; // 加密代码段的rva DWORD textRva = 0; // 加密的大小 DWORD textSize = 0; // 加密的key char xorKey = 0; // 重定位的rva DWORD relocRva = 0; // 获取导入表rva DWORD impRva = 0; }SHAREDATA, *PSHAREDATA; typedef BOOL(WINAPI *MyVirtualProtect)( _In_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flNewProtect, _Out_ PDWORD lpflOldProtect ); typedef HMODULE(WINAPI *MyLoadLibrary)( _In_ LPCSTR lpFileName ); typedef FARPROC(WINAPI *MyGetProcAddress)( _In_ HMODULE hModule, _In_ UINT ); typedef LPVOID (WINAPI *MyVirtualAlloc)( _In_opt_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flAllocationType, _In_ DWORD flProtect ); typedef BOOL (WINAPI *MyVirtualFree)( _In_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD dwFreeType ); typedef HWND (WINAPI *MyGetDlgItem)( _In_opt_ HWND hDlg, _In_ int nIDDlgItem ); typedef int (WINAPI *MyGetWindowText)( _In_ HWND hWnd, _Out_ LPCSTR lpString, _In_ int nMaxCount ); typedef int (WINAPI *MyMessageBox)( _In_opt_ HWND hWnd, _In_opt_ LPCSTR lpText, _In_opt_ LPCSTR lpCaption, _In_ UINT uType ); typedef ATOM (WINAPI *MyRegisterClass)( _In_ const WNDCLASSA *lpWndClass ); typedef LRESULT (WINAPI *MyDefWindowProc)( _In_ HWND hWnd, _In_ UINT Msg, _In_ WPARAM wParam, _In_ LPARAM lParam ); typedef HWND (WINAPI *MyCreateWindow)( _In_ DWORD dwExStyle, _In_opt_ LPCSTR lpClassName, _In_opt_ LPCSTR lpWindowName, _In_ DWORD dwStyle, _In_ int x, _In_ int y, _In_ int nWidth, _In_ int nHeight, _In_opt_ HWND hWndParent, _In_opt_ HMENU hMenu, _In_opt_ HINSTANCE hInstance, _In_opt_ LPVOID lpParam ); typedef BOOL (WINAPI *MyShowWindow)( _In_ HWND hWnd, _In_ int nCmdShow ); typedef BOOL (WINAPI *MyUpdateWindow)( HWND hWnd ); typedef BOOL (WINAPI *MyGetMessage)( _Out_ LPMSG lpMsg, _In_opt_ HWND hWnd, _In_ UINT wMsgFilterMin, _In_ UINT wMsgFilterMax ); typedef BOOL (WINAPI *MyTranslateMessage)( _In_ CONST MSG *lpMsg); typedef LRESULT (WINAPI *MyDispatchMessage)( _In_ const MSG *lpmsg ); typedef BOOL(WINAPI *MyDestroyWindow)( _In_ HWND hWnd ); typedef VOID (WINAPI *MyPostQuitMessage)( _In_ int nExitCode ); typedef VOID (WINAPI *MyExitProcess)( _In_ UINT uExitCode ); typedef void (NTAPI *TLSCall)( PVOID DllHandle, DWORD Reason, PVOID Red ); typedef LPTOP_LEVEL_EXCEPTION_FILTER (WINAPI *MySetUnhandledExceptionFilter)( _In_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter ); typedef NTSTATUS (NTAPI *MyZwSetInformationThread)( _In_ HANDLE ThreadHandle, _In_ THREADINFOCLASS ThreadInformationClass, _In_ PVOID ThreadInformation, _In_ ULONG ThreadInformationLength ); typedef HANDLE(WINAPI *MyGetCurrentThread)( VOID ); extern "C"{ // 导出共享结构体,在加壳器中将必要的信息写入 _declspec(dllexport) SHAREDATA shareData; DWORD kernelBase = 0; BOOL pwdCorrect = FALSE; DWORD dePressOK = FALSE; DWORD imageBase = 0; char cShellCode[] = "\x55\x8B\xEC\xE8\x01\x00\x00\x00\xFF\x58\xE8\x01\x00\x00\x00\xE8\x58\x83\xF5\x18\x83\xED\x20\x5D\xE8\x01\x00\x00\x00\xE9\x58\xEB\x06\xE8\x58\x8B\x00\xFF\xE0\xE8\xF6\xFF\xFF\xFF\x00\x00\x00\x00"; MyLoadLibrary myLoadLibraryA = nullptr; MyGetProcAddress myGetProcAddr = nullptr; MyVirtualProtect myVirtualProtect = nullptr; MyVirtualAlloc myVirtualAlloc = nullptr; MyVirtualFree myVirtualFree = nullptr; // 用于创建窗口的API MyGetDlgItem myGetDlgItem = nullptr; MyGetWindowText myGetWindowTextA = nullptr; MyMessageBox myMessageBoxA = nullptr; MyRegisterClass myRegisterClass = nullptr; MyDefWindowProc myDefWindowProc = nullptr; MyCreateWindow myCreateWindow = nullptr; MyShowWindow myShowWindow = nullptr; MyUpdateWindow myUpdateWindow = nullptr; MyGetMessage myGetMessage = nullptr; MyTranslateMessage myTranslateMessage = nullptr; MyDispatchMessage myDispatchMessage = nullptr; MyDestroyWindow myDestroyWindow = nullptr; MyPostQuitMessage myPostQuitMessage = nullptr; MyExitProcess myExitProcess = nullptr; // 用于反调试的API MySetUnhandledExceptionFilter mySetUnhandledExceptionFilter = nullptr; MyGetCurrentThread myGetCurrentThread = nullptr; MyZwSetInformationThread myZwSetInformationThread = nullptr; // 获取kernel32的基址 void getKernelBase() { _asm{ mov eax, fs:[0x30]; mov eax, [eax + 0x0c]; mov eax, [eax + 0x1c]; mov eax, [eax]; mov eax, [eax]; mov eax, [eax + 0x08]; mov kernelBase, eax; } } // 获取函数地址 DWORD getFunAddr(LPCSTR funName, DWORD dllBase){ PIMAGE_EXPORT_DIRECTORY pExpTab = (PIMAGE_EXPORT_DIRECTORY)(*(DWORD*)(*(DWORD *)(kernelBase + 0x3c) + kernelBase + 0x78) + kernelBase); DWORD* funcAddr = (DWORD *)(pExpTab->AddressOfFunctions + kernelBase); DWORD* nameAddr = (DWORD *)(pExpTab->AddressOfNames + kernelBase); WORD * ordAddr = (WORD*)(pExpTab->AddressOfNameOrdinals + kernelBase); DWORD funcNum = pExpTab->NumberOfFunctions; for (DWORD i = 0; i return funcAddr[ordAddr[i]] + dllBase; } } return -1; } // 异常回调函数 LONG WINAPI myUnhandledExceptionFilter(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo) { if (ExceptionInfo->ExceptionRecord->ExceptionCode==EXCEPTION_INT_DIVIDE_BY_ZERO) { ExceptionInfo->ContextRecord->Ecx = 1; if (ExceptionInfo->ContextRecord->EFlags & 0x100){ myExitProcess(0); } ExceptionInfo->ContextRecord->Dr7 = 0; return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH; } // 用于产生异常,除零异常,UEH处理 void myRaiseException() { _asm{ xor ecx, ecx; xor edx, edx; div ecx; } return; } // 设置反调试 void setAntiDebug() { // 设置异常回调 mySetUnhandledExceptionFilter(myUnhandledExceptionFilter); // 设置调试器分离 myZwSetInformationThread(myGetCurrentThread(), (THREADINFOCLASS)17, NULL, NULL); } // 获取加载基址 void getImageBase() { _asm{ mov eax, fs:[0x30]; mov eax, [eax + 0x08]; mov imageBase, eax; } return; } // 处理TLS的函数 _declspec(dllexport) void NTAPI TlsHandle(PVOID DllHandle, DWORD Reason, PVOID Red) { DWORD baseAddr = 0; _asm{ mov eax, fs:[0x30]; mov eax, [eax + 0x08]; mov baseAddr, eax; } DWORD* pTlsfnTab = (DWORD *)((shareData.tlsfnTab + baseAddr)); if (dePressOK) { while (*pTlsfnTab) { ((TLSCall)(*pTlsfnTab))(DllHandle, Reason, Red); ++pTlsfnTab; } } } // 获取需要的API void getAPi(){ // 获取壳代码需要用到的API myLoadLibraryA = (MyLoadLibrary)getFunAddr("LoadLibraryA", kernelBase); myGetProcAddr = (MyGetProcAddress)getFunAddr("GetProcAddress", kernelBase); myVirtualProtect = (MyVirtualProtect)myGetProcAddr((HMODULE)kernelBase, (UINT)"VirtualProtect"); myVirtualAlloc = (MyVirtualAlloc)myGetProcAddr((HMODULE)kernelBase, (UINT) "VirtualAlloc"); myVirtualFree = (MyVirtualFree)myGetProcAddr((HMODULE)kernelBase, (UINT)"VirtualFree"); myGetDlgItem = (MyGetDlgItem)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT)"GetDlgItem"); myGetWindowTextA = (MyGetWindowText)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT) "GetWindowTextA"); myMessageBoxA = (MyMessageBox)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT)"MessageBoxA"); // 窗口相关的API // 用于创建窗口的API myRegisterClass = (MyRegisterClass)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT) "RegisterClassA"); myDefWindowProc = (MyDefWindowProc)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT)"DefWindowProcA"); myCreateWindow = (MyCreateWindow)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT)"CreateWindowExA"); myShowWindow = (MyShowWindow)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT)"ShowWindow"); myUpdateWindow = (MyUpdateWindow)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT)"UpdateWindow"); myGetMessage = (MyGetMessage)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT)"GetMessageA"); myTranslateMessage = (MyTranslateMessage)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT)"TranslateMessage"); myDispatchMessage = (MyDispatchMessage)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT)"DispatchMessageA"); myDestroyWindow = (MyDestroyWindow)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT)"DestroyWindow"); myPostQuitMessage = (MyPostQuitMessage)myGetProcAddr(myLoadLibraryA("user32.dll"), (UINT) "PostQuitMessage"); myExitProcess = (MyExitProcess)myGetProcAddr((HMODULE)kernelBase, (UINT)"RegisterClassA"); // 用于反调试的API mySetUnhandledExceptionFilter = (MySetUnhandledExceptionFilter)myGetProcAddr((HMODULE)kernelBase, (UINT)"SetUnhandledExceptionFilter"); myGetCurrentThread = (MyGetCurrentThread)myGetProcAddr((HMODULE)kernelBase, (UINT)"GetCurrentThread"); myZwSetInformationThread = (MyZwSetInformationThread)myGetProcAddr(myLoadLibraryA("ntdll.dll"), (UINT) "ZwSetInformationThread"); } // 获取节表信息 PIMAGE_NT_HEADERS NtHeader(DWORD baseAddr) { return (PIMAGE_NT_HEADERS)(*(DWORD *)(baseAddr + 0x3c) + baseAddr); } PIMAGE_SECTION_HEADER secHeader(DWORD baseAddr) { return (PIMAGE_SECTION_HEADER)(NtHeader(baseAddr)->FileHeader.SizeOfOptionalHeader + 0x18 + (DWORD)NtHeader(baseAddr)); } // 手动调用TLS回调 void manulCallTls() { if (NtHeader(imageBase)->OptionalHeader.DataDirectory[9].VirtualAddress!=0) { // 手动调用一下TLS回调函数,模拟进程创建 TlsHandle((LPVOID)imageBase, DLL_THREAD_ATTACH, NULL); } } // 解密区段 void xorSection() { DWORD baseAddr = 0; _asm{ mov eax, fs:[0x30]; mov eax, [eax + 0x08]; mov baseAddr, eax; } BYTE *textVa = (BYTE*)(baseAddr + shareData.textRva); for (DWORD i = 0; i switch (uMsg) { case WM_DESTROY: myPostQuitMessage(0); break; case WM_COMMAND: { switch (LOWORD(wParam)) { case 0x1000: { if (HIWORD(wParam) == BN_CLICKED) { char buff[20] = { 0 }; HWND hEdit = myGetDlgItem(hwndDlg, 0x1001); myGetWindowTextA(hEdit, buff, 20); if (!strcmp(buff,shareData.pwd)) { pwdCorrect = TRUE; myDestroyWindow(hwndDlg); } else{ myMessageBoxA(hwndDlg, "密码错误", "通知", 0); } } break; } default: break; } break; } default: break; } return myDefWindowProc(hwndDlg,uMsg,wParam,lParam); } // 拷贝shellCode到申请的空间 void myMemmov(char *dest, char *src, DWORD num) { _asm{ mov edi, dest; mov esi, src; mov ecx, num; rep movsb; } } // 修改内存页属性 void changePageCharac() { DWORD baseAddr = 0; _asm{ mov eax, fs:[0x30]; mov eax, [eax + 0x08]; mov baseAddr, eax; } WORD secCount = NtHeader(baseAddr)->FileHeader.NumberOfSections-1; PIMAGE_SECTION_HEADER pSec = secHeader(baseAddr); DWORD oldProcted = 0; for (WORD i = 0; i DWORD baseAddr = 0; _asm{ mov eax, fs:[0x30]; mov eax, [eax + 0x08]; mov baseAddr, eax; } WORD secCount = NtHeader(baseAddr)->FileHeader.NumberOfSections-1;//最后一个节不修改 PIMAGE_SECTION_HEADER pSec = secHeader(baseAddr); DWORD oldProcted = 0; for (WORD i = 0; i DWORD baseAddr = 0; _asm{ mov eax, fs:[0x30]; mov eax, [eax + 0x08]; mov baseAddr, eax; } // 申请一段内存空间用于保存解压后的数据 DWORD destData = (DWORD)myVirtualAlloc(NULL, shareData.oldFileSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); // BUG,这里的数据被覆盖截断了 LZ4_uncompress_unknownOutputSize( (char *)(shareData.compressDataRva + baseAddr),/*压缩后的数据*/ (char *)destData, /*解压出来的数据*/ //(char *)(secHeader(baseAddr)->VirtualAddress+baseAddr), shareData.compressFileSize,/*压缩后的大小*/ shareData.oldFileSize/*压缩前的大小*/); // 将解压后的数据拷贝到对应的虚拟地址的位置 int i = 0; // Debug类型中第一个节是用于增量编译的节,不能使用sizeofRawData和pointerToRawData判断 // 会出问题,这里使用characteristic来判断 while (shareData.secData[i].characteristic)++i; --i; for (; i >= 0; --i) { myMemmov((char *)(secHeader(baseAddr)[i].VirtualAddress + baseAddr), //(char *)(shareData.oldFileSize + secHeader(baseAddr)->VirtualAddress+baseAddr - shareData.secData[i].sizeOfRawData), (char *)(shareData.oldFileSize + destData - shareData.secData[i].sizeOfRawData), shareData.secData[i].sizeOfRawData); shareData.oldFileSize -= shareData.secData[i].sizeOfRawData; } myVirtualFree((LPVOID)destData, 0, MEM_RELEASE); } // 修复重定位 void fixReloc() { if (shareData.relocRva == 0) { // 如果源程序不支持重定位,直接返回 return; } struct TypeOffset { WORD Offset : 12; WORD Type : 4; }; DWORD loadBase = 0; _asm{ mov eax, fs:[0x30]; mov eax, [eax + 0x08]; mov loadBase, eax; } PIMAGE_BASE_RELOCATION relocBase = (PIMAGE_BASE_RELOCATION)(loadBase + shareData.relocRva); while (relocBase->VirtualAddress) { LPVOID pageAddrVA = (LPVOID)(loadBase + relocBase->VirtualAddress); DWORD oldProtect = 0; myVirtualProtect(pageAddrVA, 0x1000, PAGE_READWRITE, &oldProtect); DWORD relocCount = (relocBase->SizeOfBlock - 8) / 2; TypeOffset * typeoffset = (TypeOffset*)(relocBase + 1); for (DWORD i = 0; i DWORD addr = typeoffset[i].Offset + relocBase->VirtualAddress + loadBase; *(DWORD *)addr = *(DWORD *)addr - shareData.oldImageBase + loadBase; } } relocBase = (PIMAGE_BASE_RELOCATION)((DWORD)relocBase + relocBase->SizeOfBlock); myVirtualProtect(pageAddrVA, 0x1000, oldProtect, &oldProtect); } } // 修复IAT void fixIAT(){ // 获取导入表VA DWORD imageBase = 0; _asm{ mov eax, fs:[0x30]; mov eax, [eax + 0x08]; mov imageBase, eax; } PIMAGE_IMPORT_DESCRIPTOR pImpTab = (PIMAGE_IMPORT_DESCRIPTOR)(imageBase + shareData.impRva); DWORD IatCallAddr = (DWORD)myVirtualAlloc(NULL, 50 * 0x51e, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 用于统计API的总的个数 int ApiCount = 0; while (pImpTab->Name) { LPCSTR dllName = (LPCSTR)(pImpTab->Name + imageBase); DWORD *IAT = (DWORD*)(pImpTab->FirstThunk+imageBase); while (*IAT) { // 拷贝shellCode到申请的空间中,BUG,这里存在序号导入的情况,需要判断序号 myMemmov((char *)IatCallAddr, cShellCode, 48); if ((*IAT)&0x80000000) { // 如果是序号导入,就直接按照序号获取函数地址 *(DWORD *)(IatCallAddr + 44) = (DWORD)myGetProcAddr(myLoadLibraryA(dllName), (*IAT)&0x7fffffff); *IAT = IatCallAddr; } else{ // 如果是名称导入,则会利用IAT中的RVA转到importbyname处根据导入名称进行加载。 *(DWORD *)(IatCallAddr + 44) = (DWORD)myGetProcAddr(myLoadLibraryA(dllName), (UINT)(*IAT + imageBase + 0x02)); /************************************************************************/ // BUG :因为MFC直接从该API的地址中取内容,所以加密后会出错,这里将其过滤出来 // 这里可能就是导入表中的变量,我们需要判断该地址处的属性是否具有执行权限,如果没有则 // 证明为变量,则不进行修复。 /************************************************************************/ if ((!strcmp((LPCSTR)(*IAT + imageBase + 0x02), "_wcmdln")) || (!strcmp((LPCSTR)(*IAT + imageBase + 0x02), "_acmdln"))) { *IAT = *(DWORD *)(IatCallAddr + 44); } else{ *IAT = IatCallAddr; } } IatCallAddr += 50; ++ApiCount; // 当API的个数超过了空间,再次申请空间 if (ApiCount >= 0x51e) { IatCallAddr = (DWORD)myVirtualAlloc(NULL, 50 * 0x51e, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); ApiCount = 0; } ++IAT; // BUG: 忘记跳过名称前的序号 // *IAT = (DWORD)myGetProcAddr(myLoadLibraryA(dllName), (LPCSTR)(*IAT + imageBase+0x02)); // ++IAT; } ++pImpTab; } } // 弹出密码框 void enterPwd() { DWORD imageBase = 0; _asm{ mov eax, fs:[0x30]; mov eax, [eax + 0x08]; mov imageBase, eax; } // 创建窗口类 WNDCLASSA wndClass = { 0 }; wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = wndProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = (HINSTANCE)imageBase; wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = "_WND"; // 注册窗口类 myRegisterClass(&wndClass); // 创建窗口 HWND hWnd = myCreateWindow(0, wndClass.lpszClassName, "提示框", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 200, 150, NULL, NULL, wndClass.hInstance, NULL); // 创建按钮控件 HWND hButton = myCreateWindow(0,"button", "确认", BS_PUSHBUTTON|WS_CHILD|WS_VISIBLE, 80, 50, 40, 30, hWnd, (HMENU)0x1000, (HINSTANCE)imageBase, NULL); // 创建编辑框控件 HWND hEdit = myCreateWindow(0,"edit", "", WS_BORDER | WS_CHILD | WS_VISIBLE,60, 20, 80, 20, hWnd, (HMENU)0x1001, (HINSTANCE)imageBase, NULL); // 显示窗口 myShowWindow(hWnd, SW_SHOW); // 更新窗口 myUpdateWindow(hWnd); // 消息循环 MSG msg = { 0 }; while (myGetMessage(&msg,NULL,0,0)) { myTranslateMessage(&msg); myDispatchMessage(&msg); } if (!pwdCorrect) { ExitProcess(0); } } // 导出的新的OEP,壳代码的执行点 _declspec(dllexport) _declspec(naked) void start(){ _asm pushad // 获取基址 getKernelBase(); // 获取加载基址 getImageBase(); // 获取API getAPi(); // 设置反调试 setAntiDebug(); // 抛出除零异常 myRaiseException(); // 输入密码框 enterPwd(); // 修改内存页属性 changePageCharac(); // 解压缩数据 depress(); // 解密代码段 xorSection(); // 抛出除零异常 myRaiseException(); // 修复重定位 fixReloc(); // 修复并加密IAT fixIAT(); // 恢复内存页属性 recoverPageCharac(); // 手动调用TLS回调函数 manulCallTls(); // 跳转到OEP _asm{ mov eax, fs:[0x30]; mov eax, [eax + 0x08]; add shareData.oldOEP, eax; popad; jmp shareData.oldOEP; ret; } } } 总结

按照以上加壳流程,加壳后的文件结构大致如下图: 在这里插入图片描述 对于该C++简单加壳器的实现,主要是对PE文件结构的拆解与拼凑,里面涉及到比较详细PE文件的基础知识,包括系统加载PE文件的流程,包括拉伸PE文件,修复重定位,修复IAT等等一系列的加载机制,对程序加壳类似对这些程序的HOOK,系统加载我们的壳代码,我们的壳模拟系统区加载PE文件,以实现在原始程序运行之前对运行环境的检测。 针对该项目的想法:该壳比较简单,没有涉及到源程序OPCODE的修改,即壳和源程序是骨肉分离的,要脱壳只要找到分界线,dump处内存即可。 对于强壳的实现,应该会结合反汇编引擎,对指令解析后使用自身的指令进行替换。比方或VMP,其就会对一段指令解析后按照vmp的指令重新解析,跳转到自身的节中执行,这样改变了源程序流程的壳与源程序是骨肉相连的,不是简单的内存dump就能获取完整的原始的程序信息的。所以脱壳页比较复杂



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有