前置知识:
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
                }
        }
}