欢迎来到 嗅灵易学

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

调试器实现_获取PE结构信息

调试器实现_获取PE结构信息

实现调试器,本来可以直接用fopen或者文件流读取文件信息,检测是否是正常的PE文件,再获取入口地址等部分信息就可以了。但是,为了让大家在用调试器的时候不要开一个PE_Infomation之类的软件,所以,我在调试器里面完全获取所有pe信息。
实现方法比较简单,我就大概说下:
由于完全面向对象,所以,为了体现面向对象的优势,我做了如下处理:
先定义一个抽象类(做用户访问的接口)

class PE_interface{
public:
  virtual PIMAGE_DOS_HEADER GetDosHeader()=0;
  virtual PIMAGE_NT_HEADERS GetNtHeaders()=0;
  virtual PIMAGE_SECTION_HEADER GetSHeader()=0;
  virtual void GetDataDir_Ex_Im(PIMAGE_IMPORT_DESCRIPTOR& pIm,PIMAGE_EXPORT_DIRECTORY& pEx)=0;
};

然后定义一个类,实现PE信息获取的功能。
class PE_Implement{
public:

  PE_Implement(const char *filename);
  struct stMapFile{
    stMapFile(){hFile=NULL;hMapping=NULL;ImageBase=NULL;}
    HANDLE hFile;
    HANDLE hMapping;
    LPVOID ImageBase;
  };
  struct IM_TABLE{
  DWORD address;
  std::string function;
  };
  bool LoadFile();
  void UnLoadFile();
  bool IsPEFile();
  LPVOID GetImageBase();
  PIMAGE_DOS_HEADER GetDosHeader_I();
  PIMAGE_NT_HEADERS GetNtHeaders_I();
  PIMAGE_OPTIONAL_HEADER GetOpHeader_I();
  PIMAGE_FILE_HEADER GetFHeader_I();
  PIMAGE_SECTION_HEADER GetSHeader_I();
  LPVOID GetDataDirEx_I();
  LPVOID GetDataDirIm_I();
  IM_TABLE *GetImportTable();

private:
    stMapFile MapFile;
    char szFilePath[MAX_PATH];
    IM_TABLE ImportTable[200];
};
而后定义一个user类,多重继承于上面两个类。

class USER:public PE_Implement,public PE_interface{
  
public:
  USER(const char *FN=NULL);
  ~USER();
  PIMAGE_DOS_HEADER GetDosHeader();
  PIMAGE_NT_HEADERS GetNtHeaders();
  PIMAGE_SECTION_HEADER GetSHeader();
  void GetDataDir_Ex_Im(PIMAGE_IMPORT_DESCRIPTOR& pIm,PIMAGE_EXPORT_DIRECTORY& pEx);
private:
      char FilePath[MAX_PATH];
};
这样就可以直接用PE_interface *p=new USER。来对调用功能函数,但是,被调用的函数只能是接口抽象类PE_interface里面声明了的函数。实现与接口分开,减少模块之间的依赖性。为了避免命名空间的泛滥,我都用的std::string形式限定命名空间。
USER类是功能的实现,界面显示是另外的一个类实现的。由于所有的显示都是控制台,所有,也就没有想把代码发出来,等图形化后,一定分享代码。

写的过程中出现的问题:(希望对其他写PE分析软件的朋友有所帮助)
1,获取PE信息,可以直接镜像到内存或者打开文件直接操作,但是,所以,需要对RVA,VA,OFFSET这些不同状态下的偏移地址清楚划分。一般情况下,.text,,.rdata,.data段之间SizeOfRawData文件对齐大小就是内存里面占用的空间大小,所以他们之间的内存偏移之差就是SizeOfRawData,但是,一定不要被这个误导了,这个只是巧合(很多时候有这个巧合),不是真正的意义所在,在计算的时候,不要利用这些巧合。
2,如果文件里面,指令占用了0x300个字节,但是对齐大小后是0x400字节,加载到内存后是0x1000个字节,在反汇编的时候,如何辨别。这个IDA做的相当的好,我也仅仅实现后两个之间的辨别,所以,也就不好意思多说了,大家仔细看看pe结构的分析吧。
3,获取输入表信息确实是比较复杂的问题,如果你全部自己写代码的话。个人觉得麻烦的主要是关于OriginalFirstThunk和FirstThunk以及他们指向的结构IMAGE_IMPORT_BY_NAME,
由于编译器不同,本来改指向一个IMAGE_THUNK_DATA结构体数组的OriginalFirstThunk可能被设为0,这是,只有通过查找输入函数的信息。但是如果FirstThunk指向的IMAGE_THUNK_DATA的最高进位是1,那么函数是序号引入的,就应该从低位提取序号。如果不为1,就是应该读取指向的IMAGE_IMPORT_BY_NAME结构体。为了兼容不同系统,需要对各种情况进行解析,我只做了解析,没有做解析后的处理(精力有限哈)

其他还有许多小问题,上网搜索就okay了。

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

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

0 0 0 举报
复制成功