【问题标题】:How to enum modules in a 64bit process from a 32bit WOW process如何从 32 位 WOW 进程枚举 64 位进程中的模块
【发布时间】:2011-04-17 14:55:45
【问题描述】:

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

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

至于 EnumProcessModulesEx 和 CreateToolhelp32Snapshot。

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

谢谢。

【问题讨论】:

    标签: windows process 32bit-64bit wow64


    【解决方案1】:

    如果不使用未记录的 API,您将无法做到这一点。一般来说,由于地址空间的差异,从 32 位进程中读取 64 位进程的内存是行不通的。

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

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

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

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

    【讨论】:

    • 谢谢,克里斯,您提供了大量有用的信息。
    • 实际是从32位进程is possible读取x64进程的内存。枚举模块有点不同。我们可以从 64 位 ntdll.dll 和其他一些库调用函数,但不能从 kernel32.dll 等调用函数。 Here is the list of 64 位库加载到 wow64 进程中:只有 ntdll.dll 包含一些有用的有效负载。 kernel32.dll 不在列表中。
    【解决方案2】:

    使用 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;
    

    【讨论】:

      【解决方案3】:

      您的请求的解决方案与reading x64 process memory from x86 process 的任务有一些交集。主要是,我们应该知道函数 NtWow64QueryInformationProcess64NtWow64ReadVirtualMemory64 存在于 x86 ntdll.dll 中,专门用于从 x86 获取有关 x64 进程的信息。

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

      PROCESS_BASIC_INFORMATION 包含PEB 的地址。 PEB 代表进程环境块。它包含PEB_LDR_DATA 结构的地址。它又包含LIST_ENTRY 链中第一个LDR_DATA_TABLE_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. 通过调用NtWow64ReadVirtualMemory64 读取PEB 结构。
      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.cppos_structs.hpp

      ma​​in.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 的符号,然后在此调试器中观察结构的布局。您可以在上面的这篇文章中看到示例。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-21
        • 2011-01-01
        • 1970-01-01
        • 2022-01-20
        • 2013-03-11
        • 2012-06-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多