脱壳与加壳-加壳-4-修复dll文件的重定位表

1 先将一个dll文件load到内存,然后找到dll文件的第一个区段也就是我们的代码段加数据段也就是壳代码

2 然后将这个dll的第一个区段复制到加壳程序的最后一个区段

3 由于ImageBase发生变化 需要修复重定位表,要修复重定位表就得获取重定位表的信息,两种方案:1 读取未加入到内存的dll文件 拿到重定位表信息 2 拿到载入到内存当中的dll文件重定位表信息

4 拿到重定位表容易,但是修复比较困难,因为要修复的是在FileBuff中的重定位表

 

修复重定位表

通过拿到重定位表,然后把这个dll文件中的全局变量,读出来,然后保存到exe程序下来处理,但是需要注意的是这里是把dll加载后,得到的其实是内存的地址,然后再转移到FileBuff的文件对齐里面

代码实现

//exe.cpp
#include"Exe.h"
CPeUtil::CPeUtil()
{
FileBuff = NULL;
pDosHeader = NULL;
pNtHeader = NULL;
pOptionHeader = NULL;
pFileHeader = NULL;
}
CPeUtil::~CPeUtil()
{
if (FileBuff)
{
delete[] FileBuff;
}
}
BOOL CPeUtil::LoadFile(const char* path)
{
//打开文件,获得文件句柄
HANDLE hFile = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//获得文件大小
FileSize = GetFileSize(hFile, 0);
//给FileBuff开辟空间来存储文件内容
FileBuff = new char[FileSize];
//读取文件到缓冲区
DWORD RealRead = 0;
BOOL IfSuccess = ReadFile(hFile, FileBuff, FileSize, &RealRead, NULL);
if (IfSuccess == FALSE)
{
MessageBoxA(0, "打开文件失败", "报错", MB_OK);
return FALSE;
}
CloseHandle(hFile);
return TRUE;
}
BOOL CPeUtil::InitFileInfo()
{
pDosHeader = (PIMAGE_DOS_HEADER)FileBuff;
pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + FileBuff);
pFileHeader = &pNtHeader->FileHeader;
pOptionHeader = &pNtHeader->OptionalHeader;
return 0;
}
DWORD CPeUtil::GetAlignSize(DWORD RealSize, DWORD AlignSize)
{
if (RealSize % AlignSize == 0)
{
return RealSize;
}
else
{
return (RealSize / AlignSize + 1) * AlignSize;
}
}
PIMAGE_SECTION_HEADER CPeUtil::GetLastSection()
{
PIMAGE_SECTION_HEADER FirstSection = IMAGE_FIRST_SECTION(pNtHeader);
FirstSection = FirstSection + pFileHeader->NumberOfSections - 1;
return FirstSection;
}
BOOL CPeUtil::SaveFile(const char* path)
{
HANDLE hFile = CreateFileA(path, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD RealInput = 0;
BOOL ret = WriteFile(hFile, FileBuff, FileSize, &RealInput, NULL);
CloseHandle(hFile);
return TRUE;
}
BOOL CPeUtil::InsertSection(const char* SectionName, DWORD ShellCodeSize, char* ShellCodeBuff, DWORD dwAttribute)
{
//1 先要判断区段表后面 是否足够存放两个区段头的地址
DWORD k = pFileHeader->NumberOfSections;
k *= sizeof(IMAGE_SECTION_HEADER);//区段数*每个区段头的大小=整个区段头有多大
k = k + 504; //总的区段头的大小+可选PE头的结尾位置的地址
//利用文件对齐来计算剩余的值
DWORD FileAlignment = pOptionHeader->FileAlignment;
DWORD Surplus = FileAlignment - k % FileAlignment;
//Surplus就是剩下的内容了
if (Surplus < 2 * sizeof(IMAGE_SECTION_HEADER))
{
MessageBoxA(0, "区段剩余内存不够", "报错", MB_OK);
return 0;
}
//2 创建新的缓冲区来存放新的PE文件,因为这里假设添加了ShellCode进去
//这里需要注意的是需要注意对齐值的应用,获取对齐后的PE文件大小
DWORD newFileSize = GetAlignSize(FileSize + ShellCodeSize, pOptionHeader->FileAlignment);
//3 创建新的缓冲区来存放PE文件
char* newFileBuff = new char[newFileSize];
memcpy_s(newFileBuff, newFileSize, FileBuff, FileSize);
FileSize = newFileSize;
delete[] FileBuff;
FileBuff = newFileBuff;
//4更新PE文件信息
InitFileInfo();
//5给新增的区段添加区段头
PIMAGE_SECTION_HEADER LastSection = GetLastSection();
LastSection++;//也就是新添加的区段的首个区段
//给新区段头设置属性
//设置区段内存大小,省事直接安装内存大小对齐
LastSection->Misc.VirtualSize = GetAlignSize(ShellCodeSize, pOptionHeader->SectionAlignment);
//设置名称
strcpy_s((char*)LastSection->Name, 8, SectionName);
//设置区段文件大小
LastSection->SizeOfRawData = GetAlignSize(ShellCodeSize, pOptionHeader->FileAlignment);
//设置偏移值,在内存中的偏移值
PIMAGE_SECTION_HEADER PreLastSectionHeader = GetLastSection();
//新加的区段的内存中的偏移=上一个区段内存中的偏移+内存中的大小对齐后的值
LastSection->VirtualAddress = PreLastSectionHeader->VirtualAddress
+ GetAlignSize(PreLastSectionHeader->Misc.VirtualSize, pOptionHeader->SectionAlignment);
//设置文件中的偏移
LastSection->PointerToRawData = PreLastSectionHeader->PointerToRawData +
+PreLastSectionHeader->SizeOfRawData;
LastSection->Characteristics = dwAttribute;
//修改Section数量
pFileHeader->NumberOfSections++;
//修改整个PE文件在内存中的大小
pOptionHeader->SizeOfImage += GetAlignSize(ShellCodeSize, pOptionHeader->SectionAlignment);
//将壳代码放到新的区段
char* SectionAddr = GetLastSection()->PointerToRawData + FileBuff;
memcpy(SectionAddr, ShellCodeBuff, ShellCodeSize);

return TRUE;
}
BOOL CPeUtil::EncodeSections()
{
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
int key = 0x51;
//进行异或来加密
char* pData = FileBuff + (DWORD)pSectionHeader->PointerToRawData;
//pData指向文件偏移中的区段的首地址
//SizeofRawData表示区段在文件中对齐后的大小
for (int i = 0; i < pSectionHeader->SizeOfRawData; i++)
{
pData[i] = pData[i] ^ key;
}
return TRUE;
}
DWORD CPeUtil::GetRealOEP()
{
DWORD a = pOptionHeader->AddressOfEntryPoint + pOptionHeader->ImageBase;
return a;
}
BOOL CPeUtil::SetShellOEP(DWORD OepRva)
{
pOptionHeader->AddressOfEntryPoint = OepRva + GetLastSection()->VirtualAddress + OepRva;
return 0;
}
typedef struct _PACKINFO
{
DWORD ShellOEP;
DWORD RealOEP;
}PACKINFO, * PPACKINFO;
BOOL CPeUtil::RepairRelco(DWORD ImageBase)
{
PIMAGE_DOS_HEADER pDllDosHeader = (PIMAGE_DOS_HEADER)ImageBase;
PIMAGE_NT_HEADERS pDllNtHeader = (PIMAGE_NT_HEADERS)(pDllDosHeader->e_lfanew + ImageBase);
PIMAGE_OPTIONAL_HEADER pDllOptionalHeader = &pDllNtHeader->OptionalHeader;
IMAGE_DATA_DIRECTORY DataDirctory = pDllOptionalHeader->DataDirectory[5];
PIMAGE_BASE_RELOCATION pDllRelocation = (PIMAGE_BASE_RELOCATION)(DataDirctory.VirtualAddress + ImageBase);
PIMAGE_SECTION_HEADER DllFirstSecion = IMAGE_FIRST_SECTION(pDllNtHeader);
while (pDllRelocation->SizeOfBlock != 0)
{
DWORD reNumber = (pDllRelocation->SizeOfBlock - 8) / 2;
char* beginAddr = (char*)pDllRelocation;
beginAddr += 8;
for (int i = 0; i < reNumber; i++)
{
WORD* prelocRva = (WORD*)beginAddr;
if (*prelocRva && 0x3000)
{
//这个数据是有效的
//取word的后12位+大偏移 =rva
WORD repairRva = (*prelocRva & 0x0FFF) + pDllRelocation->VirtualAddress;
//获取重定位表变量的地址
DWORD* relRepairAddr = (DWORD*)(ImageBase + repairRva);
//这里获得的是dll文件中的全局变量的地址
//还得把这个地址给FileBuff
//先拿到新添加区段的起始地址
DWORD newFileSecion = (DWORD)(GetLastSection()->PointerToRawData + FileBuff);
//安装内存对齐后的偏移
DWORD newSectionAddr = GetLastSection()->VirtualAddress + pOptionHeader->ImageBase;
//获取变量在新添加的区段地址的FileBuff中的位置
DWORD destAddr = (DWORD)relRepairAddr - DllFirstSecion->VirtualAddress - ImageBase + newSectionAddr;


//计算变量rva在FileBuff中的地址
DWORD OldProtect = 0;
//*((DWORD*)destAddr) = (*(DWORD*)destAddr - ImageBase) - DllFirstSecion->VirtualAddress + newSectionAddr;
}
beginAddr += 2;
}
pDllRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pDllRelocation + pDllRelocation->SizeOfBlock);
}
return TRUE;
}
int main()
{
CPeUtil a;
a.LoadFile("F:\\study\\Poj_Tools\\PETool\\PETool 1.0.0.5.exe");
a.InitFileInfo();
a.EncodeSections();
HMODULE hModule = LoadLibraryA("E:\\Project_Sum\\Encode\\pack\\Release\\pack.dll");
PPACKINFO ppackinfo = (PPACKINFO)GetProcAddress(hModule, "g_PackInfo");
ppackinfo->RealOEP = a.GetRealOEP();
//获取壳代码的区段
PIMAGE_DOS_HEADER DllDosHeader = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS DllNtHeader = (PIMAGE_NT_HEADERS)(DllDosHeader->e_lfanew + (DWORD)hModule);
PIMAGE_SECTION_HEADER DllFirstSectionHeader = IMAGE_FIRST_SECTION(DllNtHeader);
char* SectionBuff = (char*)(DllFirstSectionHeader->VirtualAddress + (DWORD)hModule);
a.InsertSection("Sna1lGo", DllFirstSectionHeader->Misc.VirtualSize, SectionBuff, 0xE00000E0);
a.RepairRelco((DWORD)hModule);
DWORD OepRva = ppackinfo->ShellOEP - (DWORD)hModule;
DWORD offset = OepRva - DllFirstSectionHeader->VirtualAddress;
a.SetShellOEP(offset);
a.SaveFile("F://pack.exe");
return 0;


}

