2010-09-27 5 views
15

У меня есть требование, чтобы восстановить все модули процесса 64-битной в 32-х WOW процесса в Windows, EnumProcessModules потерпит неудачу, как описано:Как ENUM модули в процессе 64-битной из 32-х WOW обработки

Если это функция вызывается из 32-разрядного приложения, запущенного на WOW64, он может только перечислять модули 32-битного процесса. Если процесс является 64-битным процессом, эта функция завершается с ошибкой, а последний код ошибки - ERROR_PARTIAL_COPY (299).

Что касается EnumProcessModulesEx и CreateToolhelp32Snapshot.

У вас есть идея, как этого достичь?

Спасибо.

ответ

15

Не вдаваясь в недокументированные API, вы не можете этого сделать. В общем, чтение 64-битной памяти процесса из 32-битного процесса не будет работать из-за различий в адресном пространстве.

EnumProcessModulesEx, который имеет LIST_MODULES_32BIT и LIST_MODULES_64BIT фильтра флаги, это сказать:

Эта функция предназначена в первую очередь для 64-битных приложений. Если функция вызывается 32-разрядным приложением под WOW64, параметр dwFilterFlag игнорируется, и функция дает те же результаты, что и функция EnumProcessModules.

Вы можете сделать это путем преобразования программы в 64-битной, используя вне прок 64-битный COM-сервер (в частности, с помощью DLL surrogate), или имеющие отдельный процесс, который вы общаетесь с. В качестве альтернативы, в зависимости от того, когда ваш процесс начинается относительно вашего целевого процесса, вы можете использовать WMI для получения событий загрузки модуля. См. Событие Win32_ModuleLoadTrace.

Process Explorer, один 32-разрядный exe, может показывать вам модули как для 32-, так и для 64-битных процессов, но это действительно дым и зеркала: 32-разрядный exe содержит 64-битную версию, которая записывается на диск и выполняется на 64-битных машинах.

+0

Использование 64-битного внепроцессного COM-звука - хорошая идея - +1! – snemarch

+0

Спасибо, Крис, вы предоставляете много полезной информации. –

+0

Фактически чтение памяти процесса x64 из 32-битного процесса [возможно] (https://stackoverflow.com/questions/5714297/is-it-possible-to-read-process-memory-of-a-64-bit -процесс-с-а-32bit-приложение/36798492 # 36798492). Перечисление модулей - это совсем другое дело. Мы можем вызывать функции из 64-разрядных 'ntdll.dll' и некоторых других библиотек, но не из' kernel32.dll' и т. П. [Вот список] (http://msdn.microsoft.com/en-us/library/aa384274%28v=vs.85%29.aspx) 64-разрядных библиотек, загруженных в процесс wow64: только 'ntdll.dll 'содержит полезную полезную нагрузку. 'kernel32.dll' отсутствует в списке. – greenpiece

1

Использование инструментария управления Windows (WMI). Пример (Delphi):

function GetProcessCount(const aFileName: string): Integer; 
var 
    lValue: LongWord; 
    lWMIService: OleVariant; 
    lWMIItems: OleVariant; 
    lWMIItem: OleVariant; 
    lWMIEnum: IEnumVariant; 
begin 
    Result := -1; 
    lWMIService := GetWMIObject('winmgmts:\\.\root\CIMV2'); { Do not localize. } 
    if (TVarData(lWMIService).VType = varDispatch) and (TVarData(lWMIService).VDispatch <> nil) then 
    begin 
    Result := 0; 
    lWMIItems := lWMIService.ExecQuery(Format('SELECT * FROM Win32_Process WHERE Name=''%s''', [ExtractFileName(aFileName)])); { Do not localize. } 
    lWMIEnum := IUnknown(lWMIItems._NewEnum) as IEnumVariant; 
    while lWMIEnum.Next(1, lWMIItem, lValue) = 0 do 
    begin 
     Inc(Result); 
    end; 
    end; 
end; 
0

решение для вашего запроса имеет некоторые пересечения с задачей reading x64 process memory from x86 process. В основном, мы должны знать о функциях 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" 
... 

Шаги, которые необходимо принять в коде, являются следующие:

  1. Получить дескриптор процесса с обычным вызовом OpenProcess функции.
  2. Прочитано PROCESS_BASIC_INFORMATION Структура с вызовом NtWow64QueryInformationProcess64.
  3. Получить адрес PEB, который есть в PROCESS_BASIC_INFORMATION Структура.
  4. Прочитано PEB Структура с вызовом NtWow64ReadVirtualMemory64.
  5. Адрес: PEB_LDR_DATA Структура.
  6. Прочитать PEB_LDR_DATA структуру и получить адрес первых LDR_DATA_TABLE_ENTRY элемент.
  7. Продолжайте чтение памяти для элемента 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, а затем просматривать структуру структур в этом отладчике. Вы можете увидеть образец в этом сообщении выше.

 Смежные вопросы

  • Нет связанных вопросов^_^