网络安全学习笔记(7)

具体的学习总结:
在这里插入图片描述
PE结构详解:
1、DOS头:共40H(64字节)
0X00 WORD e_magic; //※Magic DOS signature MZ(4Dh 5Ah):MZ标记:用于标记是否是可执行文件
0x3C DWORD e_lfanew; //※Offset to start of PE header:定位PE文件,PE头相对于文件的偏移量
2、PE头:
//NT头
//pNTHeader = dosHeader + dosHeader->e_lfanew;
struct _IMAGE_NT_HEADERS{
0x00 DWORD Signature; //PE文件标识:ASCII的"PE\0\0"
0x04 _IMAGE_FILE_HEADER FileHeader;//0x04是偏移量。标准PE头
0x18 _IMAGE_OPTIONAL_HEADER OptionalHeader;//0x18是偏移量,可变PE头
};
NT头的第一个成员是”PE\0\0”
//标准PE头:最基础的文件信息,共20字节

3、Section Table结构解析:
Section Table(节表)是记录PE文件中各个节的详细信息的集合,其每个成员是struct _IMAGE_SECTION_HEADER结构体,即节表是一个结构体数组来维护,属于线性结构。而节表的相对起始位置为:紧接着可选PE表。即:DOS头 + 中间空闲及垃圾数据 + NT头(三部分:4字节签名+标准PE头20字节+可选PE头)。
4、导出表、导入表
在这里插入图片描述
pDH获取DOS部分,首先判断是否为MZ,接下来判断PE,此时找到PE的位置pNtH,判断是否为PE。接下来是标准PE头pFH和扩展PE头pOH,再后面是节表,首先需要算节表的位置,
DWORD SectionHeaderOffset = (DWORD)pNtH + 24 + (DWORD)pFH->SizeOfOptionalHeader;
//节表位置的计算,4字节的PE头标志,20位的标准PE头,加上扩展PE头
找到每一个节表的位置,输出名字、虚拟地址、大小和偏移。
然后是导出表、导入表
首先编写了一个找到导入表导出表相对于内存的偏移的方法RVAOffset,获取的参数是PE头的位置和导出表的虚拟地址,找到节表的位置,然后用虚拟地址减去节表的地址,可以算出导出表对于节表的偏移,再加上节表在文件中的偏移,就可以算出所求导出表对与基地址的偏移。

#include"stdio.h"
#include"stdlib.h"
#include"windows.h"
//计算导入表导出表相对于内存的偏移
int File_IAT(PIMAGE_NT_HEADERS  pNtH,void* pFileAddress);
int File_INT(PIMAGE_NT_HEADERS  pNtH,void* pFileAddress);
unsigned int RVAOffset(PIMAGE_NT_HEADERS pNtHeader, unsigned int Rva)
{

	PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((unsigned int)pNtHeader + sizeof(IMAGE_NT_HEADERS));
	//算出节表的位置
		for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++)
	{
		unsigned int SectionBeginRva = pSectionHeader[i].VirtualAddress;//内存中的偏移地址

		unsigned int SectionEndRva = pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData;//内存中的偏移地址+节在文件中对齐后的大小
		if (Rva >= SectionBeginRva && Rva <= SectionEndRva)
		{
			unsigned int Temp = Rva - SectionBeginRva;
			unsigned int Rwa = Temp + pSectionHeader[i].PointerToRawData; //节区在文件中的偏移
			return Rwa;
		}
	}
}