//exe.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<Windows.h>
#include<iostream>
#include<string>
class CPeUtil
{
public:
CPeUtil();
~CPeUtil();
BOOL LoadFile(const char* path);//加载PE文件
BOOL InitFileInfo();//初始化
BOOL InsertSection(const char* SectionName, DWORD ShellCodeSize, char* ShellCodeBuff, DWORD dwAttribute);//添加区段函数,分别是区段名字,壳代码大小,壳代码指针,区段属性
DWORD GetAlignSize(DWORD RealSize, DWORD AlignSize);
PIMAGE_SECTION_HEADER GetLastSection();
BOOL SaveFile(const char* path);
BOOL EncodeSections();
DWORD GetRealOEP();
BOOL SetShellOEP(DWORD OepRva);
BOOL RepairRelco(DWORD ImageBase);
private:
char* FileBuff;
DWORD FileSize;
PIMAGE_DOS_HEADER pDosHeader;
PIMAGE_NT_HEADERS pNtHeader;
PIMAGE_OPTIONAL_HEADER pOptionHeader;
PIMAGE_FILE_HEADER pFileHeader;
};
//dll.cpp
#include"pack.h"
//将代码段数据段合并在一起
#pragma comment(linker,"/merge:.data=.text")
#pragma comment(linker,"/merge:.rdata=.text")
#pragma comment(linker,"/section:.text,RWE")
_declspec(naked) void packStart()
{
//保存寄存器环境
__asm pushad
//壳代码逻辑
GetImportantModule();
GetFunctions();
DeCodeSections();
//恢复寄存器环境再JMP回原始oep
__asm popad
__asm jmp g_PackInfo.RealOEP
}
//定义的全局变量
MyLoadLibraryExA g_MyLoadLibraryExA = NULL;
MYGetProcAddress g_MYGetProcAddress = NULL;
MyGetModuleHandleA g_MyGetModuleHandleA = NULL;
MyVirtualProtect g_MyVirtualProtect = NULL;
//动态获取dll基址,通过TEB和PEB
DWORD GetImportantModule()
{
DWORD dwBase = 0;
__asm
{
mov eax, dword ptr fs : [0x30]
mov eax, dword ptr[eax + 0xc]
mov eax, dword ptr[eax + 0x1c]
mov eax, [eax]
mov eax, dword ptr[eax + 0x8]
mov dwBase, eax
}
return dwBase;
}
//获取函数地址
DWORD MyGetProcAddress(DWORD hModule, LPCSTR funName)
{
//因为要通过导出表来访问,所以需要得到DOS头NT头
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + hModule);
//获取导出表
DWORD ExportTableVa = pNtHeader->OptionalHeader.DataDirectory[0].VirtualAddress;
PIMAGE_EXPORT_DIRECTORY ExportTable = (PIMAGE_EXPORT_DIRECTORY)(ExportTableVa + hModule);
//找到导出名称表,序号表,地址表来访问获取
DWORD* NameTable = (DWORD*)(ExportTable->AddressOfNames + hModule);
DWORD* AddrTable = (DWORD*)(ExportTable->AddressOfFunctions + hModule);
WORD* NumberTable = (WORD*)(ExportTable->AddressOfNameOrdinals + hModule);
for (int i = 0; i < ExportTable->NumberOfNames; i++)
{
//获取函数名字
char* FunNameTemp = (char*)(NameTable[i] + hModule);
if (!strcmp(FunNameTemp, funName))
{
//名称比对成功
return AddrTable[NumberTable[i]];
}
}
return 0;//表示没有找到
}
//动态获取函数地址
void GetFunctions()
{
//1 获取kernel32或者Kernelbase模块的基址
DWORD pKernelBase = GetImportantModule();
//2 得到LoadLibraryEx函数地址,拿到这个函数地址后
//我们就可以想加载那个dll加载那个dll然后拿到对应的函数地址
g_MyLoadLibraryExA = (MyLoadLibraryExA)MyGetProcAddress(pKernelBase, "LoadLibraryExA");
//3 获取kernel32基址
HMODULE Kernel32Base = g_MyLoadLibraryExA("kernel32.dll", 0, 0);
//4 获取GetProcAddress函数
g_MYGetProcAddress = (MYGetProcAddress)MyGetProcAddress((DWORD)Kernel32Base, "GetProcAddress");
//5 拿到GetModuleHandleA函数
g_MyGetModuleHandleA = (MyGetModuleHandleA)g_MYGetProcAddress(Kernel32Base, "GetModuleHandleA");
//想要什么函数获取什么函数
//获取VirtualProtect
g_MyVirtualProtect = (MyVirtualProtect)g_MYGetProcAddress(Kernel32Base, "VirtualProtect");
}

