2010-09-27 20 views
15

Tengo un requisito para recuperar todos los módulos de un proceso de 64 bits en un 32 bits WOW proceso en Windows, EnumProcessModules fracasaría como se describe:Cómo ENUM módulos en un proceso de 64 bits a partir de una de 32 bits WOW procesar

Si esto se llama a la función desde una aplicación de 32 bits que se ejecuta en WOW64, solo puede enumerar los módulos de un proceso de 32 bits. Si el proceso es de 64 bits, esta función falla y el último código de error es ERROR_PARTIAL_COPY (299).

EnumProcessModulesEx y CreateToolhelp32Snapshot.

¿Tiene alguna idea de cómo lograrlo?

Gracias.

Respuesta

15

Sin entrar en API no documentadas, no puede hacer esto. En general, leer una memoria de proceso de 64 bits de un proceso de 32 bits no funcionará debido a las diferencias de espacio de direcciones.

EnumProcessModulesEx, que tiene LIST_MODULES_32BIT y LIST_MODULES_64BIT banderas de filtro, tiene esto que decir:

Esta función está destinada principalmente para aplicaciones de 64 bits. Si una aplicación de 32 bits que se ejecuta en WOW64 llama a la función, la opción dwFilterFlag se ignora y la función proporciona los mismos resultados que la función EnumProcessModules.

Usted puede hacer esto mediante la conversión de su programa de 64 bits, se utiliza un servidor fuera de la proc COM de 64 bits (en concreto utilizando un DLL surrogate), o tener un proceso separado que se comunica. Alternativamente, dependiendo de cuándo se inicia el proceso en relación con su proceso objetivo, puede usar WMI para obtener eventos de carga del módulo. Consulte el evento Win32_ModuleLoadTrace.

Process Explorer, un solo exe de 32 bits, puede mostrarle módulos para procesos de 32 y 64 bits, pero es realmente espeluznante: el exe de 32 bits contiene una versión de 64 bits que se escribe fuera del disco y ejecutado en máquinas de 64 bits.

+0

Usar un COM de 64 bits fuera de proc suena como una buena idea - +1! – snemarch

+0

Gracias, Chris, proporcionas una gran cantidad de información útil. –

+0

En realidad, la memoria de lectura del proceso x64 del proceso de 32 bits [es posible] (https://stackoverflow.com/questions/5714297/is-it-possible-to-read-process-memory-of-a-64-bit -process-from-a-32bit-app/36798492 # 36798492). La enumeración de módulos es algo diferente. Podemos llamar a funciones de 64 bits 'ntdll.dll' y algunas otras bibliotecas pero no de' kernel32.dll' y similares. [Aquí está la lista] (http://msdn.microsoft.com/en-us/library/aa384274%28v=vs.85%29.aspx) de las bibliotecas de 64 bits cargadas en el proceso wow64: only 'ntdll.dll 'contiene alguna carga útil útil. 'kernel32.dll' no está en la lista. – greenpiece

1

Use Windows Management Instrumentation (WMI). Ejemplo (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

Solución para su solicitud tiene algunas intersecciones con la tarea de reading x64 process memory from x86 process. Principalmente, debemos tener en cuenta las funciones NtWow64QueryInformationProcess64 y NtWow64ReadVirtualMemory64 que están presentes en x86 ntdll.dll y están diseñadas específicamente para obtener información sobre el proceso x64 del x86.

También deberíamos conocer algunas dependencias entre las estructuras del sistema operativo.

PROCESS_BASIC_INFORMATION contiene la dirección de PEB. PEB son las siglas de Process Environment Block. Contiene la dirección de la estructura PEB_LDR_DATA. A su vez, contiene la dirección de la primera estructura LDR_DATA_TABLE_ENTRY en la cadena LIST_ENTRY. LDR_DATA_TABLE_ENTRY contiene un enlace a la siguiente LDR_DATA_TABLE_ENTRY.

Ejemplo de examinar esta información en 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" 
... 

Pasos que deben tomarse en el código son los siguientes:

  1. Obtener identificador de proceso con la llamada habitual a OpenProcess función.
  2. Lea PROCESS_BASIC_INFORMATION estructura con llamada a NtWow64QueryInformationProcess64.
  3. Obtener la dirección de PEB que está presente en la estructura PROCESS_BASIC_INFORMATION.
  4. Lea PEB estructura con llamada a NtWow64ReadVirtualMemory64.
  5. Obtener la dirección de PEB_LDR_DATA estructura.
  6. Lea PEB_LDR_DATA estructura y obtenga la dirección del primer elemento LDR_DATA_TABLE_ENTRY.
  7. Continúa leyendo la memoria para el elemento LDR_DATA_TABLE_ENTRY mientras que la dirección del siguiente elemento no es igual a la dirección del primer elemento.

En el paso 7 también leemos tampón de UNICODE_STRING (que reside en LDR_DATA_TABLE_ENTRY) para obtener nombre de módulo actual.

El código se proporciona a continuación. Se compone de dos archivos, main.cpp y 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); 

Si usted está interesado en las definiciones iniciales de la estructura - la mejor manera que he descubierto es que Descargue símbolos para WinDbg y luego observe el diseño de las estructuras en este depurador. Puedes ver la muestra en esta publicación de arriba.

Cuestiones relacionadas