2010-09-27 22 views
15

Tôi có một yêu cầu để lấy tất cả các module của một quá trình 64bit trong một 32bit WOW quá trình trong Windows, EnumProcessModules sẽ thất bại như đã mô tả:Làm thế nào để enum module trong một quá trình 64bit từ một 32bit WOW xử lý

Nếu đây chức năng được gọi từ một ứng dụng 32 bit chạy trên WOW64, nó chỉ có thể liệt kê các mô-đun của một quá trình 32-bit. Nếu quá trình này là một quá trình 64 bit, chức năng này không thành công và mã lỗi cuối cùng là ERROR_PARTIAL_COPY (299).

Vì vậy, để EnumProcessModulesEx và CreateToolhelp32Snapshot.

Bạn có ý tưởng nào về cách đạt được điều đó không?

Cảm ơn.

Trả lời

15

Nếu không tham gia vào các API không có giấy tờ, bạn không thể thực hiện việc này. Nói chung, việc đọc bộ nhớ 64-bit 'quá trình từ một quá trình 32-bit sẽ không hoạt động do sự khác biệt về không gian địa chỉ.

EnumProcessModulesEx, trong đó có LIST_MODULES_32BITLIST_MODULES_64BIT cờ lọc, có điều này để nói:

chức năng này được thiết kế chủ yếu cho các ứng dụng 64-bit. Nếu hàm được gọi bởi ứng dụng 32 bit chạy dưới WOW64, tùy chọn dwFilterFlag bị bỏ qua và hàm này cung cấp kết quả tương tự như hàm EnumProcessModules.

Bạn có thể thực hiện việc này bằng cách chuyển đổi chương trình sang 64 bit, sử dụng máy chủ COM 64 bit ngoài (cụ thể bằng cách sử dụng DLL surrogate) hoặc có quá trình riêng biệt mà bạn liên lạc. Ngoài ra, tùy thuộc vào thời điểm quá trình của bạn bắt đầu liên quan đến quá trình nhắm mục tiêu của bạn, bạn có thể sử dụng WMI để nhận các sự kiện tải mô-đun. Xem sự kiện Win32_ModuleLoadTrace.

Process Explorer, một exe 32 bit duy nhất, có thể hiển thị cho bạn mô-đun cho cả quy trình 32 và 64 bit, nhưng thực sự là khói và gương: exe 32 bit chứa phiên bản 64 bit của chính nó ra đĩa và thực hiện trên các máy 64 bit.

+0

Sử dụng COM out-of-proc 64 bit có vẻ như là một ý tưởng hay - +1! – snemarch

+0

Cảm ơn, Chris, bạn cung cấp rất nhiều thông tin hữu ích. –

+0

Trên thực tế đọc bộ nhớ của quá trình x64 từ quá trình 32-bit [có thể] (https://stackoverflow.com/questions/5714297/is-it-possible-to-read-process-memory-of-a-64-bit -process-from-a-32bit-app/36798492 # 36798492). Việc liệt kê các mô-đun có một chút khác biệt. Chúng ta có thể gọi hàm từ 'ntdll.dll' 64-bit và một số thư viện khác nhưng không phải từ' kernel32.dll' và tương tự. [Đây là danh sách] (http://msdn.microsoft.com/en-us/library/aa384274%28v=vs.85%29.aspx) của các thư viện 64 bit được nạp vào quá trình wow64: chỉ 'ntdll.dll 'chứa một số tải trọng hữu ích. 'kernel32.dll' không có trong danh sách. – greenpiece

1

Sử dụng Công cụ quản lý Windows (WMI). Ví dụ (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

Giải pháp cho yêu cầu của bạn có một số giao lộ với nhiệm vụ reading x64 process memory from x86 process. Chủ yếu, chúng ta nên biết các hàm NtWow64QueryInformationProcess64NtWow64ReadVirtualMemory64 có mặt trong x86 ntdll.dll và được thiết kế đặc biệt để nhận thông tin về quy trình x64 từ x86.

Chúng ta cũng nên biết một số phụ thuộc giữa cấu trúc hệ điều hành.

PROCESS_BASIC_INFORMATION chứa địa chỉ PEB. PEB là viết tắt của Process Environment Block. Nó chứa địa chỉ của cấu trúc PEB_LDR_DATA. Nó lần lượt chứa địa chỉ của cấu trúc LDR_DATA_TABLE_ENTRY đầu tiên trong chuỗi LIST_ENTRY. LDR_DATA_TABLE_ENTRY chứa liên kết đến LDR_DATA_TABLE_ENTRY sau đây.

Ví dụ về kiểm tra thông tin này trong 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" 
... 

bước được thực hiện trong mã như sau:

  1. Lấy quá trình xử lý với cuộc gọi thông thường để OpenProcess chức năng.
  2. Đọc cấu trúc PROCESS_BASIC_INFORMATION có gọi tới NtWow64QueryInformationProcess64.
  3. Nhận địa chỉ PEB có mặt trong cấu trúc PROCESS_BASIC_INFORMATION.
  4. Đọc cấu trúc PEB có gọi tới NtWow64ReadVirtualMemory64.
  5. Nhận địa chỉ của cấu trúc PEB_LDR_DATA.
  6. Đọc PEB_LDR_DATA cấu trúc và nhận địa chỉ của phần tử LDR_DATA_TABLE_ENTRY đầu tiên.
  7. Tiếp tục đọc bộ nhớ cho LDR_DATA_TABLE_ENTRY phần tử trong khi địa chỉ của phần tử tiếp theo không bằng địa chỉ của phần tử đầu tiên.

Ở bước 7, chúng tôi cũng đọc bộ đệm UNICODE_STRING (nằm ở LDR_DATA_TABLE_ENTRY) để lấy tên mô-đun hiện tại.

Mã được cung cấp bên dưới. Nó bao gồm hai tệp, main.cppos_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); 

Nếu bạn quan tâm trong định nghĩa cấu trúc ban đầu - cách tốt nhất mà tôi đã tìm ra là để tải xuống biểu tượng cho WinDbg và sau đó xem bố cục của cấu trúc trong trình gỡ lỗi này. Bạn có thể xem mẫu trong bài đăng này ở trên.