PE文件解析ICO资源图片(x86和x64均可用)

xingyun86 2023-8-11 1154

PE文件解析ICO资源图片(x86和x64均可用)

#include <Windows.h>
#include <stdio.h>
#include <iostream>
#include <time.h>
#define RES_IS_DIR 0x80000000
#define RES_AND_DIR 0x7fffffff
#define RES_IS_NAME 0x80000000
#define RES_AND_NAME 0x7fffffff
#define FILENAME_LENGTH 256
#define RES_ICON_INDEX 0x03
#define RES_ICON_GROUP_INDEX 0xE
char sectioninfo[] = "\nname\t实际大小\tRVA\t\t原始大小\t文件位置\t属性\n";
char szSection[] = "%s\t%08x\t%08x\t%08x\t%08x\t%08x";
char szMsgRes[] = "\n\n资源所处的节:%s , rva:%08X , foa:%08X,资源首地址:%08X\n";
char szMsgResErr[] = "没有资源\n";
wchar_t szLevel1ByID[] = L"资源类型(自定义) %d";
wchar_t szLevel1[] = L"\n资源类型:%s\n";
wchar_t szLevel2ByName[] = L"  Name:%s\n";
wchar_t szLevel2ByID[] = L"  ID:%d\n";
wchar_t szResData[] = L"     文件偏移:%08X, RVA::%08X  代码页=%04X, 长度%d字节\n";
const wchar_t* szMsgResType[] =
{
    L"光标",
    L"位图",
    L"图标",\
    L"菜单",\
    L"对话框",\
    L"字符串",\
    L"字体目录",\
    L"字体",\
    L"加速键",\
    L"未格式化资源",\
    L"消息表",\
    L"光标组",\
    L"未知类型",\
    L"图标组",\
    L"未知类型",\
    L"版本信息" };
