调试信息段,.debug

  调试信息位于.debug段之中,同时PE文件格式也支持单独的调试文件(通常由.DBG扩展名标识)作为一种将调试信息集中的方法。调试段包含了调试信息,但是调试目录却位于早先提到的.rdata段之中。这其中每个目录都涉及了.debug段之中的调试信息。调试目录的结构IMAGE_DEBUG_DIRECTORY被定义为:
WINNT.H
typedef struct _IMAGE_DEBUG_DIRECTORY {
  ULONG Characteristics;
  ULONG TimeDateStamp;
  USHORT MajorVersion;
  USHORT MinorVersion;
  ULONG Type;
  ULONG SizeOfData;
  ULONG AddressOfRawData;
  ULONG PointerToRawData;
} IMAGE_DEBUG_DIRECTORY, *PIMAGE_DEBUG_DIRECTORY;
  这个段被分为单独的部分,每个部分为不同种类的调试信息数据。对于每个部分来说都是一个像上边一样的调试目录。不同的调试信息种类如下:
WINNT.H
#define IMAGE_DEBUG_TYPE_UNKNOWN 0
#define IMAGE_DEBUG_TYPE_COFF 1
#define IMAGE_DEBUG_TYPE_CODEVIEW 2
#define IMAGE_DEBUG_TYPE_FPO 3
#define IMAGE_DEBUG_TYPE_MISC 4
  每个目录之中的Type域表示该目录的调试信息种类。如你所见,在上边的表中,PE文件格式支持很多不同的调试信息种类,以及一些其它的信息域。对于那些来说,IMAGE_DEBUG_TYPE_MISC信息是唯一的。这一信息被添加到描述可执行映像的混杂信息之中,这些混杂信息不能被添加到PE文件格式任何结构化的数据段之中。这就是映像文件中最合适的位置,映像名称则肯定会出现在这里。如果映像导出了信息,那么导出数据段也会包含这一映像名称。
  每种调试信息都拥有自己的头部结构,该结构定义了它自己的数据。这些结构都列于WINNT.H之中。关于IMAGE_DEBUG_DIRECTORY一件有趣的事就是它包括了两个标识调试信息的域。第一个是AddressOfRawData,为相对文件装载的数据虚拟地址;另一个是PointerToRawData,为数据所在PE文件之中的实际偏移量。这就使得定位指定的调试信息相当容易了。
  作为最后的例子,请你考虑以下的函数代码,它从IMAGE_DEBUG_MISC结构中提取了映像名称。
PEFILE.C
int WINAPI RetrieveModuleName(LPVOID lpFile, HANDLE hHeap,
    char **pszModule)
{
  PIMAGE_DEBUG_DIRECTORY pdd;
  PIMAGE_DEBUG_MISC pdm = NULL;
  int nCnt;
  if (!(pdd = (PIMAGE_DEBUG_DIRECTORY)ImageDirectoryOffset
      (lpFile, IMAGE_DIRECTORY_ENTRY_DEBUG)))
    return 0;
  while (pdd->SizeOfData)
  {
    if (pdd->Type == IMAGE_DEBUG_TYPE_MISC)
    {
      pdm = (PIMAGE_DEBUG_MISC)((DWORD)pdd->PointerToRawData +
          (DWORD)lpFile);
      nCnt = lstrlen(pdm->Data) * (pdm->Unicode ? 2 : 1);
      *pszModule = (char *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY,
          nCnt+1);
      CopyMemory(*pszModule, pdm->Data, nCnt);
      break;
    }
    pdd ++;
  }
  if (pdm != NULL)
    return nCnt;
  else
    return 0;
}
  你看到了,调试目录结构使得定位一个特定种类的调试信息变得相对容易了些。只要定位了IMAGE_DEBUG_MISC结构,提取映像名称就如同调用CopyMemory函数一样简单。
  如上所述,调试信息可以被剥离到单独的.DBG文件中。Windows NT SDK包含了一个名为REBASE.EXE的程序可以实现这一目的。例如,以下的语句可以将一个名为TEST.EXE的调试信息剥离:
  rebase -b 40000 -x c:/samples/testdir test.exe
  调试信息被置于一个新的文件中,这个文件名为TEST.DBG,位于c:/samples/testdir之中。这个文件起始于一个单独的IMAGE_SEPARATE_DEBUG_HEADER结构,接着是存在于原可执行映像之中的段头部的一份拷贝。在段头部之后,是.debug段的数据。也就是说,在段头部之后,就是一系列的IMAGE_DEBUG_DIRECTORY结构及其相关的数据了。调试信息本身保留了如上所描述的常规映像文件调试信息。

