一个解析PE文件的小工具(可以检测一些可疑的api)

文章目录

// 003_解析导入表.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <windows.h>



DWORD RvaToOffset( IMAGE_NT_HEADERS* pNtHdr , DWORD dwRva ) {

	// 1. 找Rva所在的区段
	// 2. 用Rva减去所在区段的首Rva ,再用减出来的差,加上所在
	//    区段的首区段偏移
	IMAGE_SECTION_HEADER* pSechdr = NULL;
	pSechdr = IMAGE_FIRST_SECTION( pNtHdr );
	for( int i = 0 ; i < pNtHdr->FileHeader.NumberOfSections; ++i ) {
		if( dwRva >= pSechdr[ i ].VirtualAddress
			&& dwRva <= pSechdr[ i ].VirtualAddress + pSechdr[ i ].SizeOfRawData ) {

			dwRva -= pSechdr[ i ].VirtualAddress;
			dwRva += pSechdr[ i ].PointerToRawData;
			return dwRva;
		}
	}
	return -1;
}



int main( ) {
	
	//typedef struct _IMAGE_IMPORT_DESCRIPTOR {
	//	union {
	//		DWORD   Characteristics;            
	//		DWORD   OriginalFirstThunk; /*保存导入函数名称表(INT)的地址(RVA)*/        
	//	} DUMMYUNIONNAME;
	//	DWORD   TimeDateStamp;
	//	DWORD   ForwarderChain;// 是否是dll转发
	//	DWORD   Name;/*导入的模块名(DLL的名字)*/
	//	DWORD   FirstThunk;/*导入函数地址表(IAT)(RVA)*/
	//} IMAGE_IMPORT_DESCRIPTOR;
	// 解析导入表
	// 1. 读取文件到内存
	printf( "请输入要解析的PE文件的路径:" );
	char szPath[ MAX_PATH ];
	gets_s( szPath , MAX_PATH );

	HANDLE hFile = INVALID_HANDLE_VALUE;
	hFile = CreateFileA( szPath , GENERIC_READ , FILE_SHARE_READ ,
						 NULL , OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL );
	if( hFile == INVALID_HANDLE_VALUE ) {
		printf( "文件不存在\n" );
		system( "pause" );
		return 0;
	}
	DWORD dwHeight = 0;
	DWORD dwFileSize = GetFileSize( hFile , &dwHeight );

	BYTE* pBuff = new BYTE[ dwFileSize ];

	ReadFile( hFile , pBuff , dwFileSize , &dwFileSize , NULL );

	// 2. 解析出Dos头,Nt头,扩展头
	IMAGE_DOS_HEADER* pDosHdr = NULL;
	IMAGE_NT_HEADERS* pNtHdr = NULL;
	IMAGE_OPTIONAL_HEADER* pOptHdr = NULL;
	IMAGE_DATA_DIRECTORY* pDataDir = NULL;
	IMAGE_EXPORT_DIRECTORY* pExortTable = NULL;


	pDosHdr = (IMAGE_DOS_HEADER*)pBuff;
	pNtHdr = (IMAGE_NT_HEADERS*)( (ULONG_PTR)pDosHdr + pDosHdr->e_lfanew );
	pOptHdr = &pNtHdr->OptionalHeader;

	// 3. 得到扩展头中的数据目录表
	pDataDir = pOptHdr->DataDirectory;

	// 4. 通过数据目录表中的第1个元素得到导入表的RVA
	DWORD dwImpRva = pDataDir[ 1 ].VirtualAddress;
	if( dwImpRva == 0 ) {
		printf( "没有导入表" );
		system( "pause" );
		return 0;
	}
	// 5. 将导入表的RVA转换成文件偏移
	DWORD dwImpOfs = RvaToOffset( pNtHdr , dwImpRva );

	// 6. 使用导入表结构体指针指向导入表所在的内存地址
	IMAGE_IMPORT_DESCRIPTOR* pImpTab = NULL;
	pImpTab = (IMAGE_IMPORT_DESCRIPTOR*)( dwImpOfs + (ULONG_PTR)pDosHdr );

	// 7. 遍历所有的导入表
	//    导入表是以一个全0元素作为结束的标志
	while( pImpTab->Name != 0 ) {

		// 7.1 获取这个导入所导入的DLL的名称,导入表里面的Name也是一个RVA
		ULONG_PTR dwNameOfs = RvaToOffset( pNtHdr , pImpTab->Name );
		char* pName = nullptr;
		pName = (char *)( dwNameOfs + (ULONG_PTR)pDosHdr );
		printf( "导入的DLL:%s\n" , pName );			//pName为导入的dll的名字
		//IMAGE_THUNK_DATA32
		// 7.2 遍历从这个dll导入的函数名称
		DWORD dwINToffset = RvaToOffset(pNtHdr, pImpTab->OriginalFirstThunk);

		// INT : 导入名称表
		// 这个表记录了所有导入函数名称的地址(RVA)
		// 每个地址都是4/8字节(如果是32位PE文件,就是4字节,如果是64位的PE文件就是8字节)
		ULONG_PTR* pInt = (ULONG_PTR*)( dwINToffset + (ULONG_PTR)pDosHdr );

		while( *pInt != 0 ) {
			// 导入函数有两种方式:
			// 1. 以序号导入
			// 2. 以名称导入
			// 如果INT表中保存的值,最高位是0的时候,说明这个值是一个函数名称的RVA
			// 否则这个值的低16位就是一个导入的序号。
			if( IMAGE_SNAP_BY_ORDINAL( *pInt ) ) {

				// 以序号方式导入
				// 以序号方式导入, 保存的是序号,需要只有2字节
				printf( "\t0x%04X\n" , *pInt & 0xFFFF );
			}
			else {
				// 以名称方式导入,数组保存的RVA,并非是一个字符串的RVA
				// 而是一个结构体(IMAGE_IMPORT_BY_NAME)的RVA
				IMAGE_IMPORT_BY_NAME* pImpByName = NULL;
				DWORD dwNameRva = *pInt;
				dwNameOfs = RvaToOffset( pNtHdr , dwNameRva );
				pImpByName = (IMAGE_IMPORT_BY_NAME*)( dwNameOfs + (ULONG_PTR)pDosHdr );
					//printf( "\t序号:%04X,名称:%s这是以名称方式导入的\n" ,
					//	pImpByName->Hint ,
					//	pImpByName->Name );
				char* api[] = {"CreateFile ","RreadFile" ,"WriteFile" , "CreateFileMapping", "MapViewOfFile","RegOpenKeyEx", "RegSetValueEx", "RegGetValue" ,"OpenSCManager", "CreateService", "StartService"
					,"CoCreateInstance", "DllCanUnloadNow", "DllGetClassObject","DllInstall","DllRegisterServer","DllUnregisterServer","AdjustTokenPrivileges", "AttachThreadInput", "bind", "BitBlt", "CallNextHookEx"
					, "CertOpenSystemStore", "CheckRemoteDebuggerPresent", "connect", "ConnectNamedPipe","ControlService","CreateMutex", "CreateProcess", "CreateRemoteThread","CreateToolhelp32Snapshot", "CryptAcquireContext"
					, "DeviceIoControl", "DllCanUnloadNow","DllGetClassObject ","EnableExecuteProtectionSupport", "EnumProcesses","EnumProcessModules","FindFirstFile", "FindNextFile", "FindResource", "FindWindow", "FtpPutFile"
					, "GetAdaptersInfo","GetAsyncKeyState", "GetDC", "GetForegroundWindow", "gethostbyname", "gethostname","GetKeyState", "GetModuleFileName", "GetProcAddress", "GetStartupInfo","GetSystemDefaultLangID"
					," GetTempPath", "GetThreadContext", "GetTickCount", "GetVersionEx","GetWindowsDirectory", "inet_addr", "InternetOpenA" , "InternetReadFile" , "InternetWriteFile","IsDebuggerPresent", "IsNTAdmin"
					, "IsWow64Process", "LdrLoadDll", "LoadLibraryA","LoadResource", "LsaEnumerateLogonSessions", "MapVirtualKey","MmGetSystemRoutineAddress", "Module32First", "Module32Next", "NetScheduleJobAdd","NetShareEnum"
					,  "NtQueryDirectoryFile","NtQueryInformationProcess"," NetScheduleJobAdd", "OleInitialize","OpenMutex","OpenProcess", "OpenSCManagerA", "OutputDebugString", "PeekNamedPipe", "Process32First"                
					,   "Process32Next", "QueryPerformanceCounter", "QueueUserAPC", "ReadProcessMemory", "recv", "RegisterHotKey", "RegOpenKey","ResumeThread", "RtlCreateRegistryKey"  , "RtlWriteRegistryValue" ," SamIConnect"    
					,"SamIGetPrivateDate", "SamQueryInformationUse", "send", "SetFileTime ", "SetThreadContext","SetWindowsHookEx", "SfcTerminateWatcherThread", "ShellExecute","StartServiceCtrlDispatcher", "SuspendThread"		
					, "system"," Thread32First","Thread32Next" , "Toolhelp32ReadProcessMemory" , "URLDownloadToFile ", "VirtualAllocEx", "VirtualProtectEx", "WideCharToMultiByte", "WinExec", "WlxLoggedOnSAS"						
					,"Wow64DisableWow64FsRedirection" ,"OpenFile", "GetModuleHandle", "InternetOpenA", "InternetOpenUrl","InternetWriteFile", "RegCreateKey", "RegCreateKeyEx", "RegDeleteKey", "RegQueryValue"						
					,"RegEnumKey", "RegEnumKeyEx", "RegEnumValue", "RegLoadKey", "RegReplaceKey", "RegRestoreKey"," RegConnectRegistry", "RegUnLoadKeyA" ," AttachThreadInput"," EnumProcesses","ZwQueryInformationProcess"			
					, "WriteProcessMemory","RpcServerRegisterIf", "RpcServerListen", "RpcServerUseProtseqEp", "RpcMgmtStopServerListening", "RpcServerUnregisterIf"," RpcStringBindingCompose"                                       
					,"RpcBindingFromStringBinding", "RpcStringFree", "GetThreadContext","CryptReleaseContext"," CryptEnumProviders", "CryptCreateHash", "CryptGetHashParam", "CryptDestroyHash", "CryptHashData", "CryptDeriveKey"		
					, "CryptGetProvParam", "CryptSetKeyParam"," CryptEncrypt", "CryptDecrypt", "CryptDestroyKey", "CryptGenKey"," CryptGetUserKey", "CryptContextAddRef", "CryptExportKey", "SendMessage", "SendMessageCallback"		
					, "SendNotifyMessage","SendMessageTimeout", "PostMessage", "PostThreadMessage", "PostQuitMessage", "PostQuitMessage", "BroadcastSystemMessage", "GetMessage"," PeekMessage", "WaitMessage", "DispatchMessage" };  
				for (int i = 0;i < 177;i++)
				{
					if (strcmp(pImpByName->Name, api[i]) == 0)
						printf("比较可疑的api为%s\n", api[i]);
				}
			}

			// 得到下一个导入函数的名称的地址
			++pInt;
		}
		// 得到下一个导入表的地址
		++pImpTab;
	}  

	system( "pause" );
    return 0;
}





  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值