Решение по вашему запросу имеет некоторые пересечения с задачей чтение памяти процесса x64 из процесса x86 . В основном, мы должны знать о функциях NtWow64QueryInformationProcess64
и NtWow64ReadVirtualMemory64
, которые присутствуют в x86 ntdll.dll
и предназначены специально для получения информации о процессе x64 от x86.
Мы также должны знать некоторые зависимости между структурами ОС.
PROCESS_BASIC_INFORMATION
содержит адрес PEB
. PEB
обозначает блок технологической среды. Содержит адрес структуры PEB_LDR_DATA
. В свою очередь, он содержит адрес первой структуры LDR_DATA_TABLE_ENTRY
в цепочке LIST_ENTRY
. LDR_DATA_TABLE_ENTRY
содержит ссылку на следующее LDR_DATA_TABLE_ENTRY
.
Пример проверки этой информации в WinDbg:
0:000> !peb
PEB at 000007fffffdb000
...
0:000> dt ntdll!_peb 000007fffffdb000
...
+0x018 Ldr : 0x00000000`76fbd640 _PEB_LDR_DATA
...
0:000> dt ntdll!_PEB_LDR_DATA 76fbd640
...
+0x010 InLoadOrderModuleList : _LIST_ENTRY [ 0x00000000`00415bb0 - 0x00000000`070eb9c0 ]
...
0:000> dt ntdll!_LDR_DATA_TABLE_ENTRY 00415bb0
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x00000000`00415ca0 - 0x00000000`76fbd650 ]
...
+0x030 DllBase : 0x00000001`3f4d0000 Void
...
+0x058 BaseDllName : _UNICODE_STRING "procexp64.exe"
...
0:000> dt ntdll!_LDR_DATA_TABLE_ENTRY 00415ca0
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x00000000`00416020 - 0x00000000`00415bb0 ]
...
+0x030 DllBase : 0x00000000`76e90000 Void
...
+0x058 BaseDllName : _UNICODE_STRING "ntdll.dll"
...
В коде необходимо выполнить следующие шаги:
- Получить дескриптор процесса с обычным вызовом функции
OpenProcess
.
- Считать
PROCESS_BASIC_INFORMATION
структуру с вызовом NtWow64QueryInformationProcess64
.
- Получить адрес
PEB
, который присутствует в структуре PROCESS_BASIC_INFORMATION
.
- Считать
PEB
структуру с вызовом NtWow64ReadVirtualMemory64
.
- Получить адрес структуры
PEB_LDR_DATA
.
- Считайте
PEB_LDR_DATA
структуру и получите адрес первого LDR_DATA_TABLE_ENTRY
элемента.
- Продолжить чтение памяти для элемента
LDR_DATA_TABLE_ENTRY
, пока адрес следующего элемента не равен адресу первого элемента.
На шаге 7 мы также читаем буфер UNICODE_STRING
(который находится в LDR_DATA_TABLE_ENTRY
), чтобы получить текущее имя модуля.
Код указан ниже. Он состоит из двух файлов, main.cpp
и os_structs.hpp
.
main.cpp
#include "os_structs.hpp"
#include <algorithm>
#include <codecvt>
#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#ifndef WIN32
# error "This application must be built as an x86 executable"
#endif
#define GET_FUNC_ADDR(name) _##name name = (_##name)::GetProcAddress(::GetModuleHandleA("ntdll.dll"), #name)
#define IS_TRUE(clause, msg) if (!(clause)) { throw std::runtime_error(msg); }
namespace
{
struct close_on_exit
{
close_on_exit(HANDLE ptr)
: ptr_(ptr)
{ };
~close_on_exit()
{
if (ptr_)
{
::CloseHandle(ptr_);
ptr_ = nullptr;
}
}
private:
HANDLE ptr_;
};
// Names of modules
std::string convert_unicode_to_utf8(std::vector<uint8_t> &raw_bytes)
{
std::vector<uint16_t> unicode(raw_bytes.size() >> 1, 0);
memcpy(unicode.data(), raw_bytes.data(), raw_bytes.size());
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
const std::wstring wide_string(unicode.begin(), unicode.end());
const std::string utf8_string = converter.to_bytes(wide_string);
return utf8_string;
}
void *get_handle(uint32_t id)
{
HANDLE handle = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);
std::cout << "Opening target process...";
IS_TRUE(NULL != handle, "OpenProcess failed");
std::cout << " ok" << std::endl;
return handle;
}
void check_if_process_is_x64(HANDLE handle)
{
BOOL is_wow64_process = TRUE;
IS_TRUE(::IsWow64Process(handle, &is_wow64_process), "IsWow64Process failed");
IS_TRUE(FALSE == is_wow64_process, "Target process is not x64 one");
}
std::vector<uint8_t> read_mem(HANDLE handle, uint64_t address, uint32_t length)
{
IS_TRUE(handle, "No process handle obtained");
std::vector<uint8_t> data(length, 0);
GET_FUNC_ADDR(NtWow64ReadVirtualMemory64);
NTSTATUS status = NtWow64ReadVirtualMemory64(handle, address, data.data(), data.size(), FALSE);
IS_TRUE(NT_SUCCESS(status), "NtWow64ReadVirtualMemory64 failed");
return data;
}
void read_pbi(HANDLE handle, sys::PROCESS_BASIC_INFORMATION64 &pbi)
{
IS_TRUE(handle, "No process handle obtained");
GET_FUNC_ADDR(NtWow64QueryInformationProcess64);
NTSTATUS status = NtWow64QueryInformationProcess64(handle, sys::ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
IS_TRUE(NT_SUCCESS(status), "NtQueryInformationProcess failed");
}
std::vector<uint8_t> read_peb_data(HANDLE handle)
{
sys::PROCESS_BASIC_INFORMATION64 pbi = { 0 };
read_pbi(handle, pbi);
return read_mem(handle, pbi.PebBaseAddress, sizeof(sys::PEB64));
}
bool get_modules_load_order_via_peb(HANDLE handle)
{
std::cout << "Getting module load order...\n" << std::endl;
std::vector<uint8_t> read_peb = read_peb_data(handle);
sys::PEB64 *peb = (sys::PEB64 *)read_peb.data();
// ------------------------------------------------------------------------
// Read memory from pointer to loader data structures.
// ------------------------------------------------------------------------
std::vector<uint8_t> read_peb_ldr_data = read_mem(handle, (uintptr_t)peb->LoaderData, sizeof(sys::PEB_LDR_DATA64));
sys::PEB_LDR_DATA64 *peb_ldr_data = (sys::PEB_LDR_DATA64 *)read_peb_ldr_data.data();
sys::PEB_LDR_DATA64 *loader_data = (sys::PEB_LDR_DATA64 *)peb->LoaderData;
const uintptr_t addr_of_ptr_to_first_ldr_module = (uintptr_t)loader_data
+ ((uintptr_t)&loader_data->InLoadOrderModuleList - (uintptr_t)&loader_data->Length);
ULONGLONG address = peb_ldr_data->InLoadOrderModuleList.Flink;
uint32_t counter = 1;
// ------------------------------------------------------------------------
// Traversing loader data structures.
// ------------------------------------------------------------------------
do
{
std::vector<uint8_t> read_ldr_table_entry = read_mem(handle, address, sizeof(sys::LDR_DATA_TABLE_ENTRY64));
sys::LDR_DATA_TABLE_ENTRY64 *ldr_table_entry = (sys::LDR_DATA_TABLE_ENTRY64 *)read_ldr_table_entry.data();
std::vector<uint8_t> unicode_name = read_mem(handle, ldr_table_entry->BaseDllName.Buffer, ldr_table_entry->BaseDllName.MaximumLength);
std::string name = convert_unicode_to_utf8(unicode_name);
std::cout << "Module: " << name << std::endl;
std::cout << " Image base: 0x" << std::hex << ldr_table_entry->BaseAddress << std::endl;
ldr_table_entry = (sys::LDR_DATA_TABLE_ENTRY64 *)read_ldr_table_entry.data();
address = (uintptr_t)ldr_table_entry->InLoadOrderModuleList.Flink;
} while (addr_of_ptr_to_first_ldr_module != address);
std::cout << "\nEnumeration finished" << std::endl;
return true;
}
} // namespace
int main()
{
try
{
HANDLE handle = get_handle(16944);
close_on_exit auto_close_handle(handle);
check_if_process_is_x64(handle);
get_modules_load_order_via_peb(handle);
}
catch (const std::runtime_error &e)
{
std::cerr << "\n----------------------------------------------------\n";
std::cerr << "Exception occurred: " << e.what();
std::cerr << "\n----------------------------------------------------\n";
}
return 0;
}
os_structs.hpp
#pragma once
#include <windows.h>
#define NT_SUCCESS(x) ((x) >= 0)
// Namespace is present Not to collide with "winbase.h"
// definition of PROCESS_INFORMATION_CLASS and others.
namespace sys
{
typedef enum _PROCESS_INFORMATION_CLASS {
ProcessBasicInformation,
ProcessQuotaLimits,
ProcessIoCounters,
ProcessVmCounters,
ProcessTimes,
ProcessBasePriority,
ProcessRaisePriority,
ProcessDebugPort,
ProcessExceptionPort,
ProcessAccessToken,
ProcessLdtInformation,
ProcessLdtSize,
ProcessDefaultHardErrorMode,
ProcessIoPortHandlers,
ProcessPooledUsageAndLimits,
ProcessWorkingSetWatch,
ProcessUserModeIOPL,
ProcessEnableAlignmentFaultFixup,
ProcessPriorityClass,
ProcessWx86Information,
ProcessHandleCount,
ProcessAffinityMask,
ProcessPriorityBoost,
MaxProcessInfoClass
} PROCESS_INFORMATION_CLASS, *PPROCESS_INFORMATION_CLASS;
// ------------------------------------------------------------------------
// Structs.
// ------------------------------------------------------------------------
typedef struct _PROCESS_BASIC_INFORMATION64 {
ULONGLONG Reserved1;
ULONGLONG PebBaseAddress;
ULONGLONG Reserved2[2];
ULONGLONG UniqueProcessId;
ULONGLONG Reserved3;
} PROCESS_BASIC_INFORMATION64;
typedef struct _PEB_LDR_DATA64 {
ULONG Length;
BOOLEAN Initialized;
ULONGLONG SsHandle;
LIST_ENTRY64 InLoadOrderModuleList;
LIST_ENTRY64 InMemoryOrderModuleList;
LIST_ENTRY64 InInitializationOrderModuleList;
} PEB_LDR_DATA64, *PPEB_LDR_DATA64;
// Structure is cut down to ProcessHeap.
typedef struct _PEB64 {
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;
BOOLEAN BeingDebugged;
BOOLEAN Spare;
ULONGLONG Mutant;
ULONGLONG ImageBaseAddress;
ULONGLONG LoaderData;
ULONGLONG ProcessParameters;
ULONGLONG SubSystemData;
ULONGLONG ProcessHeap;
} PEB64;
typedef struct _UNICODE_STRING64 {
USHORT Length;
USHORT MaximumLength;
ULONGLONG Buffer;
} UNICODE_STRING64;
typedef struct _LDR_DATA_TABLE_ENTRY64 {
LIST_ENTRY64 InLoadOrderModuleList;
LIST_ENTRY64 InMemoryOrderModuleList;
LIST_ENTRY64 InInitializationOrderModuleList;
ULONGLONG BaseAddress;
ULONGLONG EntryPoint;
DWORD64 SizeOfImage;
UNICODE_STRING64 FullDllName;
UNICODE_STRING64 BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY64 HashTableEntry;
ULONGLONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY64, *PLDR_DATA_TABLE_ENTRY64;
} // namespace sys
// ------------------------------------------------------------------------
// Function prototypes.
// ------------------------------------------------------------------------
typedef NTSTATUS(NTAPI *_NtWow64QueryInformationProcess64)(
IN HANDLE ProcessHandle,
ULONG ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL);
typedef NTSTATUS(NTAPI *_NtWow64ReadVirtualMemory64)(
IN HANDLE ProcessHandle,
IN DWORD64 BaseAddress,
OUT PVOID Buffer,
IN ULONG64 Size,
OUT PDWORD64 NumberOfBytesRead);
Если вас интересуют исходные определения структур - лучший способ, который я понял, - это загрузить символы для WinDbg, а затем посмотреть расположение структур в этом отладчике. Вы можете увидеть образец в этом посте выше.