PE文件格式总结

  Windows NT的PE文件格式向熟悉Windows和MS-DOS环境的开发者引入了一种全新的结构。然而熟悉UNIX环境的开发者会发现PE文件格式与COFF规范很相像(如果它不是以COFF为基础的话)。
  整个格式的组成:一个MS-DOS的MZ头部,之后是一个实模式的残余程序、PE文件标志、PE文件头部、PE可选头部、所有的段头部,最后是所有的段实体。
  可选头部的末尾是一个数据目录入口的数组,这些相对虚拟地址指向段实体之中的数据目录。每个数据目录都表示了一个特定的段实体数据是如何组织的。
  PE文件格式有11个预定义段,这是对Windows NT应用程序所通用的,但是每个应用程序可以为它自己的代码以及数据定义它自己独特的段。
  .debug预定义段也可以分离为一个单独的调试文件。如果这样的话,就会有一个特定的调试头部来用于解析这个调试文件,PE文件中也会有一个标志来表示调试数据被分离了出去。

PEFILE.DLL函数描述

  PEFILE.DLL主要由一些函数组成,这些函数或者被用来获得一个给定的PE文件中的偏移量,或者被用来把文件中的一些数据复制到一个特定的结构中去。每个函数都有一个需求——第一个参数是一个指针,这个指针指向PE文件的起始处。也就是说,这个文件必须首先被映射到你进程的地址空间中,然后映射文件的位置就可以作为每个函数第一个参数的lpFile的值来传入了。
  我意在使函数的名称使你能够一见而知其意,并且每个函数都随一个详细描述其目的的注释而列出。如果在读完函数列表之后,你仍然不明白某个函数的功能,那么请参考EXEVIEW.EXE示例来查明这个函数是如何使用的。以下的函数原型列表可以在PEFILE.H中找到:
PEFILE.H
/* 获得指向MS-DOS MZ头部的指针 */
BOOL WINAPI GetDosHeader(LPVOID, PIMAGE_DOS_HEADER);

/* 决定.EXE文件的类型 */
DWORD WINAPI ImageFileType(LPVOID);

/* 获得指向PE文件头部的指针 */
BOOL WINAPI GetPEFileHeader(LPVOID, PIMAGE_FILE_HEADER);

/* 获得指向PE可选头部的指针 */
BOOL WINAPI GetPEOptionalHeader(LPVOID, PIMAGE_OPTIONAL_HEADER);

/* 返回模块入口点的地址 */
LPVOID WINAPI GetModuleEntryPoint(LPVOID);

/* 返回文件中段的总数 */
int WINAPI NumOfSections(LPVOID);

/* 返回当可执行文件被装载入进程地址空间时的首选基地址 */
LPVOID WINAPI GetImageBase(LPVOID);

/* 决定文件中一个特定的映像数据目录的位置 */
LPVOID WINAPI ImageDirectoryOffset(LPVOID, DWORD);

/* 获得文件中所有段的名称 */
int WINAPI GetSectionNames(LPVOID, HANDLE, char **);

/* 复制一个特定段的头部信息 */
BOOL WINAPI GetSectionHdrByName(LPVOID, PIMAGE_SECTION_HEADER,
    char *);

