2010-09-27 20 views
15

Mam wymaganie, aby pobrać wszystkie moduły procesu 64bit na 32bit WOW procesów w systemie Windows, EnumProcessModules zawiedzie jak opisano:Jak ENUM modułów w procesie 64-bitowym z 32bit WOW przetwarzać

Jeśli ta Funkcja jest wywoływana z 32-bitowej aplikacji działającej na WOW64, może jedynie wyliczyć moduły 32-bitowego procesu. Jeśli proces jest procesem 64-bitowym, ta funkcja kończy się niepowodzeniem, a ostatnim błędem jest ERROR_PARTIAL_COPY (299).

Tak jak w przypadku EnumProcessModulesEx i CreateToolhelp32Snapshot.

Czy masz pojęcie, jak to osiągnąć?

Dzięki.

Odpowiedz

15

Bez wchodzenia w nieudokumentowane interfejsy API, nie można tego zrobić. Ogólnie rzecz biorąc, odczyt 64-bitowej pamięci procesowej z 32-bitowego procesu nie będzie działał ze względu na różnice w przestrzeni adresowej.

EnumProcessModulesEx, który ma LIST_MODULES_32BIT i LIST_MODULES_64BIT flagi filtrów, ma do powiedzenia:

Funkcja ta jest przeznaczona przede wszystkim dla aplikacji 64-bitowych. Jeśli funkcja jest wywoływana przez 32-bitową aplikację działającą pod WOW64, opcja dwFilterFlag jest ignorowana, a funkcja zapewnia takie same wyniki, jak funkcja EnumProcessModules.

Można to zrobić poprzez przekształcenie swój program 64-bit, używając out-of-proc 64-bitowego serwera COM (konkretnie przy użyciu DLL surrogate) lub konieczności oddzielnego procesu, które komunikować. Alternatywnie, w zależności od czasu rozpoczęcia procesu w stosunku do procesu docelowego, można użyć usługi WMI, aby uzyskać zdarzenia ładowania modułów. Zobacz wydarzenie Win32_ModuleLoadTrace.

Process Explorer, pojedynczy 32-bitowy exe, może pokazać moduły dla procesów zarówno 32-, jak i 64-bitowych, ale to naprawdę dym i lustra: 32-bitowy kod exe zawiera 64-bitową wersję samego siebie, która zostanie napisana na dysk i wykonywane na 64-bitowych komputerach.

+0

Używanie 64-bitowej wersji z procem COM brzmi jak dobry pomysł - +1! – snemarch

+0

Dzięki, Chris, podajesz wiele użytecznych informacji. –

+0

Właściwie odczytywanie pamięci procesu x64 z 32-bitowego procesu [jest możliwe] (https://stackoverflow.com/questions/5714297/is-it-possible-to-read-process-memory-a-64-bit -proces-from-a-32bit-app/36798492 # 36798492). Wyliczanie modułów to trochę inna sprawa. Możemy wywoływać funkcje z 64-bitowego 'ntdll.dll' oraz kilku innych bibliotek, ale nie z' kernel32.dll' i tym podobnych. [Oto lista] (http://msdn.microsoft.com/en-us/library/aa384274%28v=vs.85%29.aspx) 64-bitowych bibliotek załadowanych do procesu wow64: tylko 'ntdll.dll 'zawiera trochę użytecznego ładunku. 'kernel32.dll' nie znajduje się na liście. – greenpiece

1

Użyj Windows Management Instrumentation (WMI). Przykład (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

Rozwiązanie na żądanie ma kilka skrzyżowań z zadaniem reading x64 process memory from x86 process. Głównie powinniśmy być świadomi funkcji NtWow64QueryInformationProcess64 i NtWow64ReadVirtualMemory64, które są obecne w x86 ntdll.dll i zostały zaprojektowane specjalnie w celu uzyskania informacji o procesie x64 z x86.

Powinniśmy również znać pewne zależności między strukturami systemu operacyjnego.

PROCESS_BASIC_INFORMATION zawiera adres PEB. PEB oznacza Blok środowiska procesu. Zawiera adres struktury PEB_LDR_DATA. To z kolei zawiera adres pierwszej struktury LDR_DATA_TABLE_ENTRY w łańcuchu LIST_ENTRY. LDR_DATA_TABLE_ENTRY zawiera link do następującego LDR_DATA_TABLE_ENTRY.

Przykład badając tę ​​informację w 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" 
... 

Kroki, które należy podjąć w kodzie są następujące:

  1. uzyskać dojście procesu ze zwykłej rozmowy do OpenProcess funkcji.
  2. Przeczytaj strukturę PROCESS_BASIC_INFORMATION z połączeniem z numerem NtWow64QueryInformationProcess64.
  3. Uzyskaj adres PEB, który jest obecny w strukturze PROCESS_BASIC_INFORMATION.
  4. Przeczytaj strukturę PEB z numerem telefonu NtWow64ReadVirtualMemory64.
  5. Uzyskaj adres struktury PEB_LDR_DATA.
  6. Przeczytaj PEB_LDR_DATA strukturę i uzyskaj adres pierwszego elementu LDR_DATA_TABLE_ENTRY.
  7. Kontynuuj czytanie pamięci dla elementu LDR_DATA_TABLE_ENTRY, podczas gdy adres następnego elementu nie jest równy adresowi pierwszego elementu.

W kroku 7 czytamy również bufor UNICODE_STRING (który rezyduje w LDR_DATA_TABLE_ENTRY), aby otrzymać aktualną nazwę modułu.

Kod znajduje się poniżej. Składa się z dwóch plików: main.cpp i 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); 

Jeśli jesteś zainteresowany w początkowych definicji struktury - najlepszy sposób mam zorientowali się, jest pobierz symbole dla WinDbg, a następnie obejrzyj układ struktur w tym debugerze. Możesz zobaczyć próbkę w tym poście powyżej.