2.8 PE 構造: リソース テーブルの詳細分析

Windows PE では、リソースとは、アイコン、ダイアログ ボックス、文字列、ビットマップ、バージョン情報など、実行可能ファイルに格納されているいくつかの固定データ コレクションを指します。PE ファイル内の各リソースには、実行時に簡単に見つけて呼び出すことができるように、対応する一意のリソース ID が割り当てられます。PE ファイル内のリソースはツリー構造に編成されており、最上位はルート ノード (Root)、次のレベルはリソース タイプ (Type)、その次のレベルはリソース名 (Name)、そして最後がリソース タイプ (Type) です。実際のリソースの内容。

PIMAGE_RESOURCE_DIRECTORY は、Windows PE 実行可能ファイルの構造タイプであり、各リソースのタイプ (Type)、名前 (Name)、および言語 (Language) を含むリソース (Resource) のツリー構造を記述するために使用されます。下向き 第 1 レベルの PE リソース ディレクトリのアドレスと関連情報。

typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY
{
    
    
    union {
    
    
        struct {
    
    
            DWORD NameOffset:31;
            DWORD NameIsString:1;
        } DUMMYSTRUCTNAME;
        DWORD   Name;
        WORD    Id;
    } DUMMYUNIONNAME;
    union {
    
    
        DWORD   OffsetToData;
        struct {
    
    
            DWORD   OffsetToDirectory:31;
            DWORD   DataIsDirectory:1;
        } DUMMYSTRUCTNAME2;
    } DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

PIMAGE_RESOURCE_DIRECTORY は、Windows PE リソースのディレクトリ構造を記述します。各リソース ディレクトリには次のフィールドが含まれます。

  • 特性: 名前付けが許可されるかどうか、ID が許可されるかどうかなど、ディレクトリの属性を指定します。
  • TimeDateStamp: ディレクトリのタイムスタンプを指定します。
  • MajorVersion/MinorVersion: PE ファイルで許可される最高および最低のバージョンを指定します。
  • NumberOfNamedEntries: ディレクトリ内の名前付きリソース エントリの数を指定します。
  • NumberOfIdEntries: リソース ID タイプの数を指定します。
  • PIMAGE_RESOURCE_DIRECTORY_ENTRY: リソース エントリ テーブル、つまり PE ファイル内の各リソースのエントリ アドレスを指すポインタ。
  • PIMAGE_RESOURCE_DIRECTORY_ENTRY は、PE ファイル内のリソースの名前、タイプ、言語情報を参照するために使用され、Name/Id: 絞り込まれた優先度に基づいて検索される、指定されたリソースの名前または ID が含まれます。名前よりも高いです。
  • OffsetToData: リソースのデータ オフセット アドレス、またはそのリソース データ エントリのアドレスを指します。

PIMAGE_DATA_DIRECTORY解析するとき、リーダーは通常、データ ディレクトリ テーブル内のリソース テーブルを見つけ、IMAGE_DIRECTORY_ENTRY_RESOURCEループを通じてデータ ディレクトリPIMAGE_RESOURCE_DIRECTORY_ENTRY内の各ノードを走査し、最後にリソース情報を出力する必要があります。この出力コードは次のとおりです。

// --------------------------------------------------
// 定义资源表解析结构
// --------------------------------------------------
static char* szResName[0x11] = {
    
     0, (char*)"鼠标指针", (char*)"位图", (char*)"图标", (char*)"菜单", (char*)"对话框", (char*)"字符串列表", (char*)"字体目录", (char*)"字体", (char*)"快捷键", (char*)"非格式化资源", (char*)"消息列表", (char*)"鼠标指针组", (char*)"zz", (char*)"图标组", (char*)"xx", (char*)"版本信息" };


int main(int argc, char * argv[])
{
    
    
    BOOL PE = IsPeFile(OpenPeFile("c://pe/x86.exe"), 0);

    if (PE == TRUE)
    {
    
    
        // 获取数据目录表
        PIMAGE_DATA_DIRECTORY pData = NtHeader->OptionalHeader.DataDirectory;

        // 获取到资源目录表
        pData = &(pData[IMAGE_DIRECTORY_ENTRY_RESOURCE]);

        // 获取资源目录表的偏移
        DWORD dwResOffset = RVAtoFOA(pData->VirtualAddress);

        // 获取到资源目录表
        PIMAGE_RESOURCE_DIRECTORY pRes = (PIMAGE_RESOURCE_DIRECTORY)(GlobalFileBase + dwResOffset);

        // 获取紧跟着的IMAGE_RESOURCE_DIRECTORY_ENTRY的个数
        DWORD dwResSize = pRes->NumberOfNamedEntries + pRes->NumberOfIdEntries;

        // 获取到PIMAGE_RESOURCE_DIRECTORY_ENTRY 
        PIMAGE_RESOURCE_DIRECTORY_ENTRY  pResEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRes + 1);

        printf("资源类型ID \t 类型 \n");

        for (DWORD i = 0; i < dwResSize; i++)
        {
    
    

            // 如果为0则执行
            if (!pResEntry[i].NameIsString)
            {
    
    
                if (pResEntry[i].Id < 0x11)
                {
    
    
                    // printf("资源类型ID: %p --> 类型: %s\n", pResEntry[i].Id, szResName[pResEntry[i].Id]);
                    printf("%p \t %s \n", pResEntry[i].Id, szResName[pResEntry[i].Id]);
                }
                else
                {
    
    
                    char  type[20];
                    sprintf_s(type, "%d", pResEntry[i].Id);
                    // printf("资源类型ID: %p --> 类型: %s\n", pResEntry[i].Id, type);
                    printf("%p \t %s \n", pResEntry[i].Id, type);
                }
            }
            // 如果为1则执行
            else
            {
    
    
                PIMAGE_RESOURCE_DIR_STRING_U pstcString = (PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)pRes + pResEntry[i].NameOffset);
                WCHAR szStr[MAX_PATH] = {
    
     0 };
                memcpy_s(szStr, MAX_PATH, pstcString->NameString, pstcString->Length * sizeof(WCHAR));
                // printf("资源字符串: %ls\n", szStr);
            }
        }
    }
    else
    {
    
    
        printf("非标准程序 \n");
    }

    system("pause");
    return 0;
}

上記のプログラム フラグメントをコンパイルして実行すると、リーダーは現在のプログラムに含まれるすべてのリソース情報を確認できます。簡略化と使いやすさのため、ここでは再帰的なリソースは出力されません。最初のレイヤーのみが出力されます。出力レンダリングは次のとおりです。

この記事の著者: Wang Rui
この記事へのリンク: https://www.lyshark.com/post/6532d336.html
著作権に関する声明: 特に明記されていない限り、このブログのすべての記事には BY-NC-SA ライセンス契約が適用されます。出典を明記してください!

おすすめ

転載: blog.csdn.net/lyshark_csdn/article/details/132753384