Как перечислить модули в 64-битном процессе из 32-битного процесса WOW - PullRequest
18 голосов
/ 27 сентября 2010

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

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

Для EnumProcessModulesEx и CreateToolhelp32Snapshot.

Есть ли у вас какие-либоИдея о том, как этого добиться?

Спасибо.

Ответы [ 3 ]

16 голосов
/ 27 сентября 2010

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

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

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

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

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

4 голосов
/ 12 июня 2017

Решение по вашему запросу имеет некоторые пересечения с задачей чтение памяти процесса 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"
...

В коде необходимо выполнить следующие шаги:

  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, а затем посмотреть расположение структур в этом отладчике. Вы можете увидеть образец в этом посте выше.

1 голос
/ 04 февраля 2016

Использовать инструментарий управления 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;
...