//实现解密,解析得到DOS头,NT头
BOOL DeCodeSections()
{
int key = 0x51;
//获取当前进程的模块基址
HMODULE hModule = g_MyGetModuleHandleA(0);
//获取需要解密的模块的首地址
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)hModule + pDosHeader->e_lfanew);
PIMAGE_SECTION_HEADER FirstSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
char* SectionBuff = (char*)(FirstSectionHeader->VirtualAddress + (DWORD)hModule);
//修改代码段的内存属性
DWORD OldProtect = 0;
g_MyVirtualProtect(SectionBuff, FirstSectionHeader->SizeOfRawData,
PAGE_READWRITE, &OldProtect);
for (int i = 0; i < FirstSectionHeader->SizeOfRawData; i++)
{
SectionBuff[i] = SectionBuff[i] ^ 0x51;
}
//再复原内存属性
g_MyVirtualProtect(SectionBuff, FirstSectionHeader->SizeOfRawData,
OldProtect, &OldProtect);
return TRUE;
}


//dll.h
#pragma once
#pragma once
#include<Windows.h>
typedef struct _PACKINFO
{
DWORD ShellOEP;
DWORD RealOEP;
}PACKINFO, * PPACKINFO;
extern "C" _declspec(dllexport) PACKINFO g_PackInfo;
typedef HMODULE(WINAPI* MyLoadLibraryExA)(LPCSTR lpLibFileName,
HANDLE hFile,
DWORD dwFlags
);
typedef FARPROC(WINAPI* MYGetProcAddress)(HMODULE hModule,
LPCSTR lpProcName
);
typedef HMODULE(WINAPI* MyGetModuleHandleA)(
LPCSTR lpModuleName
);
typedef BOOL(WINAPI* MyVirtualProtect)(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);
DWORD GetImportantModule();
DWORD MyGetProcAddress(DWORD hModule, LPCSTR funName);
void GetFunctions();
BOOL DeCodeSections();
void packStart();
PACKINFO g_PackInfo = { (DWORD)packStart,0 };