#pragma pack( push, 1 )
//文件中 图标项结构
struct ICON_DIR_ENTRY
{
    unsigned char width;
    unsigned char height;
    unsigned char colorcount;
    unsigned char reserved;
    WORD planes;
    WORD bitcount;
    DWORD bytesInRes; //字节大小
    DWORD imageOffset; // 文件中的位置
};
//pe中图标项结构,与文件中只有最后一个字段有区别
struct ICON_DIR_ENTRY_IN_PE
{
    unsigned char width;
    unsigned char height;
    unsigned char colorcount;
    unsigned char reserved;
    WORD planes;
    WORD bitcount;
    DWORD bytesInRes;
    WORD idIndex; // 图标id, 对应资源图标目录中的id
};
//文件中icon头结构
struct ICON_DIR
{
    WORD reserved;
    WORD idtype;
    WORD idcount;
    ICON_DIR_ENTRY dir_entry[1];
};
// pe中的图标头结构
struct ICON_DIR_IN_PE
{
    WORD reserved; // 保留
    WORD idtype;  // 图标类型
    WORD idcount; // 此图标包含图片的数量
    ICON_DIR_ENTRY_IN_PE dir_entry[1];
};
#pragma pack( pop)
IMAGE_SECTION_HEADER* find_section(SIZE_T mem, DWORD rva) {
    IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)mem;
    IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(mem + dos->e_lfanew);
    WORD n = nt->FileHeader.NumberOfSections;
    IMAGE_SECTION_HEADER* header = IMAGE_FIRST_SECTION(nt);
    IMAGE_SECTION_HEADER* sec = 0;
    for (WORD i = 0; i < n; ++i) {
        if (rva >= header->VirtualAddress && rva < header->VirtualAddress + header->SizeOfRawData) {
            sec = header;
            break;
        }
        header++;
    }
    return sec;
}
SIZE_T rva2foa(SIZE_T mem, DWORD rva) {
    IMAGE_SECTION_HEADER* header = find_section(mem, rva);
    SIZE_T foa = 0;
    if (header) {
        foa = rva - header->VirtualAddress;
        foa += header->PointerToRawData;
    }
    return foa;
}
char* get_section_name(SIZE_T mem, DWORD rva) {
    IMAGE_SECTION_HEADER* header = find_section(mem, rva);
    if (header) {
        return (char*)header->Name;
    }
    return NULL;
}
void print_all_sections(SIZE_T mem) {
    IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)mem;
    IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(mem + dos->e_lfanew);
    WORD n = nt->FileHeader.NumberOfSections;
    IMAGE_SECTION_HEADER* header = IMAGE_FIRST_SECTION(nt);
    printf(sectioninfo);
    for (WORD i = 0; i < n; ++i) {
        printf(szSection, header->Name, header->Misc.VirtualSize,
            header->VirtualAddress, header->SizeOfRawData, header->PointerToRawData,
            header->Characteristics);
        header++;
    }
}
bool pathExists(const TCHAR* path) {
    WIN32_FILE_ATTRIBUTE_DATA attr = { 0 };
    return 0 != ::GetFileAttributesExW(path, GetFileExInfoStandard, &attr);
}
//查找资源类型入口, 仅匹配预定义的资源类型
IMAGE_RESOURCE_DIRECTORY_ENTRY* find_res_type_entry(SIZE_T resmem, DWORD res_type) {
    IMAGE_RESOURCE_DIRECTORY* res = (IMAGE_RESOURCE_DIRECTORY*)resmem;
    IMAGE_RESOURCE_DIRECTORY_ENTRY* entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(resmem + sizeof(IMAGE_RESOURCE_DIRECTORY));
    WORD n = res->NumberOfIdEntries + res->NumberOfNamedEntries;
    IMAGE_RESOURCE_DIRECTORY_ENTRY* ret_entry = 0;
    for (WORD i = 0; i < n; ++i) 
    {
        if (entry->OffsetToData & RES_IS_DIR) {
            if ((!(entry->Name & RES_IS_NAME)) && (res_type == entry->Name)) {
                ret_entry = entry;
                break;
            }
        }
        entry++;
    }
    return ret_entry;
}
//创建一个ico文件
HANDLE create_icon_file(SIZE_T resmem, DWORD icon_name_addr) {
    WCHAR filename[FILENAME_LENGTH] = { 0 };
    DWORD randnum = 0;
    DWORD times = 0;
create_file_name:
    ++times;
    memset(filename, 0, sizeof(wchar_t) * FILENAME_LENGTH);
    randnum = rand();
    if (icon_name_addr & RES_IS_NAME) {
        IMAGE_RESOURCE_DIR_STRING_U* rds = (IMAGE_RESOURCE_DIR_STRING_U*)((icon_name_addr & RES_AND_NAME) + resmem);
        wsprintfW(filename, L"%s_%d.ico", rds->NameString, randnum);
    }
    else {
        wsprintfW(filename, L"%d_%d.ico", icon_name_addr, randnum);
    }
    if (pathExists(filename)) {
        if (times < 100) // 大于100次尝试重命名直接放弃
            goto create_file_name;
        return INVALID_HANDLE_VALUE;
    }
    return ::CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
}
//写入
BOOL write_bin(HANDLE hFile, void* mem, DWORD size) {
    DWORD nWritten = 0;
    return WriteFile(hFile, mem, size, &nWritten, 0);
}
//根据图标id 查找图标入口
IMAGE_RESOURCE_DIRECTORY_ENTRY* find_icon_entry_by_id(SIZE_T resmem, DWORD id) {
    IMAGE_RESOURCE_DIRECTORY_ENTRY* icon_entry = find_res_type_entry(resmem, RES_ICON_INDEX);
    IMAGE_RESOURCE_DIRECTORY* icon_id_dir = (IMAGE_RESOURCE_DIRECTORY*)((icon_entry->OffsetToData & RES_AND_DIR) + resmem);
    IMAGE_RESOURCE_DIRECTORY_ENTRY* identry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)((SIZE_T)icon_id_dir + sizeof(IMAGE_RESOURCE_DIRECTORY));
    IMAGE_RESOURCE_DIRECTORY_ENTRY* ret_entry = 0;
    int n = icon_id_dir->NumberOfIdEntries + icon_id_dir->NumberOfNamedEntries;
    for (int i = 0; i < n; ++i) {
        if (identry->OffsetToData & RES_IS_DIR) {
            if ((!(identry->Name & RES_IS_NAME)) && (id == identry->Name)) {
                ret_entry = identry;
                break;
            }
        }
        ++identry;
    }
    return ret_entry;
}
//根据图标ID 找到对应的代码页
IMAGE_RESOURCE_DIRECTORY* find_icon_codepage_dir_by_id(SIZE_T resmem, DWORD id) {
    IMAGE_RESOURCE_DIRECTORY_ENTRY* id_entry = find_icon_entry_by_id(resmem, id);
    IMAGE_RESOURCE_DIRECTORY* code_page_dir = (IMAGE_RESOURCE_DIRECTORY*)((id_entry->OffsetToData & RES_AND_DIR) + resmem);
    return code_page_dir;
}
//根据图标组的ico头信息, 找到对应的图标资源本体, 写入文件
void process_icon_body_data(SIZE_T filemem, SIZE_T resmem, ICON_DIR_ENTRY_IN_PE* pe_icon_entry, HANDLE hFile) {
    DWORD id = pe_icon_entry->idIndex;
    IMAGE_RESOURCE_DIRECTORY* code_page_dir = find_icon_codepage_dir_by_id(resmem, id);
    IMAGE_RESOURCE_DIRECTORY_ENTRY* code_page_entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)((SIZE_T)code_page_dir + sizeof(IMAGE_RESOURCE_DIRECTORY));
    int n = code_page_dir->NumberOfIdEntries + code_page_dir->NumberOfNamedEntries;
    for (int i = 0; i < n; ++i) {
        IMAGE_RESOURCE_DATA_ENTRY* data_entry = (IMAGE_RESOURCE_DATA_ENTRY*)(code_page_entry->OffsetToData + resmem);
        SIZE_T data_offset = rva2foa(filemem, data_entry->OffsetToData);
        data_offset += filemem;
        write_bin(hFile, (void*)data_offset, data_entry->Size);
    }
}
//解析图标组, 把ICON_DIR_ENTRY_IN_PE  转成 文件中适用的ICON_DIR_ENTRY
void process_icon_header_data(SIZE_T filemem, SIZE_T resmem, SIZE_T data_mem, DWORD data_size, HANDLE hFile) {
    ICON_DIR_IN_PE* pe_icon_header = (ICON_DIR_IN_PE*)data_mem;
    ICON_DIR_ENTRY_IN_PE* pe_icon_entry = pe_icon_header->dir_entry;
    WORD iCount = pe_icon_header->idcount; // 此图标包含有几个图片数据
    DWORD iFileHeaderSize = data_size + iCount * 2;  //每个图标项少2个字节,因此全部加上后为文件中的头文件大小
    //首先写头 6 个字节, 后续ICON_DIR_ENTRY_IN_PE 需要改成 ICON_DIR_ENTRY
    //以下所有的写入出错处理全部简化
    BOOL bSuccess = write_bin(hFile, pe_icon_header, 6);
    if (!bSuccess) {
        std::cout << __LINE__ << ", 写入头失败" << std::endl;
        return;
    }
    ICON_DIR_ENTRY file_entry = { 0 };
    const DWORD dwCopyBytes = 12; // 前12字节全一样
    DWORD offset = 0;
    //写入ICON_DIR_ENTRY,从ICON_DIR_ENTRY_IN_PE转成ICON_DIR_ENTRY
    //等循环结束, 整个头结构全部写入
    for (int i = 0; i < iCount; ++i) {
        memcpy(&file_entry, pe_icon_entry, dwCopyBytes);
        offset += iFileHeaderSize;
        file_entry.imageOffset = offset;
        if (!write_bin(hFile, &file_entry, sizeof(ICON_DIR_ENTRY))) {
            std::cout << __LINE__ << ", 写入ICON_DIR_ENTRY失败 , 第:" << i << "个 " << std::endl;
            return;
        }
        offset = file_entry.bytesInRes;
        pe_icon_entry++;
    }
    //写入图片本身数据
    pe_icon_entry = pe_icon_header->dir_entry;
    for (int i = 0; i < iCount; ++i) {
        process_icon_body_data(filemem, resmem, pe_icon_entry, hFile);
        ++pe_icon_entry;
    }
}
void process_iconGroup_dataEntry(SIZE_T filemem, SIZE_T resmem, IMAGE_RESOURCE_DIRECTORY_ENTRY* entry, HANDLE hFile) {
    IMAGE_RESOURCE_DATA_ENTRY* data_entry = (IMAGE_RESOURCE_DATA_ENTRY*)(entry->OffsetToData + resmem);
    SIZE_T data_foa = rva2foa(filemem, data_entry->OffsetToData);
    SIZE_T data_mem = data_foa + filemem;
    process_icon_header_data(filemem, resmem, data_mem, data_entry->Size, hFile);
}
void process_iconGroup_codePageDir(SIZE_T filemem, SIZE_T resmem, SIZE_T dirmem, DWORD icon_name_addr) {
    //这里只处理一个codepage 的入口,如果有多个需要自己依次组合每个文件头
    IMAGE_RESOURCE_DIRECTORY* res = (IMAGE_RESOURCE_DIRECTORY*)dirmem;
    IMAGE_RESOURCE_DIRECTORY_ENTRY* entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(dirmem + sizeof(IMAGE_RESOURCE_DIRECTORY));
    HANDLE hFile = create_icon_file(resmem, icon_name_addr);
    if (INVALID_HANDLE_VALUE == hFile) {
        std::cout << "icon_name_addr :" << icon_name_addr << " , 创建文件失败" << std::endl;
        return;
    }
    process_iconGroup_dataEntry(filemem, resmem, entry, hFile);
    CloseHandle(hFile);
}
//开始处理图标组
void process_iconGroup_idDir(SIZE_T filemem, SIZE_T resmem, SIZE_T dirmem) {
    IMAGE_RESOURCE_DIRECTORY* res = (IMAGE_RESOURCE_DIRECTORY*)dirmem;
    IMAGE_RESOURCE_DIRECTORY_ENTRY* entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(dirmem + sizeof(IMAGE_RESOURCE_DIRECTORY));
    WORD n = res->NumberOfIdEntries + res->NumberOfNamedEntries;
    SIZE_T inner_dir_addr = 0;
    for (WORD i = 0; i < n; ++i) {
        if (entry->OffsetToData & RES_IS_DIR) {
            inner_dir_addr = (entry->OffsetToData & RES_AND_DIR) + resmem;
            process_iconGroup_codePageDir(filemem, resmem, inner_dir_addr, entry->Name);
        }
        entry++;
    }
}
//开始解析图标
void process_res(SIZE_T filemem, SIZE_T resmem) {
    IMAGE_RESOURCE_DIRECTORY_ENTRY* icon_group_entry = find_res_type_entry(resmem, RES_ICON_GROUP_INDEX);
    IMAGE_RESOURCE_DIRECTORY_ENTRY* icon_entry = find_res_type_entry(resmem, RES_ICON_INDEX);
    if (0 == icon_group_entry || 0 == icon_entry) {
        std::cout << __LINE__ << " 图标组或图标入口不存在" << std::endl;
        return;
    }
    SIZE_T icon_group_id_dir_addr = (icon_group_entry->OffsetToData & RES_AND_DIR) + resmem;
    process_iconGroup_idDir(filemem, resmem, icon_group_id_dir_addr);
}
void get_res(SIZE_T mem) {
    IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)mem;
    IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(mem + dos->e_lfanew);
    DWORD res_rva = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
    if (0 == res_rva)
    {
        puts(szMsgResErr);
        return;
    }
    SIZE_T res_foa = rva2foa(mem, res_rva);
    SIZE_T res_mem = res_foa + mem;
    puts("开始检查图标组和图标资源");
    printf(szMsgRes, get_section_name(mem, res_rva), res_rva, res_foa, res_mem);
    process_res(mem, res_mem);
}
void print_res_dir_data(SIZE_T filemem, SIZE_T resmem, IMAGE_RESOURCE_DIRECTORY_ENTRY* entry) {
    DWORD code_page = entry->Name;
    IMAGE_RESOURCE_DATA_ENTRY* data_entry = (IMAGE_RESOURCE_DATA_ENTRY*)(entry->OffsetToData + resmem);
    SIZE_T rva = data_entry->OffsetToData;
    SIZE_T foa = rva2foa(filemem, rva);
    wprintf(szResData, foa, rva, code_page, data_entry->Size);
}
void print_res_dir_id(SIZE_T resmem, IMAGE_RESOURCE_DIRECTORY_ENTRY* entry) {
    DWORD name = entry->Name;
    SIZE_T name_addr = 0;
    if (name & RES_IS_NAME) {
        name_addr = (name & RES_AND_NAME) + resmem;
        IMAGE_RESOURCE_DIR_STRING_U* rds = (IMAGE_RESOURCE_DIR_STRING_U*)(name_addr);
        wprintf(szLevel2ByName, rds->NameString);
    }
    else {
        wprintf(szLevel2ByID, name);
    }
}
void print_res_dir_type(SIZE_T resmem, IMAGE_RESOURCE_DIRECTORY_ENTRY* entry)
{
    DWORD name = entry->Name;
    SIZE_T name_addr = 0;
    wchar_t* pname = 0;
    wchar_t buffer[64] = { 0 };
    if (name & RES_IS_NAME) {
        name_addr = (name & RES_AND_NAME) + resmem;
        IMAGE_RESOURCE_DIR_STRING_U* rds = (IMAGE_RESOURCE_DIR_STRING_U*)(name_addr);
        pname = rds->NameString;
    }
    else {
        if (name <= 0x10)
            wsprintfW(buffer, szMsgResType[name - 1]);
        else
            wsprintfW(buffer, szLevel1ByID, name);
        pname = buffer;
    }
    wprintf(szLevel1, pname);
}
void print_res_dir(SIZE_T filemem, SIZE_T resmem, SIZE_T dirmem, DWORD level) {
    IMAGE_RESOURCE_DIRECTORY* res = (IMAGE_RESOURCE_DIRECTORY*)dirmem;
    IMAGE_RESOURCE_DIRECTORY_ENTRY* entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(dirmem + sizeof(IMAGE_RESOURCE_DIRECTORY));
    int n = res->NumberOfIdEntries + res->NumberOfNamedEntries;
    DWORD next_dir_mem = 0;
    for (int i = 0; i < n; ++i) {
        if (entry->OffsetToData & RES_IS_DIR) {
            if (1 == level) {
                print_res_dir_type(resmem, entry);
            }
            else if (2 == level) {
                print_res_dir_id(resmem, entry);
            }
            else {
                std::cout << "error" << std::endl;
            }
            next_dir_mem = (entry->OffsetToData & RES_AND_DIR) + resmem;
            print_res_dir(filemem, resmem, next_dir_mem, level + 1);
        }
        else {
            print_res_dir_data(filemem, resmem, entry);
        }
        entry++;
    }
}
void print_res(SIZE_T mem) {
    IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)mem;
    IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(mem + dos->e_lfanew);
    DWORD res_rva = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
    if (0 == res_rva) {
        puts(szMsgResErr);
        return;
    }
    SIZE_T res_foa = rva2foa(mem, res_rva);
    SIZE_T res_mem = res_foa + mem;
    printf(szMsgRes, get_section_name(mem, res_rva), res_rva, res_foa, res_mem);
    print_res_dir(mem, res_mem, res_mem, 1);
}
void createFileMapAndPrint(LPCWSTR lpcwFileName) {
    setlocale(LC_ALL, "");
    HANDLE hFile = 0;
    HANDLE hMap = 0;
    void* mem = 0;
    __try
    {
        hFile = ::CreateFileW(lpcwFileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, 0);
        if (INVALID_HANDLE_VALUE == hFile) {
            std::cout << "create file failed" << std::endl;
            __leave;
        }
        hMap = ::CreateFileMappingW(hFile, 0, PAGE_READONLY, 0, 0, 0);
        if (0 == hMap) {
            std::cout << "CreateFileMapping failed" << std::endl;
            __leave;
        }
        mem = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
        if (0 == mem) {
            std::cout << "MapViewOfFile failed" << std::endl;
            __leave;
        }
        get_res((SIZE_T)mem); //解析图标组,写入图标
        //print_res((DWORD)mem);
        //print_all_sections((DWORD)mem);
    }
    __finally
    {
        ::UnmapViewOfFile(mem);
        ::CloseHandle(hMap);
        ::CloseHandle(hFile);
    }
}
__inline static
int test_main(const std::wstring& wExeName)
{
    srand(time(0));
    createFileMapAndPrint(wExeName.c_str());
    return 0;
}


×
打赏作者
最新回复 (0)
只看楼主
全部楼主
返回