欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]一款简易的PE-editor

[原创]一款简易的PE-editor

目录
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老师们的指导。
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
正文
0x1     概述
PE,即Portable Executable的首字母简写,它是Win32平台可执行文件的标准格式。
编写一个简单的,类似PELoader的PE文件分析程序。程序大致实现功能及界面如下:

(注:这个强制功能,没什么亮点,比如加载PE文件了,其他选项才可以使用,判断不是pe则btn置灰,在使用过程中,修改了e_magic发现工具分析不了了,所以为一些修改过的PE方便使用(修改回来),所以加了这么个按钮:-D)
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
0x2 MS-DOS头
简要描述:
PE文件头部首先第一部分是MS-DOS头,MS-DOS头部有两个部分构成,第一部分是MZ文件头,紧跟MZ文件头后面的是一个DOS可执行文件。其结构体定义如下:


  • Typedef  struct  _IMAGE_DOS_HEADER{
  •   WORD e_magic;           // 魔术数字
  •   WORD e_cblp;              // 文件最后页的字节数
  •   WORD e_cp;                 // 文件页数
  •   WORD e_crlc;               // 重定义元素个数
  •   WORD e_cparhdr;        // 头部尺寸,以段落为单位
  •   WORD e_minalloc;        // 所需的最小附加段
  •   WORD e_maxalloc;       // 所需的最大附加段
  •   WORD e_ss;                 // 初始的SS值(相对偏移量)
  •   WORD e_sp;                 // 初始的SP值
  •   WORD e_csum;             // 校验和
  •   WORD e_ip;                  // 初始的IP值
  •   WORD e_cs;                 // 初始的CS值(相对偏移量)
  •   WORD e_lfarlc;             // 重分配表文件地址
  •   WORD e_ovno;             // 覆盖号
  •   WORD e_res[4];           // 保留字
  •   WORD e_oemid;           // OEM标识符(相对e_oeminfo)
  •   WORD e_oeminfo;        // OEM信息
  •   WORD e_res2[10];       // 保留字
  •   LONG e_lfanew;           // 新exe头部的文件地址
  • } 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

  • typedef struct _IMAGE_NT_HEADERS {
  •     DWORD Signature;                                             //[0x00] PE文件标识
  •     IMAGE_FILE_HEADER FileHeader;                       //[0x04]文件头
  •     IMAGE_OPTIONAL_HEADER32 OptionalHeader;  //[0x18]扩展头
  • } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

二、 IMAGE_FILE_HEADER

  • typedef struct _IMAGE_FILE_HEADER {
  •     WORD    Machine;
  •     WORD    NumberOfSections;
  •     DWORD   TimeDateStamp;
  •     DWORD   PointerToSymbolTable;
  •     DWORD   NumberOfSymbols;
  •     WORD    SizeOfOptionalHeader;
  •     WORD    Characteristics;
  • } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

三、 IMAGE_OPTIONAL_HEADER

  • typedef struct _IMAGE_OPTIONAL_HEADER {
  •     //
  •     // Standard fields.
  •     //
  •     WORD    Magic;
  •     BYTE    MajorLinkerVersion;
  •     BYTE    MinorLinkerVersion;
  •     DWORD   SizeOfCode;
  •     DWORD   SizeOfInitializedData;
  •     DWORD   SizeOfUninitializedData;
  •     DWORD   AddressOfEntryPoint;
  •     DWORD   BaseOfCode;
  •     DWORD   BaseOfData;
  •     //
  •     // NT additional fields.
  •     //
  •     DWORD   ImageBase;
  •     DWORD   SectionAlignment;
  •     DWORD   FileAlignment;
  •     WORD    MajorOperatingSystemVersion;
  •     WORD    MinorOperatingSystemVersion;
  •     WORD    MajorImageVersion;
  •     WORD    MinorImageVersion;
  •     WORD    MajorSubsystemVersion;
  •     WORD    MinorSubsystemVersion;
  •     DWORD   Win32VersionValue;
  •     DWORD   SizeOfImage;
  •     DWORD   SizeOfHeaders;
  •     DWORD   CheckSum;
  •     WORD    Subsystem;
  •     WORD    DllCharacteristics;
  •     DWORD   SizeOfStackReserve;
  •     DWORD   SizeOfStackCommit;
  •     DWORD   SizeOfHeapReserve;
  •     DWORD   SizeOfHeapCommit;
  •     DWORD   LoaderFlags;
  •     DWORD   NumberOfRvaAndSizes;
  •     IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
  • } 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的结构如下:

  • typedef struct _IAMGE_DATA_DIRECTORY
  • {
  •         DWORD VirtualAddress;      // 数据块的起始地址
  •         DWORD Size;                      // 数据块的长度
  • }IAMGE_DATA_DIRECTORY, PIAMGE_DATA_DIRECTORY;

在Win32SDK中也使用了一组宏来表示不同成员的信息。
相关功能展示:

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
0x2.3  区段表
简要描述:
PE文件头的数据目录表后是区段表,区段表用来描述位于其后各个区段的各种属性。区段表是由数个首尾相连的IMAGE_SECTION_HEADER结构体数组构成,可以使用IMAGE_FIRST_SECTION32(NtHeader)宏找到第一个区段表所在的位置。示例:
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION32(NtHeader);
可以使用如下代码循环获取其他节:

  • PIMAGE_SECTION_HEADER pSection1 = IMAGE_FIRST_SECTION(pNT32);
  •     for( DWORD i=0; i<pImageHeader->NumberOfSections; i++ )
  •     {
  •         pSection.push_back(pSection1[i]);
  • }

IMAGE_SECTION_HEADER结构如下:

  • typedef struct _IMAGE_SECTION_HEADER {
  •     BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
  •     union {
  •             DWORD   PhysicalAddress;
  •             DWORD   VirtualSize;
  •     } Misc;
  •     DWORD   VirtualAddress;
  •     DWORD   SizeOfRawData;
  •     DWORD   PointerToRawData;
  •     DWORD   PointerToRelocations;
  •     DWORD   PointerToLinenumbers;
  •     WORD    NumberOfRelocations;
  •     WORD    NumberOfLinenumbers;
  •     DWORD   Characteristics;
  • } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

这个结构里包含了可以详细描述该区段属性的字段信息,如区段名、长度、属性等内容。
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
0x2.4  导出表
简要描述:
导出表是PE文件中非常重要的一个数据结构,是PE文件为其它应用程序提供API的一种函数实例导出方式。
Windows下存在导出表的可执行文件以指定自身的一些变量、函数及类,并将其导出,以便提供第三方程序使用。
IMAGE_EXPORT_DIRECTORY结构如下:

  • typedef struct _IMAGE_EXPORT_DIRECTORY {
  •     DWORD   Characteristics;
  •     DWORD   TimeDateStamp;
  •     WORD     MajorVersion;
  •     WORD     MinorVersion;
  •     DWORD   Name;
  •     DWORD   Base;
  •     DWORD   NumberOfFunctions;
  •     DWORD   NumberOfNames;
  •     DWORD   AddressOfFunctions;        // RVA from base of image
  •     DWORD   AddressOfNames;             // RVA from base of image
  •     DWORD   AddressOfNameOrdinals;  // RVA from base of image
  • } 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      :   名称表中名称数目
其后三个字段分别标识导出函数、导出函数名、导出序号的相对虚拟地址。
导出表的大致结构图如下:

获取导出表信息的相关代码:
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为例):

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
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;

上述结构说明:

  • OriginalFirstThunk : 包含指向INT的RVA,INT是一个IMAGE_THUNK_DATA结构的数组,大多情况下,数组中的每个IMAGE_THUNK_DATA结构会再指向IMAGE_IMPORT_BY_NAME结构,结尾处以全0x00的IMAGE_THUNK_DATA结构结束.

  • TimeDateStamp     :  一个32位的时间标识,与下一个转发链字段合并工作,否则可以为空。

  • ForwarderChain     :  转发链,如果不转发则此值为0

  • Name                     :  指向导入文件的名字

  • 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;

上述结构说明如下:

  • ForwarderString  :  负责与导入转发表forwarders协同工作的一个字段。当导入表的ForwarderChain不为0时,此值有效,并指向包含有转发函数与导出这个函数的映像文件名的字符串RVA。

  • Function              :  导入表导入函数的实际内存地址,此字段仅在此映像被加载,且此结构为IAT的前提下有效。

  • Ordinal                :  被导入函数的序号

  • AddressOfData    :  指向IMAGE_IMPORT_BY_NAME结构,当以上3个值都未生效时,此值有效。

下面了解一下IMAGE_IMPORT_BY_NAME结构:

  • typedef struct _IMAGE_IMPORT_BY_NAME
  • {
  •       WORD Hint;             // 需导入的函数序号
  •       BYTE    Name[1];     // 需导入的函数名称
  • }IMAGE_IMPORT_BY_NAME, PIMAGE_IMPORT_BY_NAME;

上述结构说明如下:
Hint          :     保存着本映像导入表需导入的函数序号
Name       :     保存着本映像导入表需导入的函数名称
IMAGE_IMPORT_BY_NAME结构以一个结构数组的形式存在,并以一个空的IMAGE_IMPORT_BY_NAME结构结束。
导入表的大致结构图如下:

获取导入表相关信息的代码如下:
// 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为例):

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
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);

           }

 

       }

资源表相关功能截图:

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
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);

  }

资源表相关功能截图:

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

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
0x4  16进制查看功能
简要描述:
这个16进制的编辑功能是在网上找到的16edit库来加的功能。
功能截图如下:

#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
0x5  总结
目前程序的功能如上所述。
简易的一款PE Editor。
编写的过程中,加深了对PE文件结构的认识,也找到些编程的中的种种问题。
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=# 
全文后记:
感谢学习过程中老师们和同学们的帮助:-D
参考书籍:《黑客免杀攻防》

上传的附件 a4.jpg
a5.jpg
a6.jpg
a7.jpg
a8.jpg
a9.jpg
a10.jpg
a11.jpg
aa.jpg
aaa.jpg

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功