int main()
{
void* pFileAddress=NULL;
char sFilePath[MAX_PATH];
PIMAGE_DOS_HEADER  pDH = NULL;//指向IMAGE_DOS结构的指针
PIMAGE_NT_HEADERS  pNtH = NULL;//指向IMAGE_NT结构的指针
PIMAGE_FILE_HEADER pFH = NULL;//指向IMAGE_FILE结构的指针
PIMAGE_OPTIONAL_HEADER pOH = NULL;//指向IMAGE_OPTIONALE结构的指针
long Length = 0;
//获取文件路径
printf("Please input the pe file path:\n");
scanf("%s",sFilePath);

//打开文件
FILE* pf = fopen(sFilePath,"rb");
if(pf == NULL)
{
	printf("func ReadFile() Error!\n");
}

//获取文件长度
fseek(pf, 0, SEEK_END);
Length = ftell(pf);
fseek(pf, 0, SEEK_SET);
if(Length == -1)
	printf("func GetFileLength() Error!\n");

//分配空间
pFileAddress = (void*)malloc(Length);
	if (pFileAddress == NULL)
		printf("func malloc() Error!\n");
	memset(pFileAddress, 0, Length);

	//读取文件进入内存
	fread(pFileAddress, Length, 1, pf);

	fclose(pf);

	/************************************************************************/
	/*							  PE头的判断                                 */
	/************************************************************************/
	if (!pFileAddress) //判断映像地址
	{
		printf("Not a valid PE file 1!\n");
		return 0;
	}
	printf("--------------------PEheader------------------------\n");
	pDH = (PIMAGE_DOS_HEADER)pFileAddress;
	if (pDH->e_magic!=IMAGE_DOS_SIGNATURE) //判断是否为MZ
	{
		printf("Not a valid PE file 2!\n");
		return 0;
	}
	pNtH = (PIMAGE_NT_HEADERS)((unsigned int)pDH + pDH->e_lfanew); //判断是否为PE格式
	if (pNtH->Signature!=IMAGE_NT_SIGNATURE)
	{
		printf("Not a valid PE file 3!\n");
		return 0;
	}
	printf("PE e_lfanew is: 0x%x\n", pNtH);

	/************************************************************************/
	/*							  FileHeader                                */
	/************************************************************************/
	pFH = &pNtH->FileHeader;//标准PE头
	printf("-----------------FileHeader------------------------\n");
	printf("NumberOfSections: %d\n", pFH->NumberOfSections);
	printf("SizeOfOptionalHeader: %d\n", pFH->SizeOfOptionalHeader);

	/************************************************************************/
	/*							  OptionalHeader                            */
	/************************************************************************/
	pOH = &pNtH->OptionalHeader;//扩展PE头
	printf("-----------------OptionalHeader---------------------\n");
	printf("SizeOfCode:0x%08x\n", pOH->SizeOfCode);//所有含有代码的区块的大小 编译器填入 没用(可改)
	printf("AddressOfEntryPoint: 0x%08X\n", pOH->AddressOfEntryPoint);//程序入口RVA
	printf("pFileAddress is 0x%x\n", pFileAddress);//内存镜像基址(程序默认载入基地址)
	printf("SectionAlignment: 0x%08x\n", pOH->SectionAlignment);//内存中对齐大小
	printf("FileAlignment: 0x%08x\n", pOH->FileAlignment);//文件中对齐大小(提高程序运行效率)
	printf("SizeOfImage: 0x%08x\n", pOH->SizeOfImage);//内存中整个PE文件的映射的尺寸,可比实际值大,必须是SectionAlignment的整数倍
	printf("SizeOfHeaders: 0x%08x\n", pOH->SizeOfHeaders);//所有的头加上节表文件对齐之后的值
	printf("NumberOfRvaAndSizes: 0x%08x\n", pOH->NumberOfRvaAndSizes);

	/************************************************************************/
	/*							  SectionTable                              */
	/************************************************************************/
	int SectionNumber = 0;
	unsigned int SectionHeaderOffset = (unsigned int)pNtH + 24 + (unsigned int)pFH->SizeOfOptionalHeader; //节表位置的计算
	//4字节的PE头标志,20位的标准PE头,加上扩展PE头
	printf("--------------------SectionTable---------------------\n");
	for (SectionNumber; SectionNumber < pFH->NumberOfSections;SectionNumber++)
	{
		PIMAGE_SECTION_HEADER pSh = (PIMAGE_SECTION_HEADER)(SectionHeaderOffset + 40 * SectionNumber);
		//40字节*节数
		printf("%d 's Name is %s\n", SectionNumber + 1, pSh->Name);//名字
		printf("VirtualAddress: 0x%08X\n", (unsigned int)pSh->VirtualAddress);//虚拟地址
		printf("SizeOfRawData: 0x%08X\n", (unsigned int)pSh->SizeOfRawData); //节在文件中对齐的尺寸
		printf("PointerToRawData: 0x%08X\n", (unsigned int)pSh->PointerToRawData);//节区在文件中的偏移
	}

	/************************************************************************/
	/*							  ImportTable                               */
	/************************************************************************/
	printf("--------------------ImportTable----------------------\n");

		printf("--------------------IAT----------------------\n");
		File_IAT(pNtH,pFileAddress);
		printf("--------------------INT----------------------\n");
		int i = File_INT(pNtH,pFileAddress);
		if(i ==0)
			File_IAT(pNtH,pFileAddress);
	system("pause");

}
int File_IAT(PIMAGE_NT_HEADERS  pNtH,void* pFileAddress)
{
		unsigned int dwImportOffset = RVAOffset(pNtH, pNtH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
		PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((unsigned int)pFileAddress + dwImportOffset);	
		
		 while(pImport->FirstThunk )
	{
		unsigned int* OriginalFirstThunk_INT = (unsigned int*)((unsigned int)pFileAddress + RVAOffset(pNtH, pImport->OriginalFirstThunk)); // RVA 指向 INT (PIMAGE_THUNK_DATA结构数组)
		unsigned int* FirstThunk_IAT = (unsigned int*)((unsigned int)pFileAddress + RVAOffset(pNtH, pImport->FirstThunk)); // RVA 指向 IAT (PIMAGE_THUNK_DATA结构数组)
		unsigned int dwName = (unsigned int)pFileAddress + RVAOffset(pNtH, pImport->Name);
		printf("---------Import File Name: %s\n", dwName);
		//循环输出IAT表
			while (*FirstThunk_IAT)
			{
				//	(5)进行判断,如果最高位为1则是按序号导入信息,去掉最高位就是函数序号,否则是名字导入
				if ((*FirstThunk_IAT) >> 31)	//最高位是1,序号导入
				{
					unsigned int Original = *FirstThunk_IAT << 1 >> 1;	//去除最高标志位。
					printf("按序号导入: %08Xh -- %08dd\n", Original, Original);	//16进制 -- 10 进制
				}
				else	//名字导入
				{
					//	(7)获取函数名
					unsigned int ImportNameAdd_RAV = *FirstThunk_IAT;
					unsigned int ImportNameAdd_FOA  = RVAOffset(pNtH, ImportNameAdd_RAV); 
					PIMAGE_IMPORT_BY_NAME ImportName = (PIMAGE_IMPORT_BY_NAME)((unsigned int)pFileAddress + ImportNameAdd_FOA);
					printf("按名字导入[HINT/NAME]: %02X--%s\n", ImportName->Hint, ImportName->Name);
				}
				FirstThunk_IAT++;
			}

		pImport++;
	}
		 return 1;
}

int File_INT(PIMAGE_NT_HEADERS  pNtH,void* pFileAddress)
{
		unsigned int dwImportOffset = RVAOffset(pNtH, pNtH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
		PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)((unsigned int)pFileAddress + dwImportOffset);	
		
		 while(pImport->OriginalFirstThunk )
	{
		unsigned int* OriginalFirstThunk_INT = (unsigned int*)((unsigned int)pFileAddress + RVAOffset(pNtH, pImport->OriginalFirstThunk)); // RVA 指向 INT (PIMAGE_THUNK_DATA结构数组)
		unsigned int dwName = (unsigned int)pFileAddress + RVAOffset(pNtH, pImport->Name);
		printf("---------Import File Name: %s\n", dwName);
		//循环输出IAT表
			while (*OriginalFirstThunk_INT)
			{
				//	(5)进行判断,如果最高位为1则是按序号导入信息,去掉最高位就是函数序号,否则是名字导入
				if ((*OriginalFirstThunk_INT) >> 31)	//最高位是1,序号导入
				{
					unsigned int Original = *OriginalFirstThunk_INT << 1 >> 1;	//去除最高标志位。
					printf("按序号导入: %08Xh -- %08dd\n", Original, Original);	//16进制 -- 10 进制
				}
				else	//名字导入
				{
					//	(7)获取函数名
					unsigned int ImportNameAdd_RAV = *OriginalFirstThunk_INT;
					unsigned int ImportNameAdd_FOA  = RVAOffset(pNtH, ImportNameAdd_RAV); 
					PIMAGE_IMPORT_BY_NAME ImportName = (PIMAGE_IMPORT_BY_NAME)((unsigned int)pFileAddress + ImportNameAdd_FOA);
					printf("按名字导入[HINT/NAME]: %02X--%s\n", ImportName->Hint, ImportName->Name);
				}
				OriginalFirstThunk_INT++;
			}

		pImport++;
	}
		 if ((unsigned int)pImport->OriginalFirstThunk == 0)
		{
			return 0;
		}
		 else
		 return 1;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值