zoukankan      html  css  js  c++  java
  • PE基础4-练习-导入表-到处表-重定位表-重写写成新文件

    PE基础练习

    #include <windows.h>
    #include <stdio.h>
    
    struct TypeOffset
    {
        WORD Offset : 12;
        WORD Type : 4;
    };
    
    // 保存文件大小
    DWORD FileSize = 0;
    
    // 保存文件的基址
    DWORD FileBase;
    
    // 保存DOS头
    PIMAGE_DOS_HEADER DosHeader = nullptr;
    
    PIMAGE_NT_HEADERS NtHeader = nullptr;
    
    
    DWORD RVAtoFOA(DWORD rva)
    {
        // 1. 获取区段表
        auto SectionTables = IMAGE_FIRST_SECTION(NtHeader);
    
        // 2. 获取区段数量
        WORD Count = NtHeader->FileHeader.NumberOfSections;
    
        // 3. 遍历区段
        for (int i = 0; i < Count; ++i)
        {
            // 4. 判断是否存在于区段中
            if (rva >= SectionTables[i].VirtualAddress &&
                rva < (SectionTables[i].VirtualAddress + SectionTables[i].SizeOfRawData))
            {
                // 5. 找到之后计算位置并返回值
                return rva - SectionTables[i].VirtualAddress + SectionTables[i].PointerToRawData;
            }
        }
    
        // 6. 没有找到返回 -1
        return -1;
    }
    
    
    VOID OpenPeFile(LPCSTR FileName)
    {
        // 1. 打开文件
        HANDLE Handle = CreateFileA(FileName, GENERIC_READ, NULL,
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    
        // 2. 获取文件大小
        FileSize = GetFileSize(Handle, NULL);
    
        // 3. 读取文件数据
        DWORD OperSize = 0;
        FileBase = (DWORD)new BYTE[FileSize];
        ReadFile(Handle, (LPVOID)FileBase, FileSize, &OperSize, NULL);
    
        // 4. 判断是不是一个有效的 PE 文件
        // 4.1 获取DOS头并判断是不是一个有效的DOS文件
        DosHeader = (PIMAGE_DOS_HEADER)FileBase;
        if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
        {
            MessageBox(NULL, "不是一个有效的DOS文件", "提示", MB_OK);
            system("pause"); exit(0);
        }
    
        // 4.2 获取 NT 头并判断是不是一个有效的PE文件
        NtHeader = (PIMAGE_NT_HEADERS)(FileBase + DosHeader->e_lfanew);
        if (NtHeader->Signature != IMAGE_NT_SIGNATURE)
        {
            MessageBox(NULL, "不是一个有效的PE文件", "提示", MB_OK);
            system("pause"); exit(0);
        }
    
        // 4.3 判断是不是一个32位文件
        if (NtHeader->OptionalHeader.Magic != 0x010B)
        {
            MessageBox(NULL, "不是一个有效的32位文件", "提示", MB_OK);
            system("pause"); exit(0);
        }
    
        CloseHandle(Handle);
    }
    
    // 遍历重定位表
    VOID FixReloc()
    {
        DWORD base = NtHeader->OptionalHeader.ImageBase;
    
        // 1. 获取重定位表的 rva
        DWORD RelocRVA = NtHeader->OptionalHeader.DataDirectory[5].VirtualAddress;
    
        // 2. 获取重定位表
        auto Reloc = (PIMAGE_BASE_RELOCATION)(FileBase + RVAtoFOA(RelocRVA));
    
        // 3. 遍历重定位表中的重定位块,以0结尾
        while (Reloc->SizeOfBlock != 0)
        {
            // 3.1 输出分页基址
            printf("PAGE_BASE: %08X
    ", Reloc->VirtualAddress);
        
            // 3.2 找到重定位项
            auto Offset = (TypeOffset*)(Reloc + 1);
            
            // 3.3 计算重定位项的个数
            // Reloc->SizeOfBlock 保存的是整个重定位块的大小 结构体 + 重定位项数组
            // Reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) 得到数组大小
            // 上面的结果  2 = 重定位项的个数,原因是重定位项的大小为 两个字节
            DWORD Size = (Reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
    
            // 3.4 遍历所有的重定位项
            for (int i = 0; i < Size; ++i)
            {
                // 获取重定位类型,只关心为3的类型
                DWORD Type = Offset[i].Type;
    
                // 获取重定位的偏移值
                DWORD pianyi = Offset[i].Offset;
    
                // 获取要重定位的地址所在的RVA: offset+virtualaddress
                DWORD rva = pianyi + Reloc->VirtualAddress;
    
                // 获取要重定位的地址所在的FOA
                DWORD foa = RVAtoFOA(rva);
    
                // 获取要重定位的地址所在的fa
                DWORD fa = foa + FileBase;
    
                // 获取要重定位的地址
                DWORD addr = *(DWORD*)fa;
    
                // 计算重定位后的数据: addr - oldbase + newbase
                DWORD new_addr = addr - base + 0x1500000;
    
                // 将重定位后的数据写回缓冲区(文件)
                if (Offset[i].Type == 3)
                    *(DWORD*)fa = new_addr;
    
                // printf("	T(%d)%08X->%08X: %08X[%08X]
    ", Type, rva, foa, addr, new_addr);
            }
    
            // 找到下一个重定位块
            Reloc = (PIMAGE_BASE_RELOCATION)
                ((DWORD)Reloc + Reloc->SizeOfBlock);
        }
    
        NtHeader->OptionalHeader.ImageBase = 0x1500000;
    
        // 1. 打开文件
        HANDLE Handle = CreateFileA("newfile.exe", GENERIC_WRITE, NULL,
            NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    
    
        // 3. 写入文件数据
        DWORD OperSize = 0;
        WriteFile(Handle, (LPVOID)FileBase, FileSize, &OperSize, NULL);
    
        CloseHandle(Handle);
    }
    
    VOID Import()
    {
        // 1. 从数据目录表的下标为 1的项找到rva
        DWORD rav = NtHeader->OptionalHeader.DataDirectory[1].VirtualAddress;
    
        // 2. 找到导入表结构体
        auto ImportTable = (PIMAGE_IMPORT_DESCRIPTOR)(RVAtoFOA(rav) + FileBase);
    
        // 3. 遍历导入表数组,数组以全 0 结尾
        while (ImportTable->Name)
        {
            // 4. 输出对应DLL的名字
            CHAR* DllName = (CHAR*)(RVAtoFOA(ImportTable->Name) + FileBase);
            printf("DLLName: %s
    ", DllName);
    
            // 5. 找到 iat
            auto Iat = (PIMAGE_THUNK_DATA)(RVAtoFOA(ImportTable->FirstThunk) + FileBase);
    
            // 6. 遍历 iat ,全 0 结尾
            while (Iat->u1.Ordinal != 0)
            {
                // 7. 判断是否有名字
                if (Iat->u1.AddressOfData & 0x80000000)
                {
                    // 序号导入,直接输出
                    printf("	[%hd]: None
    ", LOWORD(Iat->u1.AddressOfData));
                }
                else
                {
                    // 找到名字结构体
                    auto Name = (PIMAGE_IMPORT_BY_NAME)(RVAtoFOA(Iat->u1.AddressOfData) + FileBase);
                    printf("	[%hd]: %s
    ", Name->Hint, Name->Name);
                }
                ++Iat;
            }
    
            // 指向下一个结构
            ImportTable++;
        }
    
    }
    
    VOID Export()
    {
        // 1. 从数据目录表的下标为 0 的项找到rva
        DWORD rav = NtHeader->OptionalHeader.DataDirectory[0].VirtualAddress;
    
        // 2. 找到导入表结构体
        auto ExportTable = (PIMAGE_EXPORT_DIRECTORY)(RVAtoFOA(rav) + FileBase);
    
        // 3. 获取有名字的个数和函数总个数
        DWORD NameCount = ExportTable->NumberOfNames;
        DWORD FunctionCount = ExportTable->NumberOfFunctions;
    
        // 4. 获取三张表,序号表是WORD
        DWORD* 地址表 = (DWORD*)(RVAtoFOA(ExportTable->AddressOfFunctions) + FileBase);
        DWORD* 名称表 = (DWORD*)(RVAtoFOA(ExportTable->AddressOfNames) + FileBase);
        WORD* 序号表 = (WORD*)(RVAtoFOA(ExportTable->AddressOfNameOrdinals) + FileBase);
        
        // 5. 遍历地址表
        for (int i = 0; i < FunctionCount; ++i)
        {
            bool HaveName = FALSE;
    
            // 6. 判断是否有名字,有名字的话,下标会存在序号表中
            for (int j = 0; j < NameCount; ++j)
            {
                // 有名字
                if (i == 序号表[j])
                {
                    HaveName = TRUE;
                    // 对应序号表下标的名称表内保存的是名字
                    CHAR* Name = (CHAR*)(RVAtoFOA(名称表[j]) + FileBase);
    
                    printf("[%hd]: %p %s
    ", i + ExportTable->Base, 地址表[i], Name);
                    break;
                }
            }
    
            // 如果全部找完还没有名字
            if (HaveName == FALSE)
            {
                printf("[%hd]: %p None
    ", i + ExportTable->Base, 地址表[i]);
            }
    
        }
    }
    
    int main()
    {
        OpenPeFile("test.dll");
    
        // FixReloc();
        // Import();
        // Export();
    
        return 0;
    }
  • 相关阅读:
    236. 二叉树的最近公共祖先
    230. 二叉搜索树中第K小的元素
    221. 最大正方形
    软件构建模式之MVC框架初窥
    九度OnlineJudge之1020:最小长方形
    九度OnlineJudge之1018:统计同成绩学生人数
    九度OnlineJudge之1017:还是畅通工程
    向Python女神推荐这些年我追过的经典书籍
    最实用的10个重构小技巧排行榜,您都用过哪些呢?
    九度OnlineJudge之1014:排名
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11099355.html
Copyright © 2011-2022 走看看