作业要求:写一个遍历PE的c语言程序
最近正在看滴水逆向,这些都是我在看视频中的总结,可能有些错误,请大家指点。
基础知识:
Windows常见数据类型:
WORD: 16位无符号整形数据
DWORD: 32位无符号整型数据(DWORD32)
DWORD64: 64位无符号整型数据
INT: 32位有符号整型数据类型
INT_PTR: 指向INT数据类型的指针类型
INT32: 32位符号整型
INT64: 64位符号整型
UINT: 无符号INT
LONG: 32位符号整型(LONG32)
ULONG: 无符号LONG
LONGLONG: 64位符号整型(LONG64)
SHORT: 无符号短整型(16位)
LPARAM: 消息的L参数
WPARAM: 消息的W参数
HANDLE: 对象的句柄,最基本的句柄类型
HICON: 图标的句柄
HINSTANCE: 程序实例的句柄
HKEY: 注册表键的句柄
HMODULE: 模块的句柄
HWND: 窗口的句柄
LPSTR: 字符指针,也就是字符串变量
LPCSTR: 字符串常量
LPCTSTR: 根据环境配置,如果定义了UNICODE宏,则是LPCWSTR类型,否则则为LPCSTR类型
LPCWSTR: UNICODE字符串常量
LPDWORD: 指向DWORD类型数据的指针
CHAR: 8比特字节
TCHAR: 如果定义了UNICODE,则为WCHAR,否则为CHAR
UCHAR: 无符号CHAR
WCHAR: 16位Unicode字符
BOOL: 布尔型变量
BYTE: 字节类型(8位)
CONST: 常量
FLOAT: 浮点数据类型
SIZE_T: 表示内存大小,以字节为单位,其最大值是CPU最大寻址范围
VOID: 无类型,相当于标准C语言中的void
WINAPI: Windows API的函数调用方式,常见于SDK头文件中对API函数的声明中,相当于_stdcall(更严格地说,这不是数据类型,而是一种函数调用约定
命名规则:
基本数据类型包括:BYTE、CHAR、WORD、SHORT、INT等。
指针类型的命令方式一般是在其指向的数据类型前加“LP”或“P”,比如指向DWORD的指针类型为“LPDWORD”和“PDWORD”
各种句柄类型的命名方式一般都是在对象名前加“H”,比如位图(BITMAP)对应的句柄类型为“HBITMAP”。
无符号类型一般是以“U”开头,比如“INT”是符号类型,“UINT”是无符号类型
根据这些命名规律以及自己的经验看到一些没见过的数据类型也就能知道它的代表的意思
为什么要定义这些数据类型?
平台差异,例如__int64, long long, wchar_t, longptr_t这些类型,在不同的平台上可能定义的并不相同,为了解决这些问题,微软使用typedef关键字,为很多常用的C类型均定义了别名,这样一来,要解决源代码移植问题,只需在目标平台上定义相同的一套类型别名,即可解决大部分问题。
PIMAGE_DOS_HEADER
PIMAGE_NT_HEADERS
PIMAGE_FILE_HEADER
PIMAGE_OPTIONAL_HEADER32
PIMAGE_SECTION_HEADER
这些都是windowsPE头结构体指针
代码:
LPVOID ReadPEFile(LPSTR lpszFile) //LPVOID是一个没有类型的指针 //LPSTR",其相当于char*
{
FILE *pFile = NULL; //定义文件指针
DWORD fileSize = 0; //定义文件大小参数
LPVOID pFileBuffer = NULL; //定义堆栈的指针
//打开文件
pFile = fopen(lpszFile, "rb");
if(!pFile) //判断是否打开
{
printf(" 无法打开 EXE 文件! ");
return NULL;
}
//读取文件大小
fseek(pFile, 0, SEEK_END); //指针指到文件末尾
fileSize = ftell(pFile); //得到文件的大小
fseek(pFile, 0, SEEK_SET); //指针回到文件头
//分配缓冲区
pFileBuffer = malloc(fileSize); //分配堆区
if(!pFileBuffer) //判断是否分配成功
{
printf(" 分配空间失败! ");
fclose(pFile);
return 0;
}
//将文件数据读取到缓冲区
size_t n = fread(pFileBuffer, fileSize, 1, pFile); //写数据到堆栈区
if(!n) //判断是否写进去了
{
printf(" 读取数据失败! ");
free(pFileBuffer); //放飞指针
fclose(pFile); //关闭文件
return 0;
}
//关闭文件
fclose(pFile); //关闭文件
return pFileBuffer; //返回堆栈的指针
}
VOID PrintNTHeaders() //遍历PE头函数
{
LPVOID pFileBuffer = NULL; //定义堆的指针
PIMAGE_DOS_HEADER pDosHeader = NULL; //定义PE头结构体指针
PIMAGE_NT_HEADERS pNTHeader = NULL; //注意HEADERS的S!
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
pFileBuffer = ReadPEFile(FILEPATH); //返回堆栈的指针
if(!pFileBuffer) //判断是否成功
{
printf("文件读取失败\n");
return 0;
}
//判断是否是有效的MZ标志
if(*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) //先把pFileBuffer转换成PWORD类型的指针,然后再取值,判断是否是“MZ”
{
printf("不是有效的MZ标志\n");
free(pFileBuffer);
return 0 ;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; //把pFileBuffer转换成DOS头结构体指针类型
//打印DOC头
printf("********************DOC头********************\n");
printf("MZ标志:%x\n",pDosHeader->e_magic);
printf("PE偏移:%x\n",pDosHeader->e_lfanew);
//判断是否是有效的PE标志
if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志\n");
free(pFileBuffer);
return 0 ;
}
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew); //pFileBuffer加上NT头偏移,再强转成NT头结构体指针
//打印NT头
printf("********************NT头********************\n");
printf("NT:%x\n",pNTHeader->Signature);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); //因为signaturn占四个字节,所以加四
printf("********************PE头********************\n");
printf("PE:%x\n",pPEHeader->Machine);
printf("节的数量:%x\n",pPEHeader->NumberOfSections);
printf("SizeOfOptionalHeader:%x\n",pPEHeader->SizeOfOptionalHeader);
//可选PE头
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
//这里的IMAGE_SIZEOF_FILE_HEADER是二十个字节。
printf("********************OPTIOIN_PE头********************\n");
printf("OPTION_PE:%x\n",pOptionHeader->Magic);
printf("sizeofcode=%x\n",peOptionalHeader->SizeOfCode);
printf("baseofcode=%x\n",peOptionalHeader->BaseOfCode);
printf("baseofdata=%x\n",peOptionalHeader->BaseOfData);
printf("imagebase=%x\n",peOptionalHeader->ImageBase);
printf("sectionalignment=%x\n",peOptionalHeader->SectionAlignment);
printf("filealignment=%x\n",peOptionalHeader->FileAlignment);
printf("sizeofimage=%x\n",peOptionalHeader->SizeOfImage);
printf("sizeofheader=%x\n",peOptionalHeader->SizeOfHeaders);
printf("checksum=%x\n",peOptionalHeader->CheckSum);
//释放内存
free(pFileBuffer);
}
效果如下: