PE文件-分析vc示范所有代码[包含EXPORT TABLE]

 纯属个人研究,请勿见笑微笑

#include <windows.h>

#include <iostream>

#define PE_FILE_NAME TEXT("C:\\WINDOWS\\twain_32.dll")
#define CREATE_FILE_FAILURE "创建文件失败"
#define CREATE_MAPPING_FILE "创建文件映射对象失败"
#define MAP_VIEW_FAILURE "映射文件到调用进程的地址空间失败"
#define VALID_DOS_SIGNATURE "这个文件的DOS签名是有效的"
#define VALID_PE_SIGNATURE "这个文件的PE签名是有效的"
#define VALID_PE_FILE "这是一个有效的PE文件"
#define INVALID_PE_FILE "这是一个无效的PE文件"

#define WRITE_LINE(msg) std::cout << TEXT(msg) << std::endl;
#define WRITE_LINE_EX(msg1,msg2) std::cout << TEXT(msg1) << TEXT(msg2) << std::endl;
#define WRITE(msg) std::cout << TEXT(msg);
#define PROCESS_FAILURE(msg) WRITE_LINE(msg)\
		return FALSE;

#define PE_PARSE_NT_HEADER_CALLBACK void (*PARSE_NT_HEADER_CALLBACK)(IMAGE_NT_HEADERS*)=NULL
#define PE_PARSE_SECTION_HEADER_CALLBACK void (*PARSE_SECTION_HEADER_CALLBACK)(IMAGE_SECTION_HEADER*,INT)=NULL
#define PE_PARSE_IMPORT_TABLE_CALLBACK void (*PARSE_IMPORT_TABLE_CALLBACK)(IMAGE_DATA_DIRECTORY*,PVOID)

/* 根据文件头获得文件节表头 */
#define GET_IMAGE_SECTION_HEADER(lpNtHeader) ((IMAGE_SECTION_HEADER *)((byte*)lpNtHeader+sizeof(*lpNtHeader)))
/* 根据文件头获得文件节数量 */
#define GET_IMAGE_NUMBER_OF_SECTIONS(lpNtHeader) (lpNtHeader->FileHeader.NumberOfSections)
/* 虚拟地址到文件偏移量 */
#define RVA_TO_OFFSET(pImageBase,rva) ((PVOID)((byte*)pImageBase+rva))


/* 映射文件到内存映像 */
PVOID MapFileToView(LPCSTR filename);
/* 获得PE文件头 */
IMAGE_NT_HEADERS* GetPeHeader(LPVOID pMapping);
/* 验证PE合法性函数 */
BOOL Validate(LPVOID pMapping);
/* 分析PE头函数 */
void PARSE_NT_HEADER_CALLBACK(IMAGE_NT_HEADERS* lpNtHeader);
/* 分析PE节表函数 */
void PARSE_SECTION_HEADER_CALLBACK(IMAGE_SECTION_HEADER* lpSectionHeader,INT numberOfSections);
/* 分析引入表 */
void PARSE_IMPORT_TABLE_CALLBACK(IMAGE_DATA_DIRECTORY* lpImageDataDirectory,PVOID pImageBase);
/* 将相对虚拟地址转换为文件偏移地址 */
DWORD RVAToFileOffset(PVOID,DWORD);
/* 显示DLL的所有引入函数 */
void PARSE_IMPORT_TABLE_FUNCTION_CALLBACK(PVOID,IMAGE_THUNK_DATA*);
/* 分析导出表 */
void PARSE_EXPORT_TABLE_CALLBACK(IMAGE_DATA_DIRECTORY* lpImageDataDirectory,PVOID pImageBase);


void main()
{
	std::cout.setf(std::ios::hex,std::ios::basefield);//设置输出格式为16进制
	

	//将文件映射到内存
	PVOID pMapping = MapFileToView(PE_FILE_NAME);
	//获取文件头
	IMAGE_NT_HEADERS* lpImageNtHeader = GetPeHeader(pMapping);
	
	if (lpImageNtHeader)
	{
		//显示文件头信息
		PARSE_NT_HEADER_CALLBACK(lpImageNtHeader);
		//显示文件节头信息
		PARSE_SECTION_HEADER_CALLBACK(GET_IMAGE_SECTION_HEADER(lpImageNtHeader),GET_IMAGE_NUMBER_OF_SECTIONS(lpImageNtHeader));
		//分析导入表
		PARSE_IMPORT_TABLE_CALLBACK(&(lpImageNtHeader->OptionalHeader.DataDirectory[1]),pMapping);	
		//分析导出表
		PARSE_EXPORT_TABLE_CALLBACK(&(lpImageNtHeader->OptionalHeader.DataDirectory[0]),pMapping);
	}
	
	//取消映射
	UnmapViewOfFile(pMapping);
	pMapping = NULL;

}

/* 映射文件到内存映像 */
PVOID MapFileToView(LPCSTR filename)
{
	HANDLE fHandle = ::CreateFile(filename,
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL
		);
	if (fHandle==INVALID_HANDLE_VALUE)
	{		
		PROCESS_FAILURE(CREATE_FILE_FAILURE);		
	}
	
	HANDLE hMapping = ::CreateFileMapping(
		fHandle,
		NULL,
		PAGE_READONLY,
		NULL,
		NULL,
		NULL
		);
	if (hMapping==NULL)
	{
		CloseHandle(fHandle);
		PROCESS_FAILURE(CREATE_MAPPING_FILE);
	}
	
	LPVOID pMapping = ::MapViewOfFile(hMapping,FILE_MAP_READ,NULL,NULL,NULL);
	if (pMapping==NULL)
	{
		
		PROCESS_FAILURE(MAP_VIEW_FAILURE);
	}
	CloseHandle(hMapping);
	CloseHandle(fHandle);
	hMapping = NULL;
	fHandle = NULL;
	return pMapping;
}

/* 验证PE入口函数 */
BOOL Validate(LPVOID pMapping)
{

	//1.validate IMAGE_DOS_HEADER
	IMAGE_DOS_HEADER * dosHeader = (IMAGE_DOS_HEADER*)pMapping;
// 	if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE)
// 	{
// 		WRITE_LINE(VALID_DOS_SIGNATURE);
// 	}
	
	IMAGE_NT_HEADERS * nt_header=(IMAGE_NT_HEADERS*)((byte*)pMapping+dosHeader->e_lfanew);
// 	if (nt_header->Signature == IMAGE_NT_SIGNATURE)
// 	{
// 		WRITE_LINE(VALID_PE_SIGNATURE);
// 	}
	
	
// 	WRITE_LINE(
// 		(
// 		dosHeader->e_magic == IMAGE_DOS_SIGNATURE && nt_header->Signature == IMAGE_NT_SIGNATURE?
// 		TEXT(VALID_PE_FILE):
// 	TEXT(INVALID_PE_FILE)
// 		)
// 		);	
	
	return dosHeader->e_magic == IMAGE_DOS_SIGNATURE && nt_header->Signature == IMAGE_NT_SIGNATURE;
}

IMAGE_NT_HEADERS* GetPeHeader(LPVOID pMapping)
{
	IMAGE_DOS_HEADER * dosHeader = (IMAGE_DOS_HEADER*)pMapping;
	IMAGE_NT_HEADERS * nt_header=(IMAGE_NT_HEADERS*)((byte*)pMapping+dosHeader->e_lfanew);
	//检查PE有效性
	if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE && nt_header->Signature == IMAGE_NT_SIGNATURE)//如果PE头有效则返回PE头
	{
		return nt_header;
	}
	//PE头无效则取消内存映射释放资源并返回NULL
	UnmapViewOfFile(pMapping);
	pMapping = NULL;
	return NULL;
}

/* 分析NT_HEADER回调函数 */
void PARSE_NT_HEADER_CALLBACK(IMAGE_NT_HEADERS* lpNtHeader)
{
	WRITE_LINE(TEXT("----------------------------------FILE HEADER------------------------------------------------"));
	WRITE_LINE((lpNtHeader->FileHeader.Machine==IMAGE_FILE_MACHINE_I386?TEXT("该程序运行所在机器:Intel 386"):TEXT("该程序运行所在机器:非Intel 386")));
	WRITE(TEXT("该程序的块数量:"));
	WRITE_LINE(lpNtHeader->FileHeader.NumberOfSections);
	WRITE_LINE(TEXT("----------------------------------FILE OPTIONAL HEADER--------------------------------------"));
	//std::cout.setf(std::ios::hex,std::ios::basefield);//设置输出格式为16进制
	std::cout << TEXT("PE装载器准备运行的PE文件的第一个指令的RVA:") << lpNtHeader->OptionalHeader.AddressOfEntryPoint << std::endl;
	std::cout << TEXT("PE文件的优先装载地址:") << lpNtHeader->OptionalHeader.ImageBase << std::endl;
	std::cout << TEXT("内存中节对齐的粒度:") << lpNtHeader->OptionalHeader.SectionAlignment << std::endl;
	std::cout << TEXT("文件中节对齐的粒度:") << lpNtHeader->OptionalHeader.FileAlignment << std::endl;
	std::cout << TEXT("win32子系统版本[若PE文件是专门为Win32设计的,该子系统版本必定是4.0否则对话框不会有3维立体感]:") << lpNtHeader->OptionalHeader.MajorOperatingSystemVersion << "." << lpNtHeader->OptionalHeader.MinorOperatingSystemVersion << std::endl;
	std::cout << TEXT("内存中整个PE映像体的尺寸[它是所有头和节经过节对齐处理后的大小]:") << lpNtHeader->OptionalHeader.SizeOfImage << std::endl;
	std::cout << TEXT("所有头+节表的大小[也就等于文件尺寸减去文件中所有节的尺寸。可以以此值作为PE文件第一节的文件偏移量]:") << lpNtHeader->OptionalHeader.SizeOfHeaders << std::endl;
	std::cout << TEXT("PE文件属于子系统:") << (lpNtHeader->OptionalHeader.Subsystem==IMAGE_SUBSYSTEM_WINDOWS_GUI?TEXT("图形用户界面"):TEXT("字符界面")) << std::endl;
	WRITE_LINE(TEXT("----------------------------------FILE SECTION TABLE--------------------------------------"));
	//定位节表位置
	IMAGE_SECTION_HEADER *lpSectionHeader = (IMAGE_SECTION_HEADER *)((byte*)lpNtHeader+sizeof(*lpNtHeader));
//	PARSE_SECTION_HEADER_CALLBACK(lpSectionHeader,lpNtHeader->FileHeader.NumberOfSections);
	
	
// 	for (int i=0;i<lpNtHeader->FileHeader.NumberOfSections;i++)
// 	{		
// 		std::cout << lpSectionHeader->Name << "\0" << std::endl;
// 		std::cout << TEXT("\t本节的RVA(相对虚拟地址):") << lpSectionHeader->VirtualAddress << std::endl;
// 		std::cout << TEXT("\t经过文件对齐处理后节尺寸:") << lpSectionHeader->SizeOfRawData << std::endl;
// 		std::cout << TEXT("\t本节基于文件的偏移量:") << lpSectionHeader->PointerToRawData << std::endl;
// 		lpSectionHeader++;
// 	}

}





/* 分析IMAGE_SECTION_HEADER回调函数 */
void PARSE_SECTION_HEADER_CALLBACK(IMAGE_SECTION_HEADER* lpSectionHeader,INT numberOfSections)
{
	if (lpSectionHeader && numberOfSections)
	{
		for (int i=0;i<numberOfSections;i++)
		{		
			std::cout << lpSectionHeader->Name << "\0" << std::endl;
			std::cout << TEXT("\t本节的RVA(相对虚拟地址):") << lpSectionHeader->VirtualAddress << std::endl;
			std::cout << TEXT("\t经过文件对齐处理后节尺寸:") << lpSectionHeader->SizeOfRawData << std::endl;
			std::cout << TEXT("\t本节基于文件的偏移量:") << lpSectionHeader->PointerToRawData << std::endl;
			lpSectionHeader++;
			
		}
	}
}



/* 分析PE导入表函数 

IMAGE_DATA_DIRECTORY.VirtualAddress												-->
IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk/IMAGE_IMPORT_DESCRIPTOR.FirstThunk	-->//IMAGE_IMPORT_DESCRIPTOR 数组以一个全0域元素结尾
IMAGE_THUNK_DATA																-->
IMAGE_IMPORT_BY_NAME.Name
*/
void PARSE_IMPORT_TABLE_CALLBACK(IMAGE_DATA_DIRECTORY* lpImageDataDirectory,PVOID pImageBase)
{
	if (!lpImageDataDirectory)
		return;
	
	WRITE_LINE(TEXT("----------------------------------PARSE IMPORT TABLE----------------------------------------"));
	int dllcounter = 0;
	IMAGE_IMPORT_DESCRIPTOR* lpImageImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)(RVAToFileOffset(pImageBase,lpImageDataDirectory->VirtualAddress));
	while (lpImageImportDescriptor->Name!=NULL)//如果不是空元素
	{
		dllcounter++;
		WRITE_LINE((char*)(RVAToFileOffset(pImageBase,lpImageImportDescriptor->Name)));//输出当前的DLL名字
		DWORD thunk = (lpImageImportDescriptor->OriginalFirstThunk==NULL?lpImageImportDescriptor->FirstThunk:lpImageImportDescriptor->OriginalFirstThunk);
		PARSE_IMPORT_TABLE_FUNCTION_CALLBACK(pImageBase,(IMAGE_THUNK_DATA *)(RVAToFileOffset(pImageBase,thunk)));
		WRITE_LINE(TEXT("-------------------------------------------"));
		lpImageImportDescriptor++;
	}
	WRITE_LINE_EX(TEXT("导出DLL数目:"),TEXT(dllcounter));
}

/* 分析PE引出表函数 */
void PARSE_EXPORT_TABLE_CALLBACK(IMAGE_DATA_DIRECTORY* lpImageDataDirectory,PVOID pImageBase)
{
	if(!lpImageDataDirectory || !(lpImageDataDirectory->VirtualAddress))
		return;
	
	WRITE_LINE(TEXT("\n\n---------------------PARSE EXPORT TABLE-----------------------"));
	IMAGE_EXPORT_DIRECTORY* lpImageExportDirectory =  (IMAGE_EXPORT_DIRECTORY*)(RVAToFileOffset(pImageBase,lpImageDataDirectory->VirtualAddress));
	if (!lpImageExportDirectory->NumberOfNames)//判断有无命名函数导出
		return;
	WRITE_LINE("the information of current module:");
	std::cout	<< TEXT("\tName:") << (char*)RVAToFileOffset(pImageBase,lpImageExportDirectory->Name) << TEXT("\t")
				<< TEXT("\n\tBase:") << lpImageExportDirectory->Base << TEXT("\t")
				<< TEXT("\n\tNumberOfFunctions:") << lpImageExportDirectory->NumberOfFunctions << TEXT("\t")
				<< TEXT("\n\tNumberOfNames:") << lpImageExportDirectory->NumberOfNames << TEXT("\t")
				<< TEXT("\n\tAddressOfFunctions:") << lpImageExportDirectory->AddressOfFunctions << TEXT("\t")
				<< TEXT("\n\tAddressOfNames:") << lpImageExportDirectory->AddressOfNames << TEXT("\t")
				<< TEXT("\n\tAddressOfNameOrdinals:") << lpImageExportDirectory->AddressOfNameOrdinals << TEXT("\t")
				<< std::endl;
	WRITE_LINE("\n\nthe functions information of current module:");

	int numberOfNames = lpImageExportDirectory->NumberOfNames;

	 DWORD* pAddressOfNames = (DWORD*)(RVAToFileOffset(pImageBase,lpImageExportDirectory->AddressOfNames));
	 WORD* pAddressOfNameOrdinals = (WORD*)(RVAToFileOffset(pImageBase,lpImageExportDirectory->AddressOfNameOrdinals));
	 DWORD* pAddressOfFunctions = (DWORD*)(RVAToFileOffset(pImageBase,lpImageExportDirectory->AddressOfFunctions));
	 WRITE_LINE("\tOrdinal\tHint\tFunction\t\t\tEntryPoint\n");
	 while (numberOfNames-- > 0)
	 {		
		//得到函数名称
		 char* functionName = (char*)(RVAToFileOffset(pImageBase,*pAddressOfNames));
		//得到函数索引
		 WORD index = *pAddressOfNameOrdinals;//AddressOfNameOrdinals RVA,指向包含AddressOfNames数组中相关函数之序数的16位数组 
		 //得到函数Ordinal
		 int ordinal = lpImageExportDirectory->Base+index;

		 //得到函数地址
		 DWORD* pFunctionEntryPoint = pAddressOfFunctions+index;
		
		
		std::cout	<< TEXT("\t")
					<< ordinal
					<< TEXT("\t")
					<< index 
					<< TEXT("\t")
					<< functionName
					<< TEXT("\t\t\t")
					<< *pFunctionEntryPoint
					<<std::endl;

		pAddressOfNames++;	
		pAddressOfNameOrdinals++;
	 }
}

void PARSE_IMPORT_TABLE_FUNCTION_CALLBACK(PVOID pImageBase,IMAGE_THUNK_DATA *lpImageThunkData)
{
	
	WRITE_LINE(TEXT("\t\tHint\t\tFunction"));
	while (lpImageThunkData->u1.Ordinal!=NULL)
	{
		//对于每个数组元素,我们比对元素值是否等于IMAGE_ORDINAL_FLAG32。
		//如果该元素值的最高二进位为1,那么函数是由序数引入的,可以从该值的低字节提取序数。 
		//如果元素值的最高二进位为0,就可将该值作为RVA转入 IMAGE_IMPORT_BY_NAME 数组,跳过 Hint 就是函数名字了
		if(lpImageThunkData->u1.Ordinal & IMAGE_ORDINAL_FLAG32)//如果该元素值的最高二进位为1,那么函数是由序数引入的
		{
			WORD ordinal = (lpImageThunkData->u1.Ordinal & 0xFFFF);//从该值的低字节提取序数
			WRITE_LINE_EX(TEXT("\t\t"),ordinal);//输出该函数编号
		}
		else
		{
			IMAGE_IMPORT_BY_NAME* lpImageImportByName = (IMAGE_IMPORT_BY_NAME*)(RVAToFileOffset(pImageBase,lpImageThunkData->u1.Ordinal));
			WRITE(TEXT("\t\t0x"));
			WRITE(lpImageImportByName->Hint);
			WRITE(TEXT("\t\t"));
			WRITE_LINE((char*)(lpImageImportByName->Name));
			
		}
		lpImageThunkData++;
	}
	
}


/* 将相对虚拟地址转换为文件偏移地址 */
DWORD RVAToFileOffset(PVOID pMappping,DWORD rva)
{
	//定位到DOS头
	IMAGE_DOS_HEADER* lpImageDosHeader = (IMAGE_DOS_HEADER*)pMappping;
	//定位到PE头
	IMAGE_NT_HEADERS* lpImageNtHeader = (IMAGE_NT_HEADERS*)((byte*)pMappping+lpImageDosHeader->e_lfanew);
	//定位到节表
	IMAGE_SECTION_HEADER* lpSectionTable = (IMAGE_SECTION_HEADER*)((byte*)lpImageNtHeader+sizeof(IMAGE_NT_HEADERS));
	//用节数量作循环次数
	int i = lpImageNtHeader->FileHeader.NumberOfSections;
	while(i>0)//检查所有块
	{
		if (rva>=lpSectionTable->VirtualAddress)//如果传入的rva大于等于当前节的虚拟地址
		{
			DWORD sectionEndAddr = lpSectionTable->VirtualAddress+lpSectionTable->SizeOfRawData;//当前节结束地址=当前节的虚拟地址+文件对齐处理后节尺寸
			if (rva<sectionEndAddr)//如果这个rva地址在这个块里面
			{
				DWORD r_rva = rva-lpSectionTable->VirtualAddress;//这个rva地址-当前节的虚拟地址[rva距离节的开始地址的距离]
				return (DWORD)((byte*)pMappping+(lpSectionTable->PointerToRawData+r_rva));//内存映像基地址+当前节基于文件的偏移量+rva距离节的开始地址的距离
			}
		}
		lpSectionTable++;
		i--;
	}
	return (DWORD)((byte*)pMappping+rva);
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值