كيفية تعداد الوحدات النمطية في عملية 64 بت من عملية واو 32 بت

StackOverflow https://stackoverflow.com/questions/3801517

سؤال

لدي شرط لاسترداد جميع الوحدات النمطية لعملية 64 بت في عملية واو 32 بت في Windows ، EnumprocessModules سوف تفشل كما هو موضح:

إذا تم استدعاء هذه الوظيفة من تطبيق 32 بت يعمل على WOW64 ، فيمكنه فقط تعداد وحدات عملية 32 بت. إذا كانت العملية عبارة عن عملية 64 بت ، فإن هذه الوظيفة تفشل وآخر رمز الخطأ هو error_partial_copy (299).

وذلك ل enumProcessModulesex و CreateToolHelp32Snapshot.

هل لديك أي فكرة عن كيفية تحقيق ذلك؟

شكرًا.

هل كانت مفيدة؟

المحلول

دون الذهاب إلى واجهات برمجة التطبيقات غير الموثقة ، لا يمكنك القيام بذلك. بشكل عام ، لن تعمل قراءة ذاكرة عملية 64 بت من عملية 32 بت بسبب اختلافات مساحة العنوان.

EnumProcessModulesEx, الذي لديه LIST_MODULES_32BIT و LIST_MODULES_64BIT فلتر أعلام ، لديه هذا لقول:

هذه الوظيفة مخصصة في المقام الأول لتطبيقات 64 بت. إذا تم استدعاء الوظيفة بواسطة تطبيق 32 بت يعمل ضمن WOW64 ، يتم تجاهل خيار DWFilterFlag وتوفر الوظيفة نفس النتائج مثل وظيفة EnumProcessModules.

يمكنك القيام بذلك عن طريق تحويل برنامجك إلى 64 بت ، باستخدام خادم COM 64 بت خارجية (على وجه التحديد باستخدام أ DLL بديل) ، أو وجود عملية منفصلة تتواصل معها. بدلاً من ذلك ، بناءً على متى تبدأ عمليتك بالنسبة إلى العملية المستهدفة ، يمكنك استخدام WMI للحصول على أحداث تحميل الوحدة النمطية. انظر Win32_ModuleLoadTrace حدث.

عملية المستكشف, ، EXE 32 بت واحد ، يمكن أن يوضح لك وحدات لكل من العمليات 32 و 64 بت ، لكنها حقًا دخان ومرايا: يحتوي EXE 32 بت على نسخة 64 بت من نفسها يتم كتابتها إلى القرص وتنفيذها على آلات 64 بت.

نصائح أخرى

الحل لطلبك لديه بعض التقاطعات مع مهمة قراءة ذاكرة عملية 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 ثم شاهد تصميم هياكل في هذا التصحيح. يمكنك رؤية العينة في هذا المنشور أعلاه.

استخدم أجهزة إدارة 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;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top