/* 获得由空字符分隔的导入模块名称列表 */
int WINAPI GetImportModuleNames(LPVOID, HANDLE, char **);

/* 获得一个模块由空字符分隔的导入函数列表 */
int WINAPI GetImportFunctionNamesByModule(LPVOID, HANDLE, char *,
    char **);

/* 获得由空字符分隔的导出函数列表 */
int WINAPI GetExportFunctionNames(LPVOID, HANDLE, char **);

/* 获得导出函数总数 */
int WINAPI GetNumberOfExportedFunctions(LPVOID);

/* 获得导出函数的虚拟地址入口点列表 */
LPVOID WINAPI GetExportFunctionEntryPoints(LPVOID);

/* 获得导出函数顺序值列表 */
LPVOID WINAPI GetExportFunctionOrdinals(LPVOID);

/* 决定资源对象的种类 */
int WINAPI GetNumberOfResources (LPVOID);

/* 返回文件中所使用的所有资源对象的种类 */
int WINAPI GetListOfResourceTypes(LPVOID, HANDLE, char **);

/* 决定调试信息是否已从文件中分离 */
BOOL WINAPI IsDebugInfoStripped(LPVOID);

/* 获得映像文件名称 */
int WINAPI RetrieveModuleName(LPVOID, HANDLE, char **);

/* 决定文件是否是一个有效的调试文件 */
BOOL WINAPI IsDebugFile(LPVOID);

/* 从调试文件中返回调试头部 */
BOOL WINAPI GetSeparateDebugHeader(LPVOID,
    PIMAGE_SEPARATE_DEBUG_HEADER);
  除了以上所列的函数之外,本文中早先提到的宏也定义在了PEFILE.H中,完整的列表如下:
/* PE文件标志的偏移量 */
#define NTSIGNATURE(a) ((LPVOID)((BYTE *)a + /
                       ((PIMAGE_DOS_HEADER)a)->e_lfanew))

/* MS操作系统头部标识了双字的NT PE文件标志;PE文件头部就紧跟在这个双字之后 */
#define PEFHDROFFSET(a) ((LPVOID)((BYTE *)a + /
                        ((PIMAGE_DOS_HEADER)a)->e_lfanew + /
                        SIZE_OF_NT_SIGNATURE))

/* PE可选头部紧跟在PE文件头部之后 */
#define OPTHDROFFSET(a) ((LPVOID)((BYTE *)a + /
                        ((PIMAGE_DOS_HEADER)a)->e_lfanew + /
                        SIZE_OF_NT_SIGNATURE + /
                        sizeof(IMAGE_FILE_HEADER)))

/* 段头部紧跟在PE可选头部之后 */
#define SECHDROFFSET(a) ((LPVOID)((BYTE *)a + /
                        ((PIMAGE_DOS_HEADER)a)->e_lfanew + /
                        SIZE_OF_NT_SIGNATURE + /
                        sizeof(IMAGE_FILE_HEADER) + /
                        sizeof(IMAGE_OPTIONAL_HEADER)))
  要使用PEFILE.DLL,你只用包含PEFILE.H文件并在应用程序中链接到这个DLL即可。所有的这些函数都是互斥性的函数,但是有些函数的功能可以相互支持以获得文件信息。例如,GetSectionNames可以用于获得所有段的名称,这样一来,为了获得一个拥有独特段名称(在编译期由应用程序开发者定义的)的段头部,你就需要首先获得所有名称的列表,然后再对那个准确的段名称调用函数GetSectionHeaderByName了。现在,你可以享受我为你带来的这一切了!(全文完)
MSDN原文:The Portable Executable File Format from Top to Bottom
原作者:Randy Kath
李马 2003/12/24 译于山西太原

