windows 如何从 32 位 WOW 进程枚举 64 位进程中的模块

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3801517/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 15:18:57  来源:igfitidea点击:

How to enum modules in a 64bit process from a 32bit WOW process

windowsprocess32bit-64bitwow64

提问by Baiyan Huang

I have a requirement to retrieve all modules of a 64bit process in a 32bit WOW process in Windows, EnumProcessModuleswould fail as described:

我需要在 Windows 的 32 位 WOW 进程中检索 64 位进程的所有模块,EnumProcessModules将失败,如下所述:

If this function is called from a 32-bit application running on WOW64, it can only enumerate the modules of a 32-bit process. If the process is a 64-bit process, this function fails and the last error code is ERROR_PARTIAL_COPY (299).

如果从运行在 WOW64 上的 32 位应用程序调用此函数,则它只能枚举 32 位进程的模块。如果进程是 64 位进程,则此函数失败,最后一个错误代码是 ERROR_PARTIAL_COPY (299)。

So as to EnumProcessModulesEx and CreateToolhelp32Snapshot.

比如 EnumProcessModulesEx 和 CreateToolhelp32Snapshot。

Do you have any idea on how to achieve it?

你对如何实现它有任何想法吗?

Thanks.

谢谢。

采纳答案by Chris Schmich

Without going into undocumented APIs, you can't do this. In general, reading a 64-bit process' memory from a 32-bit process won't work due to the address space differences.

如果不进入未记录的 API,您就无法做到这一点。通常,由于地址空间差异,从 32 位进程读取 64 位进程的内存将不起作用。

EnumProcessModulesEx, which has LIST_MODULES_32BITand LIST_MODULES_64BITfilter flags, has this to say:

EnumProcessModulesEx,它有LIST_MODULES_32BITLIST_MODULES_64BIT过滤器标志,有这样的说法:

This function is intended primarily for 64-bit applications. If the function is called by a 32-bit application running under WOW64, the dwFilterFlag option is ignored and the function provides the same results as the EnumProcessModules function.

此函数主要用于 64 位应用程序。如果该函数由在 WOW64 下运行的 32 位应用程序调用,则 dwFilterFlag 选项将被忽略,并且该函数提供与 EnumProcessModules 函数相同的结果。

You could do this by converting your program to 64-bit, using an out-of-proc 64-bit COM server (specifically using a DLL surrogate), or having a separate process that you communicate with. Alternatively, depending on when your process starts relative to your target process, you could use WMI to get module load events. See the Win32_ModuleLoadTraceevent.

您可以通过将程序转换为 64 位、使用进程外64 位 COM 服务器(特别是使用DLL 代理)或使用单独的进程与之通信来实现此目的。或者,根据您的进程相对于目标进程的启动时间,您可以使用 WMI 来获取模块加载事件。看到Win32_ModuleLoadTrace事件。

Process Explorer, a single 32-bit exe, can show you modules for both 32- and 64-bit processes, but it's really smoke and mirrors: the 32-bit exe contains a 64-bit version of itself that gets written out to disk and executed on 64-bit machines.

Process Explorer是一个 32 位 exe,可以向您显示 32 位和 64 位进程的模块,但它实际上是雾里看花:32 位 exe 包含一个 64 位版本的自身,可以写入磁盘并在 64 位机器上执行。

回答by greenpiece

Solution for your request has some intersections with the task of reading x64 process memory from x86 process. Mainly, we should be aware of functions NtWow64QueryInformationProcess64and NtWow64ReadVirtualMemory64which are present in x86 ntdll.dlland are designed specifically for getting information about x64 process from the x86 one.

您的请求的解决方案与从 x86 进程读取 x64 进程内存的任务有一些交集。主要是,我们应该了解x86 中存在的函数NtWow64QueryInformationProcess64NtWow64ReadVirtualMemory64它们是ntdll.dll专门为从 x86 中获取有关 x64 进程的信息而设计的。

We should also know some dependencies between OS structures.

我们还应该了解操作系统结构之间的一些依赖关系。

PROCESS_BASIC_INFORMATIONcontains address of PEB. PEBstands for Process Environment Block. It contains address of PEB_LDR_DATAstructure. It in turn contains address of the first LDR_DATA_TABLE_ENTRYstructure in the LIST_ENTRYchain. LDR_DATA_TABLE_ENTRYcontains link to the following LDR_DATA_TABLE_ENTRY.

PROCESS_BASIC_INFORMATION包含地址PEBPEB代表进程环境块。它包含PEB_LDR_DATA结构地址。它依次包含链中第一个LDR_DATA_TABLE_ENTRY结构的地址LIST_ENTRYLDR_DATA_TABLE_ENTRY包含指向以下内容的链接LDR_DATA_TABLE_ENTRY

Example of examining this information in WinDbg:

在 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"
...

Steps to be taken in code are the following:

在代码中要采取的步骤如下:

  1. Obtain process handle with usual call to OpenProcessfunction.
  2. Read PROCESS_BASIC_INFORMATIONstructure with call to NtWow64QueryInformationProcess64.
  3. Get address of PEBwhich is present in PROCESS_BASIC_INFORMATIONstructure.
  4. Read PEBstructure with call to NtWow64ReadVirtualMemory64.
  5. Get address of PEB_LDR_DATAstructure.
  6. Read PEB_LDR_DATAstructure and get address of first LDR_DATA_TABLE_ENTRYelement.
  7. Keep on reading memory for LDR_DATA_TABLE_ENTRYelement while the address of the next element is not equal to the address of the first element.
  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当下一个元素的地址不等于第一个元素的地址时,继续读取元素的内存。

At step 7 we also read buffer of UNICODE_STRING(which resides in LDR_DATA_TABLE_ENTRY) to obtain current module name.

在第 7 步,我们还读取了UNICODE_STRING(驻留在LDR_DATA_TABLE_ENTRY)的缓冲区以获取当前模块名称。

The code is provided below. It consists of two files, main.cppand os_structs.hpp.

下面提供了代码。它由两个文件组成,main.cppos_structs.hpp.

main.cpp:

主.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:

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);

If you're interested in initial structure definitions - the best way I've figured out is to download symbols for WinDbg and then watch structures' layout in this debugger. You can see the sample in this post above.

如果您对初始结构定义感兴趣 - 我想出的最好方法是下载 WinDbg 的符号,然后在此调试器中观察结构的布局。您可以在上面的这篇文章中看到示例。

回答by Patrick Timmermans

Use Windows Management Instrumentation (WMI). Example (Delphi):

使用 Windows 管理规范 (WMI)。示例(德尔福):

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;