小E qq592646022
PE(Portable Excute)格式,是微软Win32环境可执行文件的标准格式,PE文件使用的是一个平面地址空间,所有代码和数据都合并在一起,组成一个很大的结构。主要有:
.text 是在编译或汇编结束时产生的一种块,它的内容全是指令代码
.rdata 是运行期只读数据
.data 是初始化的数据块
.idata 包含其它外来DLL的函数及数据信息,即输入表
.rsrc 包含模块的全部资源:如图标、菜单、位图等
PE文件结构(简化):
-----------------
│1,DOS MZ header│
-----------------
│2,DOS stub │
-----------------
│3,PE header │
-----------------
│4,Section table│
-----------------
│5,Section 1 │
-----------------
│6,Section 2 │
-----------------
│6 Section ... │
-----------------
│n,Section n │
-----------------
这些都可以在winnt.h头文件中查看到具体的结构体类型。我就不多说了,我们主要是通过编程来加深对pe格式的了解:
一、检测pe的有效性
主要通过检查dos头的MZ header和nt头也就是pe header的DWORD Signature是否位固定的值,分别为0x5A4D、0x00004550(用十六进制打开可以看到这个ascii为PE00),呵呵,我们用宏定义来替代这两个固定值。
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 //这个下面会提到
typedef struct IMAGE_DOS_HEADER //dos头的结构体定义
{
WORD e_magic; //这个就是要检测的位置是否为0x5A4D
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
DWORD e_lfanew; //这个比较重要,指向下一个头开始的位置,也就是pe真正的开始,也就是nt头(nt_header)的开始
}IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
typedef struct IMAGE_NT_HEADERS //nt头开始包括两个头(File头和optional头
{
DWORD Signature; //这个就是要检测的位置是否为0x00004550
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
}IMAGE_NT_HEADERS,*PIMAGE_NT_HEADERS;
typedef struct IMAGE_FILE_HEADER //文件头
{
WORD Machine;
WORD NumberOfSections; //节的数量
DWORD TimeDateStamp;
DWORD PointerToSymbols;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
}IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;
typedef struct IMAGE_OPTIONAL_HEADER32 //optional头,看到这么长是不是吓坏了,呵呵,其实,有的没怎么用的,都是一些类型值。比较重要的地方我标注了。
{
WORD Magic; //幻数,听了很迷惑,呵呵,没事继续
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUnInitializedData;
DWORD AddressOfEntryPoint; //程序入口地址
DWORD BaseOfCode; //代码基址
DWORD BaseOfData; //数据基址
DWORD ImgaeBase; //镜像基址
DWORD SectionAlignment; //节对齐度也好像叫粒度
DWORD FileAlignment; //文件对齐度
WORD MajorOperatingSystemVersion;
WORD MinorOperatingsystemversion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsybtemVersion;
WORD MinorSubsybtemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage; //镜像大小
DWORD SizeoOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlages;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //这个是DataDirectory结构体数组IMAGE_NUMBEROF_DIRECTORY_ENTRIES为宏定义值为 16
}IMAGE_OPTIONAL_HEADER32,*PIMAGE_OPTIONAL_HEADER32;
好了,我们可以开始写检测pe的有效性了,什么?你还以为还需要些什么,其实不用了,这些够我们检测pe有效性了,呵呵,开始吧。
写个主函数,会写吧,不要告诉我这个你都不会。
void main(int argc,char* argv[])
{
IMAGE_DOS_HEADER dosheader; //定义dos头结构体变量
IMAGE_NT_HEADERS ntheader; //一样,只不过是放nt头的变量
FILE* p;
DWORD Sig; / /这个放前面提到的nt头的DWORD Signature;
//printf("%d %s",argc,argv[1]);
//getchar();
if(argc>1)
{
p=fopen(argv[1],"rb");
if(p!=NULL)
{
fread(&dosheader,sizeof(dosheader),1,p); //读头放到dosheader结构体变量
if(dosheader.e_magic==IMAGE_DOS_SIGNATURE) //先检测dos头是否有效
{
PrintDosHeader(dosheader);
fseek(p,dosheader.e_lfanew,SEEK_SET); //定位到pe(nt头)头开始
fread(&Sig,sizeof(Sig),1,p); //读取Signature值
if(Sig==IMAGE_NT_SIGNATURE) //检测是否有效
printf("是pe文件\n\n"); //有效是pe文件
}
else
printf("无效文件\n");
fclose(p);
}
else printf("Erro open file\n");
}
else
{
printf("参数不正确\n");
}
}
好了,编译成功,在cmd输入 pe xxx.exe 回车看到没呵呵,pe是这个编译后的程序,xxx.exe是要检测的文件。其实也可以输出其它dos头的信息。写个函数就好了。
void PrintDosHeader(IMAGE_DOS_HEADER dosheader)
{
printf("IMAGE_DOS_HEADER dump:\n");
printf("e_magic :%04x\n",dosheader.e_magic);
printf("e_cblp : %04x\n",dosheader.e_cblp);
printf("e_cp : %04x\n",dosheader.e_cp);
printf("e_crlc : %04x\n",dosheader.e_crlc);
printf("e_cparhdr : %04x\n",dosheader.e_cparhdr);
printf("e_minalloc: %04x\n",dosheader.e_minalloc);
printf("e_maxalloc: %04x\n",dosheader.e_maxalloc);
printf("e_ss : %04x\n",dosheader.e_ss);
printf("e_sp : %04x\n",dosheader.e_sp);
printf("e_csum : %04x\n",dosheader.e_csum);
printf("e_ip : %04x\n",dosheader.e_ip);
printf("e_cs : %04x\n",dosheader.e_cs);
printf("e_lfarlc : %04x\n",dosheader.e_lfarlc);
printf("e_ovno : %04x\n",dosheader.e_ovno);
printf("e_res[0] : %04x\n",dosheader.e_res[0]);
printf("e_oemid : %04x\n",dosheader.e_oemid);
printf("e_oeminfo : %04x\n",dosheader.e_oeminfo);
printf("res2[0] : %04x\n",dosheader.e_res2[0]);
printf("lfanew : %08x\n",dosheader.e_lfanew);
}