(代码略搓,大神请飘走~~~)
cutePE基于Qt5.5开发,可以查看PE文件结构。开发过程如下:
0x01 读取文件
要【通过signature判断是否为PE文件】其实是件简单的事,but,首先得需要读取文件呀。所以第一步,实现Qt读取文件,以Hex形式打印:
#include <QCoreApplication>
#include <QFile>
#include <QDebug>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QByteArray raw_bytes;
QFile file("E:/myCode/binary_file_rw/notepad.exe");
if(file.open(QIODevice::ReadOnly))
{
raw_bytes = file.readAll();
string byte_string;
for(int i=0;i<raw_bytes.count();i++){
byte_string=QString::number(raw_bytes[i]&0x000000000000ff, 16).toUpper().toStdString();
if(byte_string.length()==1){
byte_string="0"+byte_string;
}
if(!(i%4) && (i%16))
cout<<"\t";
if(!(i%16))
cout<<"\n";
cout<<byte_string<<" ";
}
file.close();
}
return a.exec();
}
效果如图:
0x02 识别PE文件
不管是变形还是没变形的PE,起始两个byte肯定是 “MZ”,DOS Header结尾的4个byte肯定指向PE Header处,而PE Header起始两个byte肯定是 “PE\0\0”。以此可判断PE文件。
这里对之前的代码做了一丢丢改进,把读取到的byte_string通通存储到全局变量raw_bytes_string里,方便后续处理。bytes_string是一个以string为元素的容器。
同时增加了两个函数,便于对数据进行处理。
#include <QCoreApplication>
#include <QFile>
#include <QDebug>
#include <iostream>
#include <vector>
using namespace std;
vector <string> raw_bytes_string;
//获取raw_bytes_string中addr起始的bytes_length字节。is_reversed指定是否逆序输出字节
string getBytesFromRAW(long long addr,long long bytes_length,bool is_reversed){
string bytes_string;
if(is_reversed){//小端机逆序输出地址,方便后续操作
for(int i=addr+bytes_length-1;i>addr-1;i--){
bytes_string.append(raw_bytes_string[i]);
}
}else{
for(int i=addr;i<addr+bytes_length;i++){
bytes_string.append(raw_bytes_string[i]);
}
}
return bytes_string;
}
long long hexString2LongLong(string& hexStr)
{
hexStr=hexStr.erase(0,hexStr.find_first_not_of("0"));//去掉000000E0前面的000000
return strtoll(hexStr.c_str(),NULL,16);
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QByteArray raw_bytes;
QFile file("C:/Windows/system32/notepad.exe");
if(file.open(QIODevice::ReadOnly))
{
raw_bytes = file.readAll();
string byte_string;
for(int i=0;i<raw_bytes.count();i++){
byte_string=QString::number(raw_bytes[i]&0x000000000000ff, 16).toUpper().toStdString();
if(byte_string.length()==1){
byte_string="0"+byte_string;
}
raw_bytes_string.push_back(byte_string);
//打印整个文件
// if(!(i%4) && (i%16))
// cout<<"\t";
// if(!(i%16))
// cout<<"\n";
// cout<<byte_string<<" ";
}
file.close();
//判断是否为PE文件
if(raw_bytes_string[0]=="4D" && raw_bytes_string[1]=="5A"){//MZ
string pe_header_offset_string=getBytesFromRAW(0x3c,4,true);
long long pe_header_offset=hexString2LongLong(pe_header_offset_string);
string pe_header_first_4_bytes=getBytesFromRAW(pe_header_offset,4,false);
if(pe_header_first_4_bytes=="50450000"){//PE\0\0
cout<<"it is a PE file!\n";
}else{
cout<<"Ops,it is NOT a PE file!\n";
}
}else{
cout<<"Ops,it is NOT a PE file!\n";
}
}
return a.exec();
}
把file路径替换成任何PE文件,exe或者dll,执行效果都是:
0x03 读取 & 显示PE文件结构
cutePE里,界面左的内容都是固定的,界面右的内容根据实际PE文件内容变化。涉及到的主要的Qt控件是QTreeWidget(对应图左)、QTableWidget(对应图右)。
总的来说,实现过程有点像写八股文,照着PE文件结构来写就好了~当然具体的SectionTable定位等问题需要自己根据实际的SizeOfOptionlHeader和PE Header offset进行计算,不能照着一般PE文件来写,具体定位还得自己计算,毕竟万一是个变形PE呢。实现效果如下图:
代码放在这里了,其实程序还有很多可以优化的地方。现在程序对于PE文件的处理性能不是很理想,随便碰到一个稍微大点的PE就会崩溃。因为我在PE文件初始加载过程中(加载过程有好几道处理工序),把PE文件所有字节都赋给了栈上的变量。。。所以,你懂的……我比较懒,基本功能实现后,也没再进行优化。界面嘛,,本人审美有限,也没怎么调。