前置知识:
PE头
导数表
导出表
重定位表
TLS
壳的基本原理
对目标程序添加一段壳代码,目标PE执行的时候,先执行壳代码,之后再回到原始OEP
难点
API调用问题
重定位问题
信息交互问题
调试(非常浪费时间)
被加壳程序的随机基址
被加壳程序的导入表
动态加密
TLS处理问题
实现思路
读取一个文件,并判断目标文件是否是 PE 文件
- 使用 CreateFIle 打开一个文件
- 使用函数 GetFileSize 获取文件的大小
- 使用前面获取到的大小申请一块堆空间
- 从文件中读取内容并保存到堆空间中
- 判断 Dos 头中和 Nt 头中的字段
- 如果不兼容 dll 加壳,还需要判断目标是否是 exe
BOOL Pack::LoadFile(LPCWSTR path)
{
HANDLE file = CreateFileW(path,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if (file == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, L"文件打开失败", L"错误", MB_OK | MB_ICONERROR);
ExitProcess(-1);
}
size = GetFileSize(file, NULL);
data = (ULONG)malloc(size);
DWORD bytes = 0;
if (!ReadFile(file, (LPVOID)data, size, &bytes, NULL))
{
MessageBox(NULL, L"文件读取失败", L"错误", MB_OK | MB_ICONERROR);
ExitProcess(-1);
}
if (DosHeader(data)->e_magic != IMAGE_DOS_SIGNATURE ||
NtHeader(data)->Signature != IMAGE_NT_SIGNATURE)
{
MessageBox(NULL, L"不是有效的PE文件", L"错误", MB_OK | MB_ICONERROR);
ExitProcess(-1);
}
printf("文件加载完成\n\n");
CloseHandle(file);
return TRUE;
}
读取壳代码对应的 dll 文件,并保存相关信息
- 将模块展开到当前应用程序的虚拟空间中
- 获取到壳代码的入口函数,并计算其区段内偏移,后续用于求新偏移
新oeprva = startva - dll基址 - 所在区段rva + 移动后的区段rva
- 将共享数据获取到,加壳器主要负责向内写入数据
VOID Pack::LoadStub(LPCWSTR path)
{
stub = (ULONG)LoadLibraryExW(path,NULL,DONT_RESOLVE_DLL_REFERENCES);
if (stub == NULL)
{
MessageBox(NULL, L"文件打开失败", L"错误", MB_OK | MB_ICONERROR);
ExitProcess(-1);
}
ULONG va = (ULONG)GetProcAddress((HMODULE)stub, "start");
offset = va - stub - GetSection(stub, ".text")->VirtualAddress;
share = (PSHAREDATA)GetProcAddress((HMODULE)stub, "share");
printf("Dll加载完成\n\n");
}
将壳代码的区段[表]直接的复制到PE文件中
1. 通过名称找到 src_name 对应于 dll 的区段表
2. 找到 PE 文件内新区段的位置,并设置区段数量 +1
3. 将目标区段的区段属性复制到当前的新区段表内
4. 重新计算出新区段在 PE 文件中的起始 FOA\RVA
5. 重新计算 PE 文件的大小以及 SizeOfImage 的值
6. 扩充 PE 文件到指定的大小并结束当前函数
VOID Pack::CopySection(LPCSTR src_name,LPCSTR dest_name)
{
auto src_section = GetSection(stub, ".text");
auto last_section = &IMAGE_FIRST_SECTION(NtHeader(data))[FileHeader(data)->NumberOfSections - 1];
auto new_section = last_section + 1;
FileHeader(data)->NumberOfSections++;
memset(new_section, 0, sizeof(IMAGE_SECTION_HEADER));
memcpy(new_section, src_section, sizeof(IMAGE_SECTION_HEADER));
memcpy(new_section->Name, dest_name, strlen(dest_name) + 1);
//newRVA = oldRVA + 内存对齐
new_section->VirtualAddress = last_section->VirtualAddress + Aligment(last_section->Misc.VirtualSize,OptionalHeader(data)->SectionAlignment);
//文件偏移 = 旧文件偏移 + 内存对齐
new_section->PointerToRawData = last_section->PointerToRawData + Aligment(last_section->SizeOfRawData,OptionalHeader(data)->FileAlignment);
//文件大小 + 偏移
this->size = new_section->SizeOfRawData + new_section->PointerToRawData;
data = (ULONG)realloc((LPVOID)data, this->size);
//映像总大小 = RVA + 对齐前的大小
OptionalHeader(data)->SizeOfImage = new_section->VirtualAddress + new_section->Misc.VirtualSize;
printf("区段表拷贝完成\n\n");
}
备份,修复重定位表
1.备份原程序的重定位表地址,将原程序的重定位写入到通讯结构体中,
将dll的重定位表替换到原程序的重定位表
2.在壳代码中,系统会帮我修复壳代码的重定位,待原程序解密之后,
在壳代码中获取通讯结构体,获取原程序重定位表,修复原程序重定位
VOID Pack::BackupReloc()
{
//保存数据
//文件在内存中的地址
DWORD DllImageBase = OptionalHeader(stub)->ImageBase;
share->dwRelocRva = OptionalHeader(data)->DataDirectory[5].VirtualAddress;
share->dwRelocSize = OptionalHeader(data)->DataDirectory[5].Size;
share->OldImageBase = OptionalHeader(data)->ImageBase;
auto DllBaseReloc = (PIMAGE_BASE_RELOCATION)
(OptionalHeader(stub)->DataDirectory[5].VirtualAddress + DllImageBase);
DWORD DllRelocSize = OptionalHeader(stub)->DataDirectory[5].Size;
AddSection(".Sec", DllRelocSize + 8);
auto NewSection = GetSection(data, ".Sec");
auto OldSection = GetSection(stub, ".text");
auto PackSection = GetSection(data, ".pack");
//文件偏移 + 首地址
auto NewSentionReloc = (PIMAGE_BASE_RELOCATION)(NewSection->PointerToRawData + data);
//rva + 基址
DWORD OldSectionAddr = (DWORD)(OldSection->VirtualAddress + stub);
memcpy((DWORD*)NewSentionReloc, (DWORD*)(DllBaseReloc), DllRelocSize);
while (NewSentionReloc->VirtualAddress)
{
//新的内存页起始RVA = 原RVA - 原段基址 +.pack段基址
NewSentionReloc->VirtualAddress = NewSentionReloc->VirtualAddress - (OldSectionAddr - stub) + PackSection->VirtualAddress;
NewSentionReloc = (PIMAGE_BASE_RELOCATION)(NewSentionReloc->SizeOfBlock + (DWORD)NewSentionReloc);
}
//替换原程序重定位表
OptionalHeader(data)->DataDirectory[5].VirtualAddress = NewSection->VirtualAddress;
OptionalHeader(data)->DataDirectory[5].Size = DllRelocSize;
}
备份,修复重定位表
1.备份原程序的重定位表地址,将原程序的重定位写入到通讯结构体中,
将dll的重定位表替换到原程序的重定位表
2.在壳代码中,系统会帮我修复壳代码的重定位,待原程序解密之后,
在壳代码中获取通讯结构体,获取原程序重定位表,修复原程序重定位
VOID Pack::FixReloc()
{
typedef struct _TYPEOFFSET
{
WORD offset : 12;
WORD type : 4;
} TYPEOFFSET, * PTYPEOFFSET;
auto reloc = (PIMAGE_BASE_RELOCATION)(OptionalHeader(stub)->DataDirectory[5].VirtualAddress + stub);
ULONG old_base = stub;
ULONG new_base = OptionalHeader(data)->ImageBase;
ULONG old_section_base = GetSection(stub, ".text")->VirtualAddress;
ULONG new_section_base = GetSection(data, ".pack")->VirtualAddress;
while (reloc->SizeOfBlock)
{
auto item = (PTYPEOFFSET)(reloc + 1);
ULONG count = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
for (ULONG i = 0; i < count; ++i)
{
if (item[i].type == 3)
{
DWORD old_protect = 0;
ULONG* address = (ULONG*)(item[i].offset + reloc->VirtualAddress + stub);
VirtualProtect(address, 4, PAGE_READWRITE, &old_protect);
*address = *address - old_base - old_section_base + new_base + new_section_base;
VirtualProtect(address, 4, old_protect, &old_protect);
}
}
reloc = (PIMAGE_BASE_RELOCATION)((ULONG)reloc + reloc->SizeOfBlock);
}
//auto a = OptionalHeader(data)->DllCharacteristics;
//a &= ~IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
//OptionalHeader(data)->DllCharacteristics = a;
printf("Dll重定位修复完成\n\n");
return;
}
将TLS信息保存到共享数据中
VOID Pack::SaveTLS()
{
//备份
share->TlsVirtualAddress = OptionalHeader(data)->DataDirectory[9].VirtualAddress;
if (NULL != share->TlsVirtualAddress)
{
OptionalHeader(data)->DataDirectory[9].VirtualAddress = 0;
ULONG Foa = RvaToOffset(data,share->TlsVirtualAddress);
auto Tls = (PIMAGE_TLS_DIRECTORY)(Foa + data);
share->TlsVa = Tls->AddressOfCallBacks;
share->OldImageBase = OptionalHeader(data)->ImageBase;
}
}
设置 OEP 到新的位置
1. 将旧的 oep 保存到共享数据中
2. 将新的 oep 设置到扩展头中
1. 分别找到两个区段在文件中(pe)和内存中(stub)的区段表
2. 计算出两个区段在内存中的位置,分别使用 foa 和 rva
3. 进行拷贝,拷贝的大小就是区段的大小(SizeOfRawData)
VOID Pack::SetNewOep()
{
share->old_oep = OptionalHeader(data)->AddressOfEntryPoint;
OptionalHeader(data)->AddressOfEntryPoint = offset + GetSection(data, ".pack")->VirtualAddress;
printf("新OEP设置完成\n\n");
}
加密指定的区段,使用异或方式
1. 找到区段
2. 计算大小和起始位置
3. 生成 key 并将区段信息保存到共享数据
4. 进行加密操作
VOID Pack::XorSection(LPCSTR name)
{
auto section = GetSection(data, name);
auto text = (BYTE*)(section->PointerToRawData + data);
share->xorkey = rand() % 0x100;
share->xorsection = section->VirtualAddress;
share->xorsize = section->SizeOfRawData;
for (UINT i = 0; i < share->xorsize; ++i)
{
text[i] ^= share->xorkey;
}
}
1. 首先将数据目录表中的导入表信息保存到共享数据
2. 清除数据目录表中的导入表和 IAT 表
VOID Pack::HookIat()
{
share->intrva = OptionalHeader(data)->DataDirectory[1].VirtualAddress;
OptionalHeader(data)->DataDirectory[1].Size = OptionalHeader(data)->DataDirectory[1].VirtualAddress =
OptionalHeader(data)->DataDirectory[12].Size =
OptionalHeader(data)->DataDirectory[12].VirtualAddress = 0;
}
// 将壳代码的区段[内容]直接的复制到PE文件中
VOID Pack::CopySectionData(LPCSTR src_name,LPCSTR dest_name)
{
auto src_section = GetSection(stub, src_name);
auto dest_section = GetSection(data, dest_name);
LPVOID src_data = (LPVOID)(src_section->VirtualAddress + stub);
LPVOID dest_data = (LPVOID)(dest_section->PointerToRawData + data);
memcpy(dest_data, src_data, src_section->SizeOfRawData);
}
保存修改后的 PE 文件到新的位置
1. 使用指定路径创建一个新的文件
2. 按照事先约定好的大小写入内容
3. 释放堆空间并且关闭句柄
VOID Pack::SaveFile(LPCWSTR path)
{
HANDLE file = CreateFileW(path,GENERIC_ALL,NULL,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
if (file == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, L"文件打开失败", L"错误", MB_OK | MB_ICONERROR);
ExitProcess(-1);
}
DWORD bytes = 0;
if (!WriteFile(file, (LPVOID)data, size, &bytes, NULL))
{
MessageBox(NULL, L"文件写入失败", L"错误", MB_OK | MB_ICONERROR);
ExitProcess(-1);
}
free((LPVOID)data);
CloseHandle(file);
}
壳代码实现
typedef struct _SHAREDATA
{
unsigned int old_oep;
unsigned char xorkey;
unsigned long xorsection;
unsigned int xorsize;
int TlsVirtualAddress;
int TlsVa;
int dwRelocRva;
int dwRelocSize;
int OldImageBase;
DWORD intrva;
} SHAREDATA, * PSHAREDATA;
using FuncVirtualProtect = BOOL(WINAPI*)(
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flNewProtect,
_Out_ PDWORD lpflOldProtect);
FuncVirtualProtect pVirtualProtect;
using funcGetProcAddress = FARPROC(WINAPI*)(
_In_ HMODULE hModule,
_In_ LPCSTR lpProcName
);
funcGetProcAddress pGetProcAddress;
using funcLoadLibraryA = HMODULE(WINAPI*)(
_In_ LPCSTR lpLibFileName
);
funcLoadLibraryA pLoadLibraryA;
using funcVirtualAlloc = LPVOID(WINAPI*)(
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect);
funcVirtualAlloc pVirtualAlloc;
typedef ATOM(WINAPI* MYRegisterClassA)
(CONST WNDCLASSA* lpWndClass);
MYRegisterClassA MyRegisterClassA;
typedef HWND(WINAPI* MYCreateWindowExA)
(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);
MYCreateWindowExA MyCreateWindowExA;
typedef HMODULE(WINAPI* MYGetModuleHandleA)
(LPCSTR lpModuleName);
MYGetModuleHandleA MyGetModuleHandleA;
typedef BOOL(WINAPI* MYShowWindow)
(HWND hWnd, int nCmdShow);
MYShowWindow MyShowWindow;
typedef BOOL(WINAPI* MYUpdateWindow)
(HWND hWnd);
MYUpdateWindow MyUpdateWindow;
typedef BOOL(WINAPI* MYGetMessageA)
(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
MYGetMessageA MyGetMessageA;
typedef BOOL(WINAPI* MYTranslateMessage)
(CONST MSG* lpMsg);
MYTranslateMessage MyTranslateMessage;
typedef LRESULT(WINAPI* MYDispatchMessageA)
(CONST MSG* lpMsg);
MYDispatchMessageA MyDispatchMessageA;
typedef LRESULT(WINAPI* MYDefWindowProcA)
(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
MYDefWindowProcA MyDefWindowProcA;
typedef VOID(WINAPI* MYPostQuitMessage)
(int nExitCode);
MYPostQuitMessage MyPostQuitMessage;
typedef BOOL(WINAPI* MYGetClientRect)
(HWND hWnd, LPRECT lpRect);
MYGetClientRect MyGetClientRect;
typedef int (WINAPI* MYGetWindowTextA)
(HWND hWnd, _Out_writes_(nMaxCount) LPSTR lpString, int nMaxCount);
MYGetWindowTextA MyGetWindowTextA;
typedef HWND(WINAPI* MYGetDlgItem)
(HWND hDlg, int nIDDlgItem);
MYGetDlgItem MyGetDlgItem;
typedef int(__cdecl* MYmemcmp)
(_In_reads_bytes_(_Size) void const* _Buf1, _In_reads_bytes_(_Size) void const* _Buf2, size_t _Size);
MYmemcmp Mymemcmp;
typedef VOID(WINAPI* MYExitProcess)
(UINT uExitCode);
MYExitProcess MyExitProcess;
typedef LRESULT(WINAPI* MYSendMessageA)
(HWND hWnd, UINT Msg, _Pre_maybenull_ _Post_valid_ WPARAM wParam, _Pre_maybenull_ _Post_valid_ LPARAM lParam);
MYSendMessageA MySendMessageA;
typedef HMODULE(WINAPI* MYLoadLibraryExA)
(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags);
MYLoadLibraryExA MyLoadLibraryExA;
typedef int(WINAPI* MYMessageBoxA)
(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
MYMessageBoxA MyMessageBoxA;
typedef BOOL(WINAPI* MYVirtualProtect)
(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
MYVirtualProtect MyVirtualProtect;
HMODULE gKernelBase = 0;
HMODULE gUser32Base = 0;
DWORD gImageBase;
HWND gParentWnd = 0;
PIMAGE_DOS_HEADER DosHeader(ULONG base)
{
return (PIMAGE_DOS_HEADER)base;
}
PIMAGE_NT_HEADERS NtHeader(ULONG base)
{
return (PIMAGE_NT_HEADERS)(DosHeader(base)->e_lfanew + base);
}
PIMAGE_FILE_HEADER FileHeader(ULONG base)
{
return &NtHeader(base)->FileHeader;
}
PIMAGE_OPTIONAL_HEADER OptionalHeader(ULONG base)
{
return &NtHeader(base)->OptionalHeader;
}
extern "C"
{
// 定义一个全局变量,用于保存当前程序的加载基址
unsigned long base = 0;
// 这里导出了之后,加壳器就可以通过 GetProcAddress 获取了
__declspec(dllexport) SHAREDATA share;
// 获取当前程序的加载基址
__declspec(naked) void GetBase()
{
__asm
{
mov eax, fs: [0x30]
mov eax, [eax + 0x08]
mov base, eax
ret
}
}
// 获取当前程序的加载基址
__declspec(naked) void JmpOep()
{
__asm
{
mov eax, base
add eax, share.old_oep
jmp eax
}
}
// 从 LDR 获取 kernel32.dll 的基址
DWORD GetKernelBase()
{
DWORD addr = 0;
__asm
{
mov eax, dword ptr fs : [0x30]
mov eax, dword ptr[eax + 0x0C]
mov eax, dword ptr[eax + 0x0C]
mov eax, dword ptr[eax]
mov eax, dword ptr[eax]
mov eax, dword ptr[eax + 0x18]
}
}
_declspec(naked) void Asm()
{
__asm call SIGN
__asm _emit 0XE9
__asm _emit 0XEB
__asm _emit 0X04
SIGN:
_asm pop eax
_asm inc eax;
_asm push eax
_asm ret
}
// 自己的查找函数的函数
DWORD MyGetProcAddress(DWORD Module, LPCSTR FunName)
{
// 获取 Dos 头和 Nt 头
auto DosHeader = (PIMAGE_DOS_HEADER)Module;
auto NtHeader = (PIMAGE_NT_HEADERS)(Module + DosHeader->e_lfanew);
// 获取导出表结构
DWORD ExportRva = NtHeader->OptionalHeader.DataDirectory[0].VirtualAddress;
auto ExportTable = (PIMAGE_EXPORT_DIRECTORY)(Module + ExportRva);
// 找到导出名称表、序号表、地址表
auto NameTable = (DWORD*)(ExportTable->AddressOfNames + Module);
auto FuncTable = (DWORD*)(ExportTable->AddressOfFunctions + Module);
auto OrdinalTable = (WORD*)(ExportTable->AddressOfNameOrdinals + Module);
// 遍历找名字
for (DWORD i = 0; i < ExportTable->NumberOfNames; ++i)
{
// 获取名字
char* Name = (char*)(NameTable[i] + Module);
if (!strcmp(Name, FunName))
return FuncTable[OrdinalTable[i]] + Module;
}
return -1;
}
// 动态的获取到所有的函数地址
VOID GetApis()
{
ULONG kernelbase = GetKernelBase();
MyVirtualProtect = (MYVirtualProtect)MyGetProcAddress(kernelbase, "GetProcAddress");
pVirtualProtect = (FuncVirtualProtect)MyGetProcAddress(kernelbase, "VirtualProtect");
pLoadLibraryA = (funcLoadLibraryA)MyGetProcAddress(kernelbase, "LoadLibraryA");
pGetProcAddress = (funcGetProcAddress)MyGetProcAddress(kernelbase, "GetProcAddress");
pVirtualAlloc = (funcVirtualAlloc)MyGetProcAddress(kernelbase, "VirtualAlloc");
MyLoadLibraryExA = (MYLoadLibraryExA)MyGetProcAddress(kernelbase, "LoadLibraryExA");
gUser32Base = MyLoadLibraryExA("User32.dll", NULL, NULL);
MyRegisterClassA = (MYRegisterClassA)pGetProcAddress(gUser32Base, "RegisterClassA");
MyCreateWindowExA = (MYCreateWindowExA)pGetProcAddress(gUser32Base, "CreateWindowExA");
MyGetModuleHandleA = (MYGetModuleHandleA)MyGetProcAddress(kernelbase, "GetModuleHandleA");
MyShowWindow = (MYShowWindow)pGetProcAddress(gUser32Base, "ShowWindow");
MyUpdateWindow = (MYUpdateWindow)pGetProcAddress(gUser32Base, "UpdateWindow");
MyGetMessageA = (MYGetMessageA)pGetProcAddress(gUser32Base, "GetMessageA");
MyTranslateMessage = (MYTranslateMessage)pGetProcAddress(gUser32Base, "TranslateMessage");
MyDispatchMessageA = (MYDispatchMessageA)pGetProcAddress(gUser32Base, "DispatchMessageA");
MyDefWindowProcA = (MYDefWindowProcA)pGetProcAddress(gUser32Base, "DefWindowProcA");
MyPostQuitMessage = (MYPostQuitMessage)pGetProcAddress(gUser32Base, "PostQuitMessage");
MyGetClientRect = (MYGetClientRect)pGetProcAddress(gUser32Base, "GetClientRect");
MyGetWindowTextA = (MYGetWindowTextA)pGetProcAddress(gUser32Base, "GetWindowTextA");
MyGetDlgItem = (MYGetDlgItem)pGetProcAddress(gUser32Base, "GetDlgItem");
HMODULE hVcruntime = MyLoadLibraryExA("vcruntime140.dll", NULL, NULL);
Mymemcmp = (MYmemcmp)pGetProcAddress(hVcruntime, "memcmp");
MyExitProcess = (MYExitProcess)pGetProcAddress(gUser32Base, "ExitProcess");
MySendMessageA = (MYSendMessageA)pGetProcAddress(gUser32Base, "SendMessageA");
MyMessageBoxA = (MYMessageBoxA)pGetProcAddress(gUser32Base, "MessageBoxA");
}
// 解密被加密的区段
void XorSection()
{
/*
1. 计算出被加密区段的 va
2. 使用加壳器提供的大小和 key 解密区段
3. 由于代码段不可读写,所以需要修改属性
*/
auto section = (unsigned char*)(base + share.xorsection);
DWORD protect = 0;
pVirtualProtect(section, share.xorsize, PAGE_READWRITE, &protect);
for (unsigned long i = 0; i < share.xorsize; ++i)
{
section[i] ^= share.xorkey;
}
pVirtualProtect(section, share.xorsize, protect, &protect);
}
// 修复被加密的导入表
VOID FixIat()
{
/*
1. 找到原始程序的导入表
2. 遍历导入表,加载每一个导入表的 dll
3. 遍历 int,获取所有的函数名称
4. 使用 GetProcAddress 获取函数并写入 iat
*/
auto import_table = (PIMAGE_IMPORT_DESCRIPTOR)(base + share.intrva);
static BYTE shellcode[] = "\xB8\x00\x00\x00\x00\x35\x15\x15\x15\x15\xFF\xE0";
while (import_table->Name)
{
auto name = (char*)(import_table->Name + base);
HMODULE module = pLoadLibraryA(name);
ULONG* int_table = (ULONG*)(import_table->OriginalFirstThunk + base);
ULONG* iat_table = (ULONG*)(import_table->FirstThunk + base);
for (int i = 0; int_table[i]; ++i)
{
DWORD address = 0;
DWORD protect = 0;
pVirtualProtect(&iat_table[i], 4, PAGE_READWRITE, &protect);
if (int_table[i] & 0x80000000)
{
address = (ULONG)pGetProcAddress(module, (LPCSTR)LOWORD(int_table[i]));
}
else
{
auto name = (PIMAGE_IMPORT_BY_NAME)(int_table[i] + base);
address = (ULONG)pGetProcAddress(module, name->Name);
}
PBYTE buffer = (PBYTE)pVirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(buffer, shellcode, 13);
address ^= 0x15151515;
*((ULONG*)&buffer[1]) = address;
iat_table[i] = (ULONG)buffer;
pVirtualProtect(&iat_table[i], 4, protect, &protect);
}
import_table++;
}
}
//回调函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
CHAR Buff[0x11] = { 0 };
HWND h1000 = 0;
DWORD Len = 0;
switch (message)
{
case WM_CREATE:
MyCreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 10, 200, 30, hWnd, (HMENU)0x1000, NULL, NULL);
MyCreateWindowExA(WS_EX_CLIENTEDGE, "BUTTON", "确定", WS_CHILD | WS_VISIBLE, 10, 40, 80, 30, hWnd, (HMENU)0x1001, NULL, NULL);
MyCreateWindowExA(WS_EX_CLIENTEDGE, "BUTTON", "取消", WS_CHILD | WS_VISIBLE, 100, 40, 80, 30, hWnd, (HMENU)0x1002, NULL, NULL);
}
if (hWnd == gParentWnd)
{
switch (message)
{
case WM_CLOSE:
MyPostQuitMessage(0);
if (lParam != 100)
{
MyExitProcess(0);
}
break;
case WM_COMMAND:
{
int nId = LOWORD(wParam);
switch (nId)
{
case 0x1001:
if (HIWORD(wParam) == BN_CLICKED)
{
h1000 = MyGetDlgItem(gParentWnd, 0x1000);
Len = MyGetWindowTextA(h1000, Buff, 0x10);
if (Len == 0)
{
MyMessageBoxA(0, "密码不能为空", "提示", 0);
}
else if (!Mymemcmp("1234", Buff, Len)) {
MyMessageBoxA(0, "继续", "提示", 0);
MySendMessageA(gParentWnd, WM_CLOSE, 0, 100);
}
else {
MyMessageBoxA(0, "退出", "Error", 0);
}
}
break;
case 0x1002:
MyExitProcess(0);
break;
default:
break;
}
}
}
}
return MyDefWindowProcA(hWnd, message, wParam, lParam);
};
//密码弹框
void PasswordDbg() {
WNDCLASSA wc = { 0 };
HMODULE hInstance = MyGetModuleHandleA(NULL);
wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_DROPSHADOW | CS_NOCLOSE;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;//实例句柄,代表此程序
wc.hIcon = 0;
wc.hCursor = 0;
wc.hbrBackground = 0;//BRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;//菜单
wc.lpszClassName = "Password";
MyRegisterClassA( & wc);
gParentWnd = MyCreateWindowExA(
0,
"Password",
"密码",
WS_OVERLAPPEDWINDOW,
100, 200, 300, 130,
NULL,
NULL,
hInstance,
NULL
);
//显示、更新窗口
MyShowWindow(gParentWnd, SW_SHOW);
MyUpdateWindow(gParentWnd);
MSG msg = { 0 };
while (MyGetMessageA(&msg, 0, 0, 0))
{
MyTranslateMessage(&msg);
MyDispatchMessageA(&msg);
}
return;
}
//调用TIS
void CallTls()
{
if (share.TlsVirtualAddress)
{
//////恢复数据
//DWORD OldProtect = 0;
//MyVirtualProtect(&(OptionalHeader(gImageBase)->DataDirectory[9].VirtualAddress),
// 0x1000, PAGE_EXECUTE_READWRITE, &OldProtect);
//OptionalHeader(gImageBase)->DataDirectory[9].VirtualAddress = share.TlsVirtualAddress;
//MyVirtualProtect(&(OptionalHeader(gImageBase)->DataDirectory[9].VirtualAddress),
// 0x1000, OldProtect, &OldProtect);
//auto TlsTable = (PIMAGE_TLS_DIRECTORY)(OptionalHeader(gImageBase)->DataDirectory[9].VirtualAddress + gImageBase);
auto TlsTable = (PIMAGE_TLS_DIRECTORY)(share.TlsVirtualAddress + base);
//调用Tls
auto CallTlsTable = (PIMAGE_TLS_CALLBACK*)(TlsTable->AddressOfCallBacks);
while (*CallTlsTable)
{
(*CallTlsTable)((PVOID)gImageBase, DLL_PROCESS_ATTACH, NULL);
CallTlsTable++;
}
}
}
// 修复重定位
VOID FixReloc()
{
typedef struct _TYPEOFFSET
{
WORD Offset : 12;
WORD Type : 4;
}TYPEOFFSET, * PTYPEOFFSET;
// 1.找到 DLL 重定位表,遍历其中的重定位块,以全 0 结构结尾
auto reloc = (PIMAGE_BASE_RELOCATION)(share.dwRelocRva + base);
// 2.解析重定位块,找到所有 Type 为 3 的项,使用上面的公式重定位
while (reloc->SizeOfBlock)
{
auto item = (PTYPEOFFSET)(reloc + 1);
ULONG count = (reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
for (ULONG i = 0; i < count; i++)
{
DWORD old_protect = 0;
ULONG* address = (ULONG*)(item[i].Offset + reloc->VirtualAddress + base);
pVirtualProtect(address, 4, PAGE_READWRITE, &old_protect);
*address = *address - share.OldImageBase + base;
pVirtualProtect(address, 4, old_protect, &old_protect);
}
reloc = (PIMAGE_BASE_RELOCATION)((ULONG)reloc + reloc->SizeOfBlock);
}
}
//反调试之 PEB+0x68
VOID CheckNtGlobalFlag()
{
int NtGlobalFlag = 0;
__asm {
push eax
mov eax, dword ptr fs : [0x30]
mov eax, dword ptr[eax + 0x68]
mov NtGlobalFlag, eax
pop eax
}
if (NtGlobalFlag == 0x70)
{
MyMessageBoxA(0, "程序正在被调试", 0, 0);
//exit(-1);
}
}
__declspec(dllexport) __declspec(naked) void start()
{
__asm
{
xor eax,eax
test eax,eax
je text1
jne text2
text2:
__asm _emit 0xE8
text1:
call GetBase
call GetApis
call PasswordDbg
call XorSection
call FixIat
call FixReloc
}
CheckNtGlobalFlag();
CallTls();
__asm
{
call JmpOep
}
}
}