文章目录
// 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;
}