記憶體進程讀寫可以讓我們訪問其他進程的記憶體空間並讀取或修改其中的數據。這種技術通常用於各種調試工具、進程監控工具和反作弊系統等場景。在`Windows`系統中,記憶體進程讀寫可以通過一些`API`函數來實現,如`OpenProcess`、`ReadProcessMemory`和`WriteProcess... ...
記憶體進程讀寫可以讓我們訪問其他進程的記憶體空間並讀取或修改其中的數據。這種技術通常用於各種調試工具、進程監控工具和反作弊系統等場景。在Windows
系統中,記憶體進程讀寫可以通過一些API
函數來實現,如OpenProcess
、ReadProcessMemory
和WriteProcessMemory
等。這些函數提供了一種通用的方式來訪問其他進程的記憶體,並且可以用來讀取或寫入不同類型的數據,例如整數、位元組集、浮點數等。
在開始編寫記憶體讀者功能之前我們先來實現一個獲取特定進程內特定模塊基址的功能,該功能的實現分為兩部分首先我們封裝一個GetProcessModuleHandle
函數,該函數用戶可傳入一個進程PID
以及需要獲取的進程內的模塊名,此時會通過迴圈的方式找到所需返回的模塊並返回該模塊的moduleEntry.hModule
基址,由於使用了進程快照函數所以在使用時需要引入TlHelp32.h
庫。
// 根據PID模塊名(需要寫尾碼.dll)獲取模塊入口地址
HMODULE GetProcessModuleHandle(DWORD pid, CONST TCHAR* moduleName)
{
MODULEENTRY32 moduleEntry;
HANDLE handle = NULL;
// 獲取進程快照中包含在th32ProcessID中指定的進程的所有的模塊
handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
if (!handle)
{
CloseHandle(handle);
return NULL;
}
ZeroMemory(&moduleEntry, sizeof(MODULEENTRY32));
moduleEntry.dwSize = sizeof(MODULEENTRY32);
if (!Module32First(handle, &moduleEntry))
{
CloseHandle(handle);
return NULL;
}
do
{
if (_tcscmp(moduleEntry.szModule, moduleName) == 0)
{
return moduleEntry.hModule;
}
} while (Module32Next(handle, &moduleEntry));
CloseHandle(handle);
return 0;
}
有了上述讀取模塊基址的函數,接著就是要封裝實現GetProcessModuleBase
函數,該函數接收兩個參數,分別是進程名以及模塊名,並返回該模塊在指定進程中的句柄。如果指定的模塊名稱不存在於所給進程的模塊列表中,函數會返回NULL。
// 返回ExeName進程中指定Dll的基址
DWORD GetProcessModuleBase(string ExeName, string DllName)
{
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // 進程快照句柄
PROCESSENTRY32 process = { sizeof(PROCESSENTRY32) }; // 存放進程快照的結構體
// 遍歷進程
while (Process32Next(hProcessSnap, &process))
{
// 尋找ExeName指定進程 char* 轉 string
string s_szExeFile = process.szExeFile;
if (s_szExeFile == ExeName)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process.th32ProcessID);
if (hProcess != NULL)
{
// 尋找DllName並返回DWORD類型基址
return (DWORD)GetProcessModuleHandle(process.th32ProcessID, DllName.c_str());
}
}
}
return 0;
}
參數說明:
- hProcess:指定進程的句柄,通常可以通過OpenProcess函數獲取。
- lpModuleName:要獲取的模塊名稱,可以是一個字元串形式的模塊名稱或者指向模塊名稱字元串的指針。
當有了上述兩個模塊的支持那麼實現進程模塊基址的讀取將變得非常容易實現,如下是一段讀取模塊句柄的代碼示例,在代碼中我們分別讀取了Tutorial-i386.exe
自身模塊基地址,以及該進程內user32.dll
模塊基址,需要註意的是運行該程式需要使用管理員身份。
int main(int argc, char *argv[])
{
// "Tutorial-i386.exe"+256650
DWORD DllBase = GetProcessModuleBase("Tutorial-i386.exe", "Tutorial-i386.exe");
if (DllBase != 0)
{
std::cout << "模塊基地址: " << hex << DllBase + 0x256650 << std::endl;
}
DWORD User32 = GetProcessModuleBase("Tutorial-i386.exe", "user32.dll");
if (User32 != 0)
{
std::cout << "模塊基地址: " << hex << User32 << std::endl;
}
system("pause");
return 0;
}
運行上述代碼片段,讀者可看到如下輸出結果,代碼中分別讀取了一個進程基址,與系統模塊基址。
接著我們講解一下記憶體讀寫的實現方法,此處的讀寫分為32位與64位實現,在32位進程讀寫時可以使用微軟提供的ReadProcessMemory
讀及WriteProcessMemory
寫入,這兩個函數在參數傳遞上並沒有太大的差異。
ReadProcessMemory 函數用於從指定進程中讀取指定記憶體地址的數據,寫入一個緩衝區中。函數接受的參數包括要讀取的進程句柄,要讀取的記憶體地址,要讀取的數據大小等。如果讀取成功,函數會返回非零值。
BOOL WINAPI ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesRead
);
WriteProcessMemory 函數用於向指定進程中寫入數據,寫入一個緩衝區中的數據到另一個進程指定的記憶體地址中。函數接受的參數包括要寫入的進程句柄,要寫入的記憶體地址,要寫入的數據大小等。如果寫入成功,函數會返回非零值。
BOOL WINAPI WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesWritten
);
在使用 ReadProcessMemory()
和 WriteProcessMemory()
函數時,需要以管理員身份運行程式。此外,為了訪問其他進程的記憶體,還需要指定合適的訪問許可權,並且需要根據具體情況指定正確的記憶體地址和數據類型。
BOOL ReadMemory(DWORD dwID, LPCVOID lpAddress, DWORD* pRead)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwID);
if (INVALID_HANDLE_VALUE == hProcess)
{
return FALSE;
}
DWORD dwTemp = 0;
ReadProcessMemory(hProcess, lpAddress, (LPVOID)(pRead), 4, &dwTemp);
if (4 != dwTemp)
{
return FALSE;
}
return TRUE;
}
BOOL WriteMemory(DWORD dwID, LPVOID lpAddress, DWORD dwWrite)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwID);
if (INVALID_HANDLE_VALUE == hProcess)
{
return FALSE;
}
DWORD dwTemp = 0;
WriteProcessMemory(hProcess, lpAddress, (LPVOID)(&dwWrite), 4, &dwTemp);
if (4 != dwTemp)
{
return FALSE;
}
return TRUE;
}
// 讀取指定位置記憶體數據
DWORD Read(DWORD dwPID, std::string strAddress)
{
// 字元串轉為16進位整數指針
LPCVOID lpAddress = (LPCVOID)::strtol(strAddress.c_str(), NULL, 16);
DWORD dwRead = 0;
// 調用記憶體讀取
BOOL ref = ReadMemory(dwPID, lpAddress, &dwRead);
if (ref == TRUE)
{
return dwRead;
}
return FALSE;
}
// 寫入指定位置記憶體數據
BOOL Write(DWORD dwPID, std::string strAddress, std::string write_value)
{
LPVOID lpAddress = (LPVOID)::strtol(strAddress.c_str(), NULL, 16);
DWORD dwWrite = ::strtol(write_value.c_str(), NULL, 16);
BOOL bRet = WriteMemory(dwPID, lpAddress, dwWrite);
if (bRet == TRUE)
{
return TRUE;
}
return FALSE;
}
而如果讀者使用的是64位環境,那麼記憶體讀寫則需要藉助於NtWow64ReadVirtualMemory64
以及NtWow64WriteVirtualMemory64
這兩個未公開函數實現。
NtWow64ReadVirtualMemory64
和 NtWow64WriteVirtualMemory64
函數用於在 64
位 Windows
系統中的 32
位進程中讀寫虛擬記憶體。這兩個函數通常不直接由應用程式調用,而是由系統的函數庫和其他底層代碼使用。
NtWow64ReadVirtualMemory64 函數的原型為:
NTSTATUS NTAPI NtWow64ReadVirtualMemory64(HANDLE ProcessHandle,
PVOID64 BaseAddress,
PVOID Buffer,
ULONG64 Size,
PULONG64 NumberOfBytesRead);
該函數接受五個參數:
- ProcessHandle: 進程的句柄。用於指定要讀取記憶體的進程。
- BaseAddress: 要讀取的起始地址。
- Buffer: 讀取的數據存儲在這個緩衝區中。
- Size: 要讀取的位元組數量。
- NumberOfBytesRead: 實際讀取的位元組數量。
NtWow64WriteVirtualMemory64 函數的原型為:
NTSTATUS NTAPI NtWow64WriteVirtualMemory64(HANDLE ProcessHandle,
PVOID64 BaseAddress,
PVOID Buffer,
ULONG64 Size,
PULONG64 NumberOfBytesWritten);
該函數同樣接受五個參數:
- ProcessHandle: 進程的句柄。指定要寫入記憶體的進程。
- BaseAddress: 要寫入的起始地址。
- Buffer: 要寫入的數據存儲在這個緩衝區中。
- Size: 要寫入的位元組數量。
- NumberOfBytesWritten: 實際寫入的位元組數量。
上述這兩個函數都位於ntdll.dll
庫中,在使用時需要通過LoadLibrary
函數獲取到該動態鏈接庫的模塊句柄,併在該記憶體中使用GetProcAddress
函數動態得到上述兩個函數的基地址,有了基址就可以使用函數指針的方式動態的引用記憶體讀寫功能,如下則是一個完整的讀寫使用案例。
BOOL ReadMemory(DWORD dwID, PVOID64 lpAddress, DWORD* pRead)
{
// 打開進程並返回句柄
HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwID);
if (INVALID_HANDLE_VALUE == hProcess)
{
return FALSE;
}
typedef void* POINTER_64 PVOID64;
// 定義函數指針
typedef NTSTATUS(__stdcall* NTWOW64READVIRTUALMEMORY64)(HANDLE ProcessHandle, PVOID64 BaseAddress, PVOID Buffer, DWORD64 BufferSize, PDWORD64 NumberOfBytesRead);
NTWOW64READVIRTUALMEMORY64 pNtWow64ReadVirtualMemory64 = NULL;
// 載入模塊基址
HMODULE hModule = ::LoadLibrary("ntdll.dll");
if (NULL == hModule)
{
return FALSE;
}
// 通過GetProcAddress函數獲取NtWow64ReadVirtualMemory64入口地址
pNtWow64ReadVirtualMemory64 = (NTWOW64READVIRTUALMEMORY64)::GetProcAddress(hModule, "NtWow64ReadVirtualMemory64");
if (NULL == pNtWow64ReadVirtualMemory64)
{
return FALSE;
}
DWORD64 dwTemp = (DWORD64)0;
// 調用64位記憶體讀取函數
pNtWow64ReadVirtualMemory64(hProcess, (PVOID64)lpAddress, (LPVOID)(pRead), 4, &dwTemp);
FreeLibrary(hModule);
if (4 != dwTemp)
{
return FALSE;
}
return TRUE;
}
BOOL WriteMemory(DWORD dwID, PVOID64 lpAddress, DWORD* pWrite)
{
// 打開進程並返回句柄
HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwID);
if (INVALID_HANDLE_VALUE == hProcess)
{
return FALSE;
}
typedef void* POINTER_64 PVOID64;
// 定義函數指針
typedef NTSTATUS(__stdcall* NTWOW64WRITEVIRTUALMEMORY64)(HANDLE ProcessHandle, PVOID64 BaseAddress, PVOID Buffer, DWORD64 BufferSize, PDWORD64 NumberOfBytesRead);
NTWOW64WRITEVIRTUALMEMORY64 pNtWow64WriteVirtualMemory64 = NULL;
// 載入模塊基址
HMODULE hModule = ::LoadLibrary("ntdll.dll");
if (NULL == hModule)
{
return FALSE;
}
// 通過GetProcAddress函數獲取NtWow64WriteVirtualMemory64入口地址
pNtWow64WriteVirtualMemory64 = (NTWOW64WRITEVIRTUALMEMORY64)::GetProcAddress(hModule, "NtWow64WriteVirtualMemory64");
if (NULL == pNtWow64WriteVirtualMemory64)
{
return FALSE;
}
DWORD64 dwTemp = (DWORD64)0;
// 調用記憶體寫入
pNtWow64WriteVirtualMemory64(hProcess, (PVOID64)lpAddress, (pWrite), 4, &dwTemp);
::FreeLibrary(hModule);
if (4 != dwTemp)
{
return FALSE;
}
return TRUE;
}
// 執行記憶體讀取
DWORD Read(DWORD dwPID, std::string strAddress)
{
// LPCVOID lpAddress = (LPCVOID)::strtol(m_strAddress, NULL, 16);
typedef void* POINTER_64 PVOID64;
PVOID64 lpAddress = (PVOID64)::_strtoi64_l(strAddress.c_str(), NULL, 16, NULL);
DWORD dwRead = 0;
BOOL ref = ReadMemory(dwPID, lpAddress, &dwRead);
if (ref == TRUE)
{
return dwRead;
}
return FALSE;
}
// 執行寫入命令
BOOL Write(DWORD dwPID, std::string strAddress, DWORD write_value)
{
typedef void* POINTER_64 PVOID64;
PVOID64 lpAddress = (PVOID64)::_strtoi64_l(strAddress.c_str(), NULL, 16, NULL);
BOOL bRet = WriteMemory(dwPID, lpAddress, &write_value);
if (bRet)
{
return TRUE;
}
return FALSE;
}
上述代碼的使用非常容易,調用記憶體寫入時只需要傳入進程PID,寫入的記憶體地址,和寫入的數據長度即可,如下所示則是實現調用的案例。
int main(int argc, char *argv[])
{
DWORD Pid = 13564;
// 執行記憶體寫入
BOOL write_flag = Write(Pid, "0x00151F38", "9999");
printf("[記憶體寫入] 狀態 = %d \n", write_flag);
// 執行記憶體讀取
DWORD read_byte = Read(Pid, "0x00151F38");
printf("[記憶體讀取] 數據 = %d \n", read_byte);
system("pause");
return 0;
}
記憶體讀寫輸出效果如下圖所示;
我們以32
位為例對上述函數進行整合封裝,實現一個通用的記憶體讀寫,通過使用template
模板機制封裝ReadMemory
記憶體讀取,WriteMemory
記憶體寫入,這些函數在調用時支持讀寫,記憶體整數型,短整數,浮點數,位元組,位元組集等,同時還封裝實現FindPattern
函數用於實現對特定記憶體的特征匹配,讀者在編寫進程讀寫時可以直接使用這些函數案例,完整代碼如下所示;
#include <Windows.h>
#include <iostream>
#include <TlHelp32.h>
// 打開進程
HANDLE GetProcessHandle(DWORD pid)
{
return OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
}
// 獲取進程PID
DWORD GetProcessIdByHwnd(DWORD hWnd)
{
DWORD pid;
GetWindowThreadProcessId((HWND)hWnd, &pid);
return pid;
}
// 取進程PID
DWORD GetProcessIdByName(LPCTSTR name)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hSnapshot)
{
return NULL;
}
PROCESSENTRY32 pe = { sizeof(pe) };
for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe))
{
if (lstrcmpi(pe.szExeFile, name) == 0)
{
CloseHandle(hSnapshot);
return pe.th32ProcessID;
}
}
CloseHandle(hSnapshot);
return 0;
}
// 取指定模塊句柄
DWORD GetProcessModuleHandle(DWORD pid, CONST TCHAR* moduleName)
{
MODULEENTRY32 moduleEntry;
HANDLE handle = NULL;
handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
if (!handle)
{
CloseHandle(handle);
return NULL;
}
ZeroMemory(&moduleEntry, sizeof(MODULEENTRY32));
moduleEntry.dwSize = sizeof(MODULEENTRY32);
if (!Module32First(handle, &moduleEntry))
{
CloseHandle(handle);
return NULL;
}
do
{
if (lstrcmpi(moduleEntry.szModule, moduleName) == 0)
{
return (DWORD)moduleEntry.hModule;
}
} while (Module32Next(handle, &moduleEntry));
CloseHandle(handle);
return 0;
}
// 讀取類型: 整數型 短整數型 浮點型 位元組型
template<typename T1>
T1 ReadMemory(HANDLE processHandle, DWORD pAddr)
{
T1 result;
ReadProcessMemory(processHandle, (LPCVOID)(pAddr), &result, sizeof(T1), NULL);
return result;
}
// 讀取類型: 自定義大小記憶體
template<typename T1>
T1* ReadMemory(HANDLE processHandle, DWORD pAddr, DWORD length)
{
T1* result = new T1[length];
ReadProcessMemory(processHandle, (LPCVOID)(pAddr), result, length, NULL);
return result;
}
// 寫類型: 整數型 短整數型 浮點型 位元組型
template<typename T1>
void WriteMemory(HANDLE processHandle, DWORD pAddr, T1 vaule)
{
WriteProcessMemory(processHandle, (LPVOID)(pAddr), &vaule, sizeof(T1), NULL);
}
// 寫類型: 位元組集
template<typename T1>
void WriteMemory(HANDLE processHandle, DWORD pAddr, T1 vaule, DWORD length)
{
WriteProcessMemory(processHandle, (LPVOID)(pAddr), vaule, length, NULL);
}
// 分配記憶體
DWORD AllocMemory(HANDLE processHandle, DWORD size)
{
return (DWORD)VirtualAllocEx(processHandle, NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
}
// 釋放記憶體
BOOL FreeMemory(HANDLE processHandle, DWORD addr, DWORD size)
{
return VirtualFreeEx(processHandle, (LPVOID)addr, size, MEM_RELEASE);
}
// 特征查找
uintptr_t FindPattern(HANDLE processHandle, uintptr_t start, uintptr_t length, const unsigned char* pattern, const char* mask)
{
size_t pos = 0;
auto maskLength = strlen(mask) - 1;
auto startAdress = start;
for (auto it = startAdress; it < startAdress + length; ++it)
{
if (ReadMemory<unsigned char>(processHandle, (DWORD)it) == pattern[pos] || mask[pos] == '?')
{
if (mask[pos + 1] == '\0')
{
return it - maskLength;
}
pos++;
}
else
{
pos = 0;
}
}
return 0;
}
當我們需要讀寫整數或浮點數時只需要在調用特定函數時傳入模板即可,我們以讀取浮點數為例,在調用ReadMemory
函數時傳入<FLOAT>
則代表參數傳遞採用浮點數模式,同理讀取整數時同樣可以使用<DWORD>
模板,如下代碼則是實現讀寫整數與浮點數的案例演示。
int main(int argc, char *argv[])
{
DWORD Pid = GetProcessIdByName(L"Tutorial-i386.exe");
printf("[+] 進程PID = %d \n", Pid);
HANDLE handle = GetProcessHandle(Pid);
printf("[+] 進程句柄 = %X \n", handle);
// -----------------------------------------------------------
// 進程寫記憶體
// -----------------------------------------------------------
// 寫整數
WriteMemory<DWORD>(handle, 0x019C7A18, (DWORD)1000);
// 寫浮點數
WriteMemory<FLOAT>(handle, 0x019CD0E8, (FLOAT)100.234);
// -----------------------------------------------------------
// 進程讀記憶體
// -----------------------------------------------------------
// 讀整數
DWORD read_dword = ReadMemory<DWORD>(handle, 0x019C7A18);
printf("[*] 讀記憶體整數型 = %d \n", read_dword);
// 讀浮點數
FLOAT read_float = ReadMemory<FLOAT>(handle, 0x019CD0E8);
printf("[*] 讀記憶體浮點數 = %f \n", read_float);
system("pause");
return 0;
}
上述代碼運行後,首先會調用寫入函數對記憶體0x19C7A18
寫入1000
的整數,並對0x19CD0E8
寫入100.234
的浮點數,接著會再調用ReadMemory
將這兩個數讀取並輸出到屏幕,如下圖所示;
接著我們繼續實現讀寫記憶體位元組集的功能,位元組集的讀寫其原理是通過迴圈的方式讀寫位元組,每次迴圈時記憶體地址遞增1,並迴圈將列表內的參數一次性寫出到進程中,在寫入位元組集之前需要確保該記憶體空間具有PAGE_EXECUTE_READWRITE
讀寫執行屬性,如果不存在則還需要調用VirtualProtectEx
設置屬性,如下所示是讀寫位元組集的完整代碼;
int main(int argc, char *argv[])
{
DWORD Pid = GetProcessIdByName(L"Tutorial-i386.exe");
printf("[+] 進程PID = %d \n", Pid);
HANDLE handle = GetProcessHandle(Pid);
printf("[+] 進程句柄 = %X \n", handle);
// -----------------------------------------------------------
// 進程寫位元組集
// -----------------------------------------------------------
DWORD addr = 0x57E000;
DWORD length = 10;
BYTE code[10] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
DWORD old_protect;
if (VirtualProtectEx(handle, (LPVOID)addr, length, PAGE_EXECUTE_READWRITE, &old_protect))
{
BYTE* temp = (BYTE*)addr;
for (int i = 0; i < length; i++)
{
WriteMemory<BYTE>(handle, DWORD(temp + i), code[i]);
}
}
VirtualProtectEx(handle, (LPVOID)addr, length, old_protect, NULL);
// -----------------------------------------------------------
// 進程讀位元組與位元組集
// -----------------------------------------------------------
// 測試讀位元組
BYTE read_byte = ReadMemory<BYTE>(handle, 0x57E000);
printf("[byte] 讀記憶體位元組 = %02X \n", read_byte);
// 測試讀位元組集
BYTE** read_byte_ptr = ReadMemory<BYTE *>(handle, 0x57E000, 10);
for (int x = 0; x < 10; x++)
{
printf("[bytes] 讀[%d]位元組集 = %02X \n", x, read_byte_ptr[x]);
}
system("pause");
return 0;
}
當讀者運行上述代碼後,會調用WriteMemory<BYTE>
向記憶體0x57E000
寫出一段code
位元組集,接著再次調用ReadMemory<BYTE *>
讀取位元組集並列印輸出,如下圖所示;
特征碼搜索功能可以使用FindPattern
函數,該函數接收匹配進程的句柄,以及記憶體開始位置及結束位置,變數find_code
則是所需搜索的位元組集列表,mask
代表位元組集掩碼,此處的掩碼必須要與位元組集列表保持一致,當搜索到特征碼之後會返回當前的記憶體地址,放入param
變數內,如下代碼;
int main(int argc, char *argv[])
{
DWORD Pid = GetProcessIdByName(L"Tutorial-i386.exe");
printf("[+] 進程PID = %d \n", Pid);
HANDLE handle = GetProcessHandle(Pid);
printf("[+] 進程句柄 = %X \n", handle);
DWORD module_base = GetProcessModuleHandle(Pid, L"Tutorial-i386.exe");
printf("[*] 模塊句柄 = %X \n", module_base);
// -----------------------------------------------------------
// 記憶體特征匹配
// -----------------------------------------------------------
// 讀取DOS頭
IMAGE_DOS_HEADER DOSHeader = ReadMemory<IMAGE_DOS_HEADER>(handle, (DWORD)module_base);
printf("[+] DOS Header = %X \n", DOSHeader);
// 得到NT頭
IMAGE_NT_HEADERS NTHeaders = ReadMemory<IMAGE_NT_HEADERS>(handle, DWORD(uintptr_t(module_base) + DOSHeader.e_lfanew));
printf("[+] NT Header = %X \n", NTHeaders);
// 搜索位元組集
BYTE find_code[10] = { 0xF0, 0x8B, 0x46, 0x08, 0xE8, 0xCF, 0xD8, 0xFF, 0xFF, 0x8B };
// 搜索掩碼
const char* mask = "?? ?? ?? ?? ?? ?? ?? ?? ?? ??";
// 搜索特征碼並返回指針
uintptr_t param = FindPattern(handle,
reinterpret_cast<uintptr_t>(handle)+NTHeaders.OptionalHeader.BaseOfCode,
reinterpret_cast<uintptr_t>(handle)+NTHeaders.OptionalHeader.SizeOfCode,
find_code,
mask
);
printf("param = 0x%x \n", param);
system("pause");
return 0;
}
運行後即可搜索到PE文件內,符合條件的記憶體機器碼,輸出效果如下圖所示;
本文作者: 王瑞
本文鏈接: https://www.lyshark.com/post/7ab1f162.html
版權聲明: 本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!
文章出處:https://www.cnblogs.com/LyShark/p/17727115.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!