欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

自己写的文件分析器-PE DeCODER v1.0

程序员文章站 2022-09-21 17:59:59
在《软件加密技术》这本书里看过PE文件各部分的详细解释之后,我也有了一个自己写PE文件分析器的的想法。虽然好的分析器不在少数,但对于一堆十六进制数,有些朋友可能不明白它代表什么意思。如果在程序...

在《软件加密技术》这本书里看过PE文件各部分的详细解释之后,我也有了一个自己写PE文件分析器的的想法。虽然好的分析器不在少数,但对于一堆十六进制数,有些朋友可能不明白它代表什么意思。如果在程序里就可以将这些01序列转换成可以直接看懂得信息,那至少用户可以省去以后去查表的麻烦。怀着这样的想法,我仔细的研究了书中分析器PEInfo的源代码,我发现它没有提供信息转换的功能。

    通过研究发现,PEInfo是通过PE文件在内存中的映象来获取文件信息的,我在想是否还有别的方法可以绕过将文件映象到内存这一步,直接读取文件信息。这样的方法只有直接读取磁盘上的PE文件,在磁盘上寻找所需要的文件信息。

    在这里暂且不说这样的做法和内存映象法有什么优劣,我在此仅仅只是想找寻另一条解决问题的道路,并实现之。看完我的分析和源程序,大家自然知道孰优孰劣。

    为了避免引起混淆,程序中采用了与PE标准种类似变量名来定义关键的数据,如文件头,可选文件头,节表,导入表和导出表,具体名称定义细节可以在winnt.h里查到。整个程序是以面向过程的方式写的,适当结合了面向对象的特征。我将读取的PE文件信息封装在一个对象DataDump里,这样是为了方便数据的管理和最后输出分析报告。而对文件的分析则分别有一系列的子程序来完成。现将子程序说明如下:

//-------------------------------------------------------------------------------------------------------------------

  BOOL Is_EXE_file( ifstream& PE_file )                       //判断是否是合法的PE文件,是则返回true,否则返回false

  BOOL OutReady( CHAR filename[], ofstream& fout )            //输出准备,包括输出流和输出文件,是则返回true,否则返回false

  VOID WriterInfo( ofstream& fout )                           //输出程序版本信息

  BOOL Load_EXE_Info( ifstream& PE_file )                     //读取PE文件信息,成功返回true,否则返回false

  VOID Decode_EXE_Info(CHAR filename[], BOOL IsEXE, ifstream& PE_file, ofstream& fout)  //分析PE文件信息

  VOID ToNumeric( LPDWORD ptr, CHAR buf[], INT start, INT size )   //将字符数组从start位开始,转换size位为数值,放入ptr指向的DWORD类型变量中
  
  VOID ToString( LPSTR ptr, CHAR buf[], INT start, INT size)       //从字符数组从start位开始,取出其后的size位,放入一个ptr指向的的字符数组中

//-------------------------------------------------------------------------------------------------------------------

        class DataDump 
  {
    private :
    IMAGE_FILE_HEADER FILE_HEADER;                                  // IMAGE_FILE_HEADER 
    IMAGE_OPTIONAL_HEADER32 OPTIONAL_HEADER32;                      // IMAGE_OPTIONAL_HEADER32
    PIMAGE_SECTION_HEADER SECTION_HEADER;                           // PIMAGE_SECTION_HEADER
    IMAGE_IMPORT_DESCRIPTOR IMPORT_DESCRIPTOR;                      // IMAGE_IMPORT_DESCRIPTOR
    PIMAGE_EXPORT_DIRECTORY EXPORT_DIRECTORY;                       // PIMAGE_EXPORT_DIRECTORY

    DWORD ExVRk, ImVRk;                                             // 输出表和输入表在磁盘文件的偏移和RVA的差值

    public :                                                          // You can get the functions of these member functions below by their names.
    DataDump();
    ~DataDump();

    BOOL Set_FILE_HEADER( CHAR [], INT );
    BOOL Set_OPTIONAL_HEADER32( CHAR [], INT );
    BOOL Set_SECTION_HEADER32( CHAR [], INT );
    BOOL Set_EXPORT_TABLE( CHAR [],  INT );
    
                VOID GetReady( CHAR [] );
    DWORD Get_OPTIONAL_HEADER_SIZE( VOID ) const; 
    DWORD Get_SECTION_NUMBER( VOID ) const;
    DWORD Get_EXPORT_TABLE_RAW( VOID ) const;
    DWORD Get_IMPORT_TABLE_RAW( VOID ) const;

    VOID Set_Export_VRk( VOID );
    VOID Set_Import_VRk( VOID );
    BOOL Export_Table_Existed( VOID ) const;
    BOOL Import_Table_Existed( VOID ) const;
    
    BOOL Show_FILE_HEADER( ofstream& ) const;
    BOOL Show_OPTIONAL_HEADER32( ofstream& ) const;
    BOOL Show_SECTION_HEADER32( ofstream& ) const;
    BOOL Show_EXPORT_TABLE( ifstream&, ofstream& ) const;
    BOOL Show_IMPORT_TABLE( ifstream&, ofstream& ) const;

  };   
  DataDump pool; 
    
//-------------------------------------------------------------------------------------------------------------------

 

   DataDump类的实例是全局对象,这样做是方便子程序对该对象的访问。程序的基本思路是,在磁盘上打开PE文件,判断其是否为合法的PE文件,否则输出错误信息,退出;是则进行分析,包括读取文件头,可选文件头,节表,导入表和导出表,将信息储存在DataDump类中,最后以txt文件的形式输出一份文件的分析报告。

   程序的关键在于文件信息的“定位读取”上。文件头,可选文件头和节表在磁盘上是顺序存放的,跳过开始的PE标志段,就可以轻松找到上述几段,而且每一部分的确切大小都在它们的相关属性里描述了,在程序运行时可以知道的,读取信息的工作很容易就可以完成。而输入表和输出表的大小是不确定的,有的时候会存在没有输入表或没有输出表的情况,再加上输入输出表的出现位置也不固定,这会给读取输入输出表的工作带来一些困难。

   我们知道,一般在PE文件里某一项给的都是相对虚拟地址RVA,并不能直接和磁盘文件的物理地址相对应。在以内存映象为基础的方法中,只需要取得RVA,和ImageBase作简单的运算以后就可以定位到某一项数据在内存中的保存地址。而在以直接读取磁盘文件的方法里,必然要涉及到RVA到真实物理地址RAW的转换。所以对输入输出表的读取的关键转换到对输入输出表在磁盘文件上的定位了。

以下是一个通用的转换方法及示例:

+---------+---------+---------+---------+---------+---------+
|  段名称   虚拟地址  虚拟大小  物理地址  物理大小   标志      |
+---------+---------+---------+---------+---------+---------+
|  Name   &nbs