PE文件格式详解(7)相关推荐

  1. 【破解教程】PE文件格式详解(上)

    PE文件格式详解(上) 摘要 Windows NT 3.1引入了一种名为PE文件格式的新可执行文件格式.PE文件格式的规范包含在了MSDN的CD中(Specs and Strategy, Specif ...

  2. PE 文件格式 详解 一

    内容引用自:看雪<逆向工程原理>,http://www.blogfshare.com/pe-header-one.html . 如有错误,欢迎留言. 1.  PE文件是windows操作系 ...

  3. PE文件格式详解(3)

    PE可选头部 PE可执行文件中接下来的224个字节组成了PE可选头部.虽然它的名字是"可选头部",但是请确信:这个头部并非"可选",而是"必需&quo ...

  4. [系统安全] PE文件格式详解1

    文章目录 PE文件概述 PE文件相关各类地址 MS-DOS头① DOS Stub② PE头③ Signature字段 IMAGE_FILE_HEADER结构体 IMAGE_OPTIONAL_HEADE ...

  5. PE文件格式详解(手工实现一个可执行文件)

    [转载文章-看雪学院]非常经典 http://www.pediy.com/kssd/index.html 其实还是很多细节的地方没弄懂,保存下来,认真分析分析. [文章标题]: 手写可执行程序 [文章 ...

  6. PE文件格式详解(二)

    0x00 前言 上一篇讲到了PE文件头的中IMAGE_FILE_HEADER结构的第二个结构,今天从IMAGE_FILE_HEADER中第三个结构sizeOfOptionalHeader讲起.这个字段 ...

  7. PE文件格式详解(八)

    0x00 前言 前面了解了PE文件的输入和输出,今天来看看另一个重要的结构--资源.资源结构是很典型的树形结构,层层查找,最终找到资源位置. 0x01 资源结构介绍 Windows程序的各种界面成为资 ...

  8. PE文件详解(教程1-7)

    PE文件详解(教程1-7) ========================================= PE教程1: PE文件格式一览 PE 的意思就是 Portable Executable ...

  9. PE文件结构详解(二)可执行文件头

    by evil.eagle 转载请注明出处. http://blog.csdn.net/evileagle/article/details/11903197 在PE文件结构详解(一)基本概念里,解释了 ...

最新文章

  1. Linux CENTOS7 Linux流量监控工具-iftop 安装过程以及示例!
  2. (推荐阅读)H264, H265硬件编解码基础及码流分析
  3. 400 错误,因为url编码问题
  4. php的微信登录示例代码,网站微信登录实例代码
  5. nginx开启密码认证
  6. 统计信号处理知识点总结_统计信号处理-简单看看克拉美罗界
  7. 以30字符宽居中输出python字符串_从零开始学 Python 之字符串
  8. RMS调度器实现原理
  9. 什么是GSM,CDMA和3G?
  10. 蓝牙协议之配对和绑定学习笔记
  11. 读《所谓情商高,就是会说话》笔记
  12. 【Android 逆向】ELF 文件格式 ( 安装 010 Editor 二进制查看工具的 ELF.bt 插件模板 | 安装 ELF.bt 模板 | 打开 ELF 文件 )
  13. OpenGL之建立三维坐标网格
  14. 中文加解密异常的问题
  15. (转载)机器学习方法的PPT .
  16. #! 脚本解释器(如何不加node 直接执行js文件)
  17. classin安卓手机安装条件_ClassIn下载2020安卓最新版_手机app官方版免费安装下载_历趣...
  18. 最优化理论——可行方向法
  19. 2021OpenInfra年度报告摘要:OpenInfra在中国
  20. cad.net 文字

热门文章

  1. Eth2.0 -合并(Merge)
  2. javascript 老王开车去东北
  3. 用狄拉克符号推导旋转矩阵
  4. 让按钮变灰色,不可用/
  5. c语言缩进用tab还是空格,程序员编码首行缩进使用Tab键好还是空格好?
  6. java的微信公众号开发零(授权登陆,版本一)
  7. 提拔!知名教授,副部长级
  8. GSM模块TCP初始化流程
  9. 循环比赛日程表(match)
  10. Windows10超详细esmini的源码安装与测试运行——OpenScenario播放器