首页
社区
课程
招聘
[原创]调试器实现_获取PE结构信息
发表于: 2009-9-10 21:49 6461

[原创]调试器实现_获取PE结构信息

2009-9-10 21:49
6461

实现调试器,本来可以直接用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了。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 8
支持
分享
最新回复 (4)
雪    币: 63
活跃值: (17)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
2
LZ 用面向对象, 总感觉有种 杀鸡用牛刀
2009-9-10 22:51
0
雪    币: 118
活跃值: (44)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
3
我觉得嘛,面向对象是一种思想,比喻成刀法应该比牛刀更好,所以哈,我不觉得有什么不妥。
2009-9-10 23:45
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
4
调试器啥时候出来?
免费不?
分析能力强不?
支持64bit不?
支持插件不?
UI友善不?
2009-9-11 10:13
0
雪    币: 267
活跃值: (24)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
5
LS的想法全面呀!
2009-9-11 14:37
0
游客
登录 | 注册 方可回帖
返回
//