要讀取已經(jīng)執(zhí)行的進(jìn)程的 PE 結(jié)構(gòu),可以使用以下步驟:
- 獲取目標(biāo)進(jìn)程的句柄(使用
OpenProcess函數(shù))。 - 獲取目標(biāo)進(jìn)程的基址(即模塊句柄)(使用
GetModuleHandleEx或EnumProcessModules函數(shù))。 - 讀取目標(biāo)進(jìn)程內(nèi)存中的 DOS 頭(使用
ReadProcessMemory函數(shù))。 - 從 DOS 頭中獲取 NT 頭的偏移位置(即 DOS 頭中的
e_lfanew字段)。 - 讀取目標(biāo)進(jìn)程內(nèi)存中的 NT 頭(使用
ReadProcessMemory函數(shù))。 - 從 NT 頭中獲取可選頭的偏移位置。
- 讀取目標(biāo)進(jìn)程內(nèi)存中的可選頭(使用
ReadProcessMemory函數(shù))。 - 根據(jù)可選頭中的數(shù)據(jù)目錄表中的導(dǎo)入表項,獲取導(dǎo)入表的位置。
- 讀取目標(biāo)進(jìn)程內(nèi)存中的導(dǎo)入表(使用
ReadProcessMemory函數(shù))。
void OpenProcessAnalyzeImportTable(DWORD dwProcessId)
{
// 13056
HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
if (hProcess != NULL)
{
// 獲取目標(biāo)進(jìn)程模塊句柄
HMODULE hModules[1];
DWORD cbNeeded;
if (!EnumProcessModules(hProcess, hModules, sizeof(hModules), &cbNeeded))
{
printf("獲取模塊句柄失敗\n");
CloseHandle(hProcess);
return;
}
//解析DOS頭
IMAGE_DOS_HEADER DosHeader;
ReadProcessMemory(hProcess, (LPVOID)hModules[0], &DosHeader, sizeof(IMAGE_DOS_HEADER), NULL);
printf("Dos e_magic : %08X\n", DosHeader.e_magic);
printf("Dos e_lfanew : %08X\n", DosHeader.e_lfanew);
// 獲取 NT 頭的偏移位置
DWORD ntHeaderOffset = DosHeader.e_lfanew;
// e_lfanew 字段中的偏移是相對于可執(zhí)行文件的起始位置的偏移
// 讀取目標(biāo)進(jìn)程的 NT 頭
IMAGE_NT_HEADERS ntHeader;
if (!ReadProcessMemory(hProcess, (LPBYTE)hModules[0] + ntHeaderOffset, &ntHeader, sizeof(ntHeader), NULL))
{
printf("讀取 NT 頭失敗\n");
CloseHandle(hProcess);
return;
}
printf("NT Signature : %08X\n", ntHeader.Signature);
//標(biāo)準(zhǔn)文件頭
IMAGE_FILE_HEADER fileHeader;
DWORD fileHeaderOffset = DosHeader.e_lfanew + sizeof(DWORD);
if (!ReadProcessMemory(hProcess, (LPBYTE)hModules[0] + fileHeaderOffset, &fileHeader, sizeof(fileHeader), NULL))
{
printf("讀取標(biāo)準(zhǔn)文件頭失敗\n");
CloseHandle(hProcess);
return;
}
printf("Section number : %08X\n", fileHeader.NumberOfSections);
//擴(kuò)展文件頭
IMAGE_OPTIONAL_HEADER optionHeader;
DWORD optionHeaderOffset = DosHeader.e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER);
if (!ReadProcessMemory(hProcess,(LPBYTE)hModules[0] + optionHeaderOffset, &optionHeader, sizeof(optionHeader), NULL))
{
printf("讀取擴(kuò)展頭失敗\n");
CloseHandle(hProcess);
return;
}
printf("IMAGE_OPTIONAL_HEADER Magic %08X\n", optionHeader.Magic);
for (size_t i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
{
printf("%s\n", DataDirName[i]);
printf("\t%08X\n", optionHeader.DataDirectory[i].VirtualAddress);
}
// 獲取導(dǎo)入表的位置
DWORD importTableRva = optionHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
// 讀取目標(biāo)進(jìn)程的導(dǎo)入表
IMAGE_IMPORT_DESCRIPTOR importTable;
if (!ReadProcessMemory(hProcess, (LPBYTE)hModules[0] + importTableRva, &importTable, sizeof(IMAGE_IMPORT_DESCRIPTOR), NULL))
{
printf("讀取導(dǎo)入表失敗\n");
CloseHandle(hProcess);
return;
}
DWORD dwIndex = 1;
while (importTable.OriginalFirstThunk != 0 || importTable.FirstThunk != 0)
{
// 讀取模塊名 !!!
// importTable.Name RVA
char szModuleName[MAX_PATH];
LPVOID moduleNameAddress = (LPBYTE)hModules[0] + importTable.Name;
if (!ReadProcessMemory(hProcess, moduleNameAddress, szModuleName, sizeof(szModuleName), NULL))
{
printf("讀取模塊名失敗\n");
CloseHandle(hProcess);
return;
}
printf("模塊名稱 : %s\n", szModuleName);
printf("\t日期時間標(biāo)志 : %08X\n", importTable.TimeDateStamp);
printf("\tForwarderChain : %08X\n", importTable.ForwarderChain);
printf("\t名稱Offset : %08X\n", importTable.Name);
printf("\tFirstThunk : %08X\n", importTable.FirstThunk);
printf("\tOriginalFirstThunk : %08X\n", importTable.OriginalFirstThunk);
ReadProcessMemory(hProcess, (LPBYTE)hModules[0] + importTableRva + sizeof(IMAGE_IMPORT_DESCRIPTOR) * dwIndex, &importTable, sizeof(IMAGE_IMPORT_DESCRIPTOR), NULL);
dwIndex++;
}
}
else
{
DWORD dwError = GetLastError();
printf("進(jìn)程打開失敗,錯誤代碼 %d\n", dwError);
}
}
OpenProcess 函數(shù)
OpenProcess 是一個 Windows API 函數(shù),用于打開一個已存在的進(jìn)程并返回其句柄。
語法
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 指定對進(jìn)程對象的訪問權(quán)限
BOOL bInheritHandle, // 指定新句柄是否可被子進(jìn)程繼承
DWORD dwProcessId // 要打開的進(jìn)程的標(biāo)識符??梢允褂眠M(jìn)程標(biāo)識符(PID)來指定目標(biāo)進(jìn)程
);
示例
DWORD dwProcessId = 1234;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (hProcess != NULL) {
printf("進(jìn)程句柄獲取成功!\n");
// 對進(jìn)程進(jìn)行操作...
CloseHandle(hProcess);
} else {
DWORD dwError = GetLastError();
printf("進(jìn)程句柄獲取失敗,錯誤代碼:%d\n", dwError);
}
GetModuleHandleEx 函數(shù)
GetModuleHandleEx 函數(shù)用于獲取指定進(jìn)程中模塊的句柄(基址)。
函數(shù)原型
BOOL GetModuleHandleEx(
DWORD dwFlags, // 標(biāo)志位,用于指定搜索模塊的方式
LPCTSTR lpModuleName, // 要獲取句柄的模塊名稱。可以為 NULL,表示獲取當(dāng)前進(jìn)程中的模塊句柄
HMODULE *phModule // 用于接收獲取到的模塊句柄的指針
);
GetModuleHandleEx 函數(shù)獲取當(dāng)前進(jìn)程的模塊句柄
HMODULE hModule = NULL;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, NULL, &hModule)
GetModuleHandleEx 函數(shù)獲取指定進(jìn)程的模塊句柄
// 獲取指定進(jìn)程的句柄
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
// 獲取模塊的基址
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,NULL, &hModule)
進(jìn)程句柄 & 內(nèi)存基址 & 模塊句柄 & 模塊基址
- 進(jìn)程的句柄與加載到內(nèi)存的基址是相關(guān)的。
- 進(jìn)程句柄只是一個用于標(biāo)識和引用進(jìn)程的標(biāo)識符,它本身并不直接提供進(jìn)程的內(nèi)存位置信息。
- 但是,通過
使用進(jìn)程句柄可以獲取進(jìn)程加載到內(nèi)存中的模塊句柄(Module Handle)或模塊基址(Module Base Address) - 模塊句柄或模塊基址是指表示進(jìn)程中加載的某個模塊(如動態(tài)鏈接庫或可執(zhí)行文件)在內(nèi)存中的起始地址。通過獲取進(jìn)程的模塊句柄或模塊基址,可以進(jìn)一步定位和操作模塊內(nèi)的數(shù)據(jù)、函數(shù)和資源。
- 對于一個已加載的模塊,
模塊句柄和模塊基址通常是相同的,它們都指向模塊在內(nèi)存中的起始位置。因此,在大多數(shù)情況下,可以將模塊句柄和模塊基址視為相同的概念,用于引用和訪問模塊。
EnumProcessModules 函數(shù)
EnumProcessModules 函數(shù)是Windows API中的一個函數(shù),用于獲取指定進(jìn)程中加載的所有模塊(DLL)的句柄。
BOOL EnumProcessModules(
HANDLE hProcess, // 要枚舉模塊的進(jìn)程的句柄
HMODULE *lphModule, // 指向模塊句柄數(shù)組的指針,用于接收模塊句柄
DWORD cb, // 指定模塊句柄數(shù)組的大?。ㄒ宰止?jié)為單位)
LPDWORD lpcbNeeded // 指向接收實際返回的模塊句柄數(shù)的變量的指針
);
該函數(shù)的作用是枚舉指定進(jìn)程中加載的模塊,并將每個模塊的句柄存儲在提供的數(shù)組中。通過指定的句柄數(shù)組大小,可以控制要獲取的模塊句柄的數(shù)量。
示例代碼 獲取指定進(jìn)程中加載的模塊(DLL)的文件名
tips
- 獲取所有模塊,定義接收模塊數(shù)組
HMODULE hModules[1024];,數(shù)組中的第一個元素(hModules[0])對應(yīng)的是主模塊(也就是進(jìn)程自身的模塊)的句柄
DWORD processId = 1234; // 替換為目標(biāo)進(jìn)程的 ID
// 打開進(jìn)程獲取句柄
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
HMODULE hModules[1024];
DWORD cbNeeded;
// 獲取進(jìn)程中加載的模塊句柄
if (EnumProcessModules(hProcess, hModules, sizeof(hModules), &cbNeeded))
{
int moduleCount = cbNeeded / sizeof(HMODULE);
for (int i = 0; i < moduleCount; i++)
{
TCHAR szModuleName[MAX_PATH];
// 獲取模塊文件名
if (GetModuleFileNameEx(hProcess, hModules[i], szModuleName, sizeof(szModuleName) / sizeof(TCHAR)))
{
std::cout << "Module " << i + 1 << ": " << szModuleName << std::endl;
}
}
}
else
{
std::cout << "Failed to enumerate modules" << std::endl;
}
ReadProcessMemory 函數(shù)
ReadProcessMemory 是一個 Windows API 函數(shù),用于從一個指定進(jìn)程的內(nèi)存中讀取數(shù)據(jù)。
語法
BOOL ReadProcessMemory(
HANDLE hProcess, // 要讀取內(nèi)存的目標(biāo)進(jìn)程的句柄
LPCVOID lpBaseAddress, // 要讀取的內(nèi)存地址在目標(biāo)進(jìn)程中的起始位置
LPVOID lpBuffer, // 用于存儲讀取的數(shù)據(jù)的緩沖區(qū)
SIZE_T nSize, // 要讀取的字節(jié)數(shù)
SIZE_T *lpNumberOfBytesRead // 指向存儲實際讀取字節(jié)數(shù)的變量的指針
);
示例
DWORD dwProcessId = 1234;
HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, dwProcessId);
if (hProcess != NULL) {
LPVOID lpBaseAddress = (LPVOID)0x12345678;
BYTE buffer[1024];
SIZE_T nSize = sizeof(buffer);
SIZE_T nBytesRead = 0;
BOOL bSuccess = ReadProcessMemory(hProcess, lpBaseAddress, buffer, nSize, &nBytesRead);
if (bSuccess) {
printf("讀取成功!讀取到的字節(jié)數(shù):%d\n", nBytesRead);
// 處理讀取到的數(shù)據(jù)...
} else {
DWORD dwError = GetLastError();
printf("讀取失敗,錯誤代碼:%d\n", dwError);
}
CloseHandle(hProcess);
} else {
DWORD dwError = GetLastError();
printf("進(jìn)程句柄獲取失敗,錯誤代碼:%d\n", dwError);
}