Quantcast
Channel: 看雪安全论坛
Viewing all articles
Browse latest Browse all 9556

调试逆向 【原创】[15Pb培训第三阶段课后小项目]一款简易的PE-editor

$
0
0
目录

0x1     概述

0x2    MS-DOS头
0x2.2   PE文件头
0x2.3   区段表
0x2.4   导出表
0x2.5   导入表
0x2.6   资源表
0x2.7   重定位表

0x3     PE文件头信息编辑

0x4     16进制查看功能

0x5     总结


#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 

前言
为可以更好的理解pe文件结构,尝试写的一款简易的PE-Loader。

目前编辑功能仅实现了个别dos和nt头中基本信息的修改,editor功能有待加强。

感谢15pb老师们的指导。

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 

正文

[SIZE="3"][B]0x1     概述

PE,即Portable Executable的首字母简写,它是Win32平台可执行文件的标准格式。

编写一个简单的,类似PELoader的PE文件分析程序。程序大致实现功能及界面如下:
附件 84786

(注:这个强制功能,没什么亮点,比如加载PE文件了,其他选项才可以使用,判断不是pe则btn置灰,在使用过程中,修改了e_magic发现工具分析不了了,所以为一些修改过的PE方便使用(修改回来),所以加了这么个按钮:-D)

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 

0x2 MS-DOS头

简要描述:

PE文件头部首先第一部分是MS-DOS头,MS-DOS头部有两个部分构成,第一部分是MZ文件头,紧跟MZ文件头后面的是一个DOS可执行文件。其结构体定义如下:
  1. Typedef  struct  _IMAGE_DOS_HEADER{
  2.   WORD e_magic;           // 魔术数字
  3.   WORD e_cblp;              // 文件最后页的字节数
  4.   WORD e_cp;                 // 文件页数
  5.   WORD e_crlc;               // 重定义元素个数
  6.   WORD e_cparhdr;        // 头部尺寸,以段落为单位
  7.   WORD e_minalloc;        // 所需的最小附加段
  8.   WORD e_maxalloc;       // 所需的最大附加段
  9.   WORD e_ss;                 // 初始的SS值(相对偏移量)
  10.   WORD e_sp;                 // 初始的SP值
  11.   WORD e_csum;             // 校验和
  12.   WORD e_ip;                  // 初始的IP值
  13.   WORD e_cs;                 // 初始的CS值(相对偏移量)
  14.   WORD e_lfarlc;             // 重分配表文件地址
  15.   WORD e_ovno;             // 覆盖号
  16.   WORD e_res[4];           // 保留字
  17.   WORD e_oemid;           // OEM标识符(相对e_oeminfo)
  18.   WORD e_oeminfo;        // OEM信息
  19.   WORD e_res2[10];       // 保留字
  20.   LONG e_lfanew;           // 新exe头部的文件地址
  21. } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

由这个结构体,我们至少可以知道:

1) 通过判断e_magic的值,可知道文件是不是ms-dos下的可执行文件
2) 最后一个e_lfanew字段可以引导我们找到新的exe文件头.
3) 偏移为0x18的e_lfarlc字段指向的重定位表头部其实也是DOS Stub的起始偏移地址,由此向前推4个字节便是指向PE文件头e_lfanew的地址。

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
0x2.2 PE文件头

简要描述:PE文件头位于dos stub之后。是一个一PE/0/0为起始标记,由MS-DOS头中e_lfanew字段指向的结构。PE文件头是Windows NT内核下判断可执行文件的唯一有效结构,由3个字段组成:

一、 IMAGE_NT_HEADERS32
  1. typedef struct _IMAGE_NT_HEADERS {
  2.     DWORD Signature;                                             //[0x00] PE文件标识
  3.     IMAGE_FILE_HEADER FileHeader;                       //[0x04]文件头
  4.     IMAGE_OPTIONAL_HEADER32 OptionalHeader;  //[0x18]扩展头
  5. } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

二、 IMAGE_FILE_HEADER
  1. typedef struct _IMAGE_FILE_HEADER {
  2.     WORD    Machine;
  3.     WORD    NumberOfSections;
  4.     DWORD   TimeDateStamp;
  5.     DWORD   PointerToSymbolTable;
  6.     DWORD   NumberOfSymbols;
  7.     WORD    SizeOfOptionalHeader;
  8.     WORD    Characteristics;
  9. } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

三、 IMAGE_OPTIONAL_HEADER
  1. typedef struct _IMAGE_OPTIONAL_HEADER {
  2.     //
  3.     // Standard fields.
  4.     //
  5.     WORD    Magic;
  6.     BYTE    MajorLinkerVersion;
  7.     BYTE    MinorLinkerVersion;
  8.     DWORD   SizeOfCode;
  9.     DWORD   SizeOfInitializedData;
  10.     DWORD   SizeOfUninitializedData;
  11.     DWORD   AddressOfEntryPoint;
  12.     DWORD   BaseOfCode;
  13.     DWORD   BaseOfData;
  14.     //
  15.     // NT additional fields.
  16.     //
  17.     DWORD   ImageBase;
  18.     DWORD   SectionAlignment;
  19.     DWORD   FileAlignment;
  20.     WORD    MajorOperatingSystemVersion;
  21.     WORD    MinorOperatingSystemVersion;
  22.     WORD    MajorImageVersion;
  23.     WORD    MinorImageVersion;
  24.     WORD    MajorSubsystemVersion;
  25.     WORD    MinorSubsystemVersion;
  26.     DWORD   Win32VersionValue;
  27.     DWORD   SizeOfImage;
  28.     DWORD   SizeOfHeaders;
  29.     DWORD   CheckSum;
  30.     WORD    Subsystem;
  31.     WORD    DllCharacteristics;
  32.     DWORD   SizeOfStackReserve;
  33.     DWORD   SizeOfStackCommit;
  34.     DWORD   SizeOfHeapReserve;
  35.     DWORD   SizeOfHeapCommit;
  36.     DWORD   LoaderFlags;
  37.     DWORD   NumberOfRvaAndSizes;
  38.     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
  39. } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

从这三个结构体中可以获得的信息许多,如NT_HEADERS32中字段Signature,这是PE文件头的标识,其值始终是0x50450000。Win32SDK使用#define IMAGE_NT_SIGNATURE定义了这个值:
#define IMAGE_NT_SIGNATURE 0x50450000    // PE00

再如IAMGE_FILE_HEADER结构体中的字段SizeOfOptionalHeader,这个字段标识了扩展头的大小。字段NumberOfSections标识了区段的数目,等等。

在IMAGE_OPTIOAN_HEADER32中,字段
AddressOfEntryPoint                                            // [0x28] 程序执行入口
ImageBase                                                           // [0x34] 程序默认载入基地址
NumberOfRvaAndSizes                                         // [0x74] 数据目录表的数量
IMAGE_DATA_DIRECTORY DataDirectory[0x10]    // [0x78] 数据目录表的结构体数组
...

IMAGE_DATA_DIRECTORY的结构如下:
  1. typedef struct _IAMGE_DATA_DIRECTORY
  2. {
  3.         DWORD VirtualAddress;      // 数据块的起始地址
  4.         DWORD Size;                      // 数据块的长度
  5. }IAMGE_DATA_DIRECTORY, PIAMGE_DATA_DIRECTORY;

在Win32SDK中也使用了一组宏来表示不同成员的信息。

相关功能展示:
附件 84787

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 

0x2.3  区段表

简要描述:
PE文件头的数据目录表后是区段表,区段表用来描述位于其后各个区段的各种属性。区段表是由数个首尾相连的IMAGE_SECTION_HEADER结构体数组构成,可以使用IMAGE_FIRST_SECTION32(NtHeader)宏找到第一个区段表所在的位置。示例:
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION32(NtHeader);

可以使用如下代码循环获取其他节:
  1. PIMAGE_SECTION_HEADER pSection1 = IMAGE_FIRST_SECTION(pNT32);
  2.     for( DWORD i=0; i<pImageHeader->NumberOfSections; i++ )
  3.     {
  4.         pSection.push_back(pSection1[i]);
  5. }


IMAGE_SECTION_HEADER结构如下:
  1. typedef struct _IMAGE_SECTION_HEADER {
  2.     BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
  3.     union {
  4.             DWORD   PhysicalAddress;
  5.             DWORD   VirtualSize;
  6.     } Misc;
  7.     DWORD   VirtualAddress;
  8.     DWORD   SizeOfRawData;
  9.     DWORD   PointerToRawData;
  10.     DWORD   PointerToRelocations;
  11.     DWORD   PointerToLinenumbers;
  12.     WORD    NumberOfRelocations;
  13.     WORD    NumberOfLinenumbers;
  14.     DWORD   Characteristics;
  15. } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

这个结构里包含了可以详细描述该区段属性的字段信息,如区段名、长度、属性等内容。

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 

0x2.4  导出表

简要描述:
导出表是PE文件中非常重要的一个数据结构,是PE文件为其它应用程序提供API的一种函数实例导出方式。

Windows下存在导出表的可执行文件以指定自身的一些变量、函数及类,并将其导出,以便提供第三方程序使用。

IMAGE_EXPORT_DIRECTORY结构如下:
  1. typedef struct _IMAGE_EXPORT_DIRECTORY {
  2.     DWORD   Characteristics;
  3.     DWORD   TimeDateStamp;
  4.     WORD     MajorVersion;
  5.     WORD     MinorVersion;
  6.     DWORD   Name;
  7.     DWORD   Base;
  8.     DWORD   NumberOfFunctions;
  9.     DWORD   NumberOfNames;
  10.     DWORD   AddressOfFunctions;        // RVA from base of image
  11.     DWORD   AddressOfNames;             // RVA from base of image
  12.     DWORD   AddressOfNameOrdinals;  // RVA from base of image
  13. } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

上述结构说明:
1)  Characteristics         :    保留,恒为0x00000000。
2)  TimeDateStamp        :    导出表创建的时间
3)  MajorVersion            :    导出表的主版本号
4)  MinorVersion             :    导出表的主版本号
5)  Name                        :    指向此导出表所在的模块名称的Ascii码的RVA
6)  Base                         :    导出表用于输出API函数索引值的基数,一般情况下此值为1
7)  NumberOfFunctions  :    函数个数
8) NumberOfNames      :   名称表中名称数目
其后三个字段分别标识导出函数、导出函数名、导出序号的相对虚拟地址。

导出表的大致结构图如下:
附件 84776

获取导出表信息的相关代码:
代码:

PIMAGE_DOS_HEADER        pDos      = (PIMAGE_DOS_HEADER)lpImage;
    PIMAGE_NT_HEADERS32      pNT32     = (PIMAGE_NT_HEADERS32)((LONG)lpImage+pDos->e_lfanew);
    PIMAGE_OPTIONAL_HEADER32 pOptional = &(pNT32->OptionalHeader);
    PIMAGE_DATA_DIRECTORY    pDirec    = (PIMAGE_DATA_DIRECTORY)pOptional->DataDirectory;

    
    // 导出表的FOA
    PIMAGE_EXPORT_DIRECTORY pExp = (PIMAGE_EXPORT_DIRECTORY)((LONG)lpImage+RVA2FOA(lpImage, pDirec->VirtualAddress));

    DWORD dwNum = RVA2FOA(lpImage,pExp->AddressOfNameOrdinals);
    PWORD pNum = (PWORD)(dwNum+(DWORD)lpImage);


    DWORD pfn = RVA2FOA(lpImage,pExp->AddressOfFunctions);
    PDWORD pGet = PDWORD(pfn+(DWORD)lpImage);

    DWORD dwPNames  = RVA2FOA(lpImage,pExp->AddressOfNames);
    PDWORD pdwNames = PDWORD(dwPNames+(DWORD)lpImage);
    DWORD dwNames = RVA2FOA(lpImage,(DWORD)(*pdwNames));
    dwNames = (dwNames)+(DWORD)lpImage;

    vecDllNum.clear();
    vecNames.clear();
    vecAdd.clear();

    for( DWORD i=0; i<pExp->NumberOfFunctions; i++ )
    {

        if( !(*pGet) )
        {
            pGet++;
            continue;
        }
        int count=0;  BOOL bflag = FALSE;
        for( ; count<pExp->NumberOfNames; count++ )
        {
            if( i==pNum[count] )
            {
                bflag = TRUE;
                break;
            }
        }


        CString csTempNum;
        csTempNum.Format(_T("0x%p"),i+pExp->Base);
        vecDllNum.push_back(csTempNum);
        
        csTempNum.Format(_T("0x%p"),*pGet);
        vecAdd.push_back(csTempNum);
        
        
        CHAR Temp[100];

        if( !bflag )
        {
            csTempNum = _T("--");
        }
        else
        {
            sprintf_s(Temp,"%s",dwNames);
            csTempNum = Temp;
            pdwNames++;
            dwNames = RVA2FOA(lpImage,(DWORD)(*pdwNames));
            dwNames = (dwNames)+(DWORD)lpImage;
        }

        vecNames.push_back(csTempNum);
        pGet++;
    }

导出表功能测试截图如下(以32位user32.dll为例):
附件 84777

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 

0x2.5  导入表

简要描述:
导入表机制是PE文件从其他第三方程序中导入API,供本程序调用的机制。
比如导入表在反病毒的领域里,技术人员在某些情况下仅根据导入表就能猜测出此程序的大致行为。由此导入表的重要性可见一斑。

导入表(IMAGE_IMPORT_DESCRIPTOR)结构定义如下:
代码:

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time stamp
                                            //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                            // O.W. date/time stamp of DLL bound to (Old BIND)
 
    DWORD   ForwarderChain;                 // -1 if no forwarders
    DWORD   Name;
    DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

上述结构说明:
  1. OriginalFirstThunk : 包含指向INT的RVA,INT是一个IMAGE_THUNK_DATA结构的数组,大多情况下,数组中的每个IMAGE_THUNK_DATA结构会再指向IMAGE_IMPORT_BY_NAME结构,结尾处以全0x00的IMAGE_THUNK_DATA结构结束.
  2. TimeDateStamp     :  一个32位的时间标识,与下一个转发链字段合并工作,否则可以为空。
  3. ForwarderChain     :  转发链,如果不转发则此值为0
  4. Name                     :  指向导入文件的名字
  5. FirstThunk              :  指向导入地址表的RVA

在一般情况下,对于导入表只需要关注OriginalFirstThunk与FirstThunk,两个字段分别指向了保存导出名称与导出地址的IMAGE_THUNK_DATA的结构数组,且这个数组是以一个空的IMAGE_THUNK_DATA结构结尾,其结构的详细内容如下:

代码:

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;       // PBYTE 
        DWORD Function;               // PDWORD
        DWORD Ordinal;                     // 被导入函数的序号
        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

上述结构说明如下:
  1. ForwarderString  :  负责与导入转发表forwarders协同工作的一个字段。当导入表的ForwarderChain不为0时,此值有效,并指向包含有转发函数与导出这个函数的映像文件名的字符串RVA。
  2. Function              :  导入表导入函数的实际内存地址,此字段仅在此映像被加载,且此结构为IAT的前提下有效。
  3. Ordinal                :  被导入函数的序号
  4. AddressOfData    :  指向IMAGE_IMPORT_BY_NAME结构,当以上3个值都未生效时,此值有效。

下面了解一下IMAGE_IMPORT_BY_NAME结构
  1. typedef struct _IMAGE_IMPORT_BY_NAME
  2. {
  3.       WORD Hint;             // 需导入的函数序号
  4.       BYTE    Name[1];     // 需导入的函数名称
  5. }IMAGE_IMPORT_BY_NAME, PIMAGE_IMPORT_BY_NAME;

上述结构说明如下:
Hint          :     保存着本映像导入表需导入的函数序号
Name       :     保存着本映像导入表需导入的函数名称

IMAGE_IMPORT_BY_NAME结构以一个结构数组的形式存在,并以一个空的IMAGE_IMPORT_BY_NAME结构结束。

导入表的大致结构图如下:
附件 84780

获取导入表相关信息的代码如下:
代码:

// 2. 循环遍历导入表
    while ( pImport->Name )
    {
 
        IMPORT_DLL_INFO stcImportInfo;
        // 2.1 获取导入项DLL名称
        PCHAR pszDllName = (PCHAR)((DWORD)lpImage+RVA2FOA(lpImage,pImport->Name));
        // 2.2 获取INT
        PIMAGE_THUNK_DATA32 pINT = (PIMAGE_THUNK_DATA32)((DWORD)lpImage+RVA2FOA(lpImage,(DWORD)pImport->OriginalFirstThunk));
        // 2.3 导入项头部信息
         
        
        dwThunkValue = *pdwThunkValue;
 
        stcImportInfo.ThunkOffset.push_back(dwTempThunkOffset);
        stcImportInfo.ThunkRVA.push_back(dwTempThunkRVA);
        stcImportInfo.ThunkValue.push_back(dwThunkValue);
 
        lvItem.iItem = dwIndex++;
        CString csDllName; csDllName = pszDllName;
        lvItem.pszText = (LPWSTR)(LPCWSTR)csDllName;
        ListView_InsertItem(hListWnd, &lvItem);
 
        stcImportInfo.DllName = csDllName;
 
        csDllName.Format(_T("0x%p"),pImport->OriginalFirstThunk);
        ListView_SetItemText(hListWnd,lvItem.iItem,1,(LPWSTR)(LPCWSTR)csDllName);
 
        csDllName.Format(_T("0x%p"),pImport->TimeDateStamp);
        ListView_SetItemText(hListWnd,lvItem.iItem,2,(LPWSTR)(LPCWSTR)csDllName);
 
        csDllName.Format(_T("0x%p"),pImport->ForwarderChain);
        ListView_SetItemText(hListWnd,lvItem.iItem,3,(LPWSTR)(LPCWSTR)csDllName);
 
        csDllName.Format(_T("0x%p"),pImport->Name);
        ListView_SetItemText(hListWnd,lvItem.iItem,4,(LPWSTR)(LPCWSTR)csDllName);
 
        csDllName.Format(_T("0x%p"),pImport->FirstThunk);
        ListView_SetItemText(hListWnd,lvItem.iItem,5,(LPWSTR)(LPCWSTR)csDllName);
 
         // 2.4 循环INT的内容
        while ( pINT->u1.Ordinal )
        {
            // 2.4.1 判断最高位是否不为1,是的话则打印其AddressOfData的内容
            if ( !IMAGE_SNAP_BY_ORDINAL32(pINT->u1.Ordinal) )
            {
                PIMAGE_IMPORT_BY_NAME pByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)lpImage+RVA2FOA(lpImage,pINT->u1.AddressOfData));
                //printf( "%04X %s\r\n", pByName->Hint, pByName->Name );
                CString csTemp;
                csTemp.Format(_T("%04x"),pByName->Hint);
                stcImportInfo.vecHint.push_back(csTemp);
                csTemp = pByName->Name;
                stcImportInfo.vecName.push_back(csTemp);
 
                 
 
                dwTempThunkOffset += 4;
                dwTempThunkRVA    += 4;
                pdwThunkValue++;
                dwThunkValue = *pdwThunkValue;
                 
                stcImportInfo.ThunkOffset.push_back(dwTempThunkOffset);
                stcImportInfo.ThunkRVA.push_back(dwTempThunkRVA);
                stcImportInfo.ThunkValue.push_back(dwThunkValue);
 
                pINT++;
                continue;
            } 
            // 2.4.2 如果最高位为1,则直接打印Ordinal部分
            CString csTemp;
            csTemp.Format(_T("--"));
            stcImportInfo.vecHint.push_back(csTemp);
            csTemp.Format(_T("%d"),pINT->u1.Ordinal&0x7FFF);
            stcImportInfo.vecName.push_back(csTemp);
 
 
 
            dwTempThunkOffset += 4;
            dwTempThunkRVA    += 4;
            pdwThunkValue++;
            dwThunkValue = *pdwThunkValue;
 
            stcImportInfo.ThunkOffset.push_back(dwTempThunkOffset);
            stcImportInfo.ThunkRVA.push_back(dwTempThunkRVA);
            stcImportInfo.ThunkValue.push_back(dwThunkValue);
 
            pINT++;
            continue;
//             printf( "%04X %s\r\n", pINT->u1.Ordinal&0x0000FFFF, "(Null)" );
//             pINT++;
        }
//         // 2.5 打印导入项的尾部信息,并将指针加1,使其指向下一项导入表结构
//         printf( "====================================\r\n\r\n" );
         vecDllInfo.push_back(stcImportInfo);
 
         dwTempThunkOffset += 4;
         dwTempThunkRVA    += 4;
         pdwThunkValue++;
         dwThunkValue = *pdwThunkValue;
 
         pImport++;
    }

导入表功能测试截图如下(以32位user32.dll为例):
附件 84781

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 

0x2.6  资源表

简要描述:

Windows下程序中的各种界面组成部分称为资源,比如菜单、图标、快捷键、版本信息以及其他未格式化的二进制资源。
数据目录表中的IMAGE_DIRECTORY_ENTRY_RESOURCE项指向此结构

资源结构:
资源在PE文件中以目录结构的形式存在,一般情况下分为3层,从根目录开始分别为资源类型、目录资源ID与资源代码页。
此3层目录结构都是由一个IMAGE_RESOURCE_DIRECTORY结构为头部,并在后面跟着一个IMAGE_RESOURCE_DIRECTORY_ENTRY结构数组。

IMAGE_RESOURCE_DIRECTORY结构如下:

代码:

typedef struct _IMAGE_RESOURCE_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    WORD    NumberOfNamedEntries;
    WORD    NumberOfIdEntries;
//  IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

这个结构负责指出将紧随其后的IMAGE_RESOURCE_DIRECTORY_ENTRY结构数组的成员个数.IMAGE_RESOURCE_DIRECTORY_ENTRY结构如下:
代码:

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;

获取资源表相关信息的代码如下:
代码:

// 2. 循环遍历资源表
   // 2.1 第一层目录遍历
   DWORD dwCount1 = pResource1->NumberOfIdEntries+pResource1->NumberOfNamedEntries;
   for ( DWORD i=0; i<dwCount1; i++ )
   {
 
       SRC_1ST stc1st;
 
       PIMAGE_RESOURCE_DIRECTORY_ENTRY pDirEntry1 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)pResource1+sizeof(IMAGE_RESOURCE_DIRECTORY));
       if ( pDirEntry1[i].NameIsString )
       {
           PIMAGE_RESOURCE_DIR_STRING_U pString = (PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)pResource1+pDirEntry1[i].NameOffset);
           stc1st.csType = pString->NameString;
           //printf( "Type: %ls", pString->NameString );
           //printf("\r\n");
       } 
       else
       {
           if ( pDirEntry1[i].Name>0x10 )
               stc1st.csType.Format(_T("%d"),pDirEntry1[i].Name);
               //printf( "Type:%04X\r\n", pDirEntry1[i].Name );
           else
               stc1st.csType = szResourceType[pDirEntry1[i].Name];
               //printf( "Type:%s\r\n", szResourceType[pDirEntry1[i].Name] );
       }
 
       if ( pDirEntry1[i].DataIsDirectory )
       {
           SRC_2ND stc2nd;
 
           // 2.2 第二层目录的遍历
           PIMAGE_RESOURCE_DIRECTORY pResource2 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)pResource1+pDirEntry1[i].OffsetToDirectory);
           DWORD dwCount2 = pResource2->NumberOfIdEntries+pResource2->NumberOfNamedEntries;
           for ( DWORD j=0; j<dwCount2; j++ )
           {
               PIMAGE_RESOURCE_DIRECTORY_ENTRY pDirEntry2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)pResource2+sizeof(IMAGE_RESOURCE_DIRECTORY));
               if ( pDirEntry2[j].NameIsString )
               {
                   PIMAGE_RESOURCE_DIR_STRING_U pString = (PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)pResource2+pDirEntry2[j].NameOffset);
                   stc2nd.csID = pString->NameString;
                    
                   //printf( "\tID: %ls", pString->NameString );
                   //printf("\r\n");
               } 
               else
               {
                   stc2nd.csID.Format(_T("%d"),pDirEntry2[j].Name);
                   //printf( "\tID:%04X\r\n", pDirEntry2[j].Name );
               }
               if ( pDirEntry2[j].DataIsDirectory )
               {
                   // 2.3 第三层目录的遍历
                   PIMAGE_RESOURCE_DIRECTORY pResource3 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)pResource1+pDirEntry2[j].OffsetToDirectory);
                    
                   stc2nd.vec3rd.push_back(pResource3);
                    
                   DWORD dwCount3 = pResource3->NumberOfIdEntries+pResource3->NumberOfNamedEntries;
                   for ( DWORD k=0; k< dwCount3; k++ )
                   {
                       PIMAGE_RESOURCE_DIRECTORY_ENTRY pDirEntry3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)pResource3+sizeof(IMAGE_RESOURCE_DIRECTORY));
                       //printf( "\t\tLng:%04X\r\n", pDirEntry3[k].Name );
                       PIMAGE_RESOURCE_DATA_ENTRY pData = (PIMAGE_RESOURCE_DATA_ENTRY)((DWORD)pResource1+pDirEntry3[k].OffsetToData);
                       printf( "\t\tRVA:0x%p Size:%04d Page:0x%p\r\n", pData->OffsetToData, pData->Size, pData->CodePage );
                   }
               } 
               else
               {
                   // ....
               }
               stc1st.vec2nd.push_back(stc2nd);
           }
 
       }

资源表相关功能截图:
附件 84782

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 

0x2.7  重定位表

简要描述:

由于Windows系统中DLL文件并不是每次都能加载到预设的基址上,所以基址重定位主要应用于DLL文件中。

一般情况下,重定位表位于一个名为.reloc的区块内,PE对于重定位的定义非常简单,无需参考外部信息或模块中的其他节,只需简单将文件中所有需要重定位的地址放在一个数组里即可。如果此映像未能在预设的基址上载入,那么加载器就会简单的将数组中的重定位信息逐一修改。

重定位表相关结构:
代码:

  // 2. 遍历并执行虚拟重定位操作,并打印结果
  while ( pReloc->VirtualAddress )
  {
        dwIndex++;
        stcReloc.dwIndex = dwIndex;

    DWORD dwImageBase        = pNT32->OptionalHeader.ImageBase;
    DWORD dwRelocBlockBase   = pReloc->VirtualAddress;
    DWORD dwCount            = (pReloc->SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))/sizeof(WORD);
    PTYPE_OFFSET pTypeOffset = (PTYPE_OFFSET)((DWORD)pReloc+sizeof(IMAGE_BASE_RELOCATION));
        stcReloc.dwRVA = dwRelocBlockBase;
        stcReloc.dwCount = dwCount;

        for ( DWORD i=0; i<pNT32->FileHeader.NumberOfSections; i++ )
        {
            if ( dwRelocBlockBase>=pSection[i].VirtualAddress && dwRelocBlockBase<pSection[i].VirtualAddress+pSection[i].Misc.VirtualSize )
            {
                stcReloc.csSection = pSection[i].Name;
                break;
            }

        }


        vecReloc.push_back(stcReloc);

        stcRelocDetail.dwFlag = dwIndex;

    for ( DWORD i=0; i<dwCount; i++ )
    {
            stcRelocDetail.stcTypeOffset.Type   = pTypeOffset->Type;
            stcRelocDetail.stcTypeOffset.Offset = ((pTypeOffset+i)->Offset + dwRelocBlockBase);

            vecRelocDetail.push_back(stcRelocDetail);

    }
    // 指向下一个重定位表的起始地址
    pReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pReloc+pReloc->SizeOfBlock);
  }

资源表相关功能截图:
附件 84783

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 

0x3  PE文件头信息编辑

简要描述:
可选择相关字段进行编辑。保存与当前文件,或者另存新文件。目前仅可对指定字段进行编辑。
开始设计的失败,扩展头好多信息使用静态文本框显示内容,不方便文件内容的修改。
所以这里仅仅提供了下拉列表框中的信息修改。修改测试成功的:-D

功能截图如下:
附件 84784

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 

0x4  16进制查看功能

简要描述:
这个16进制的编辑功能是在网上找到的16edit库来加的功能。

功能截图如下:
附件 84785

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 

0x5  总结

目前程序的功能如上所述。
简易的一款PE Editor。
编写的过程中,加深了对PE文件结构的认识,也找到些编程的中的种种问题。


#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
全文后记:
感谢学习过程中老师们和同学们的帮助:-D
参考书籍:《黑客免杀攻防》

上传的图像
文件类型: jpg a4.jpg (50.9 KB)
文件类型: jpg a5.jpg (53.3 KB)
文件类型: jpg a6.jpg (44.8 KB)
文件类型: jpg a7.jpg (68.6 KB)
文件类型: jpg a8.jpg (28.3 KB)
文件类型: jpg a9.jpg (45.7 KB)
文件类型: jpg a10.jpg (44.3 KB)
文件类型: jpg a11.jpg (118.0 KB)
文件类型: jpg aa.jpg (50.7 KB)
文件类型: jpg aaa.jpg (90.3 KB)

Viewing all articles
Browse latest Browse all 9556

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>