備忘録

備忘録

C++でWindows上の特定プロセスの全モジュールを取得する方法

Ⅰ. はじめに

タイトルの通り「C++Windows上の特定プロセスの全モジュールを取得する方法」です。

Ⅱ. サンプルプログラム1(EnumProcessModulesExを使用する方法)

#include <windows.h>
#include <Psapi.h>
#include <iostream>

void ShowAllModules(uint32_t processId)
{
  HMODULE hMods[1024];

  auto processHandle = OpenProcess(PROCESS_ALL_ACCESS, false, processId);
  if (processHandle != nullptr)
  {
    DWORD cbNeeded;
    if (EnumProcessModulesEx(processHandle, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
    {
      for (uint32_t i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
      {
        wchar_t filePath[MAX_PATH];

        if (GetModuleFileNameEx(processHandle, hMods[i], filePath, sizeof(filePath) / sizeof(wchar_t)))
        {
          std::wcout << "BaseAddress: 0x" << hMods[i] << std::endl;
          std::wcout << "FilePath: " << filePath << std::endl;
        }
      }
    }
    CloseHandle(processHandle);
  }
}

int main(void)
{
  auto processId = 1234;
  ShowAllModules(processId);

  return 0;
}

Ⅲ. サンプルプログラム2(CreateToolhelp32Snapshotを使用する方法)

#include <windows.h>
#include <Psapi.h>
#include <TlHelp32.h>
#include <iostream>

void ShowAllModules(uint32_t processId)
{
  MODULEENTRY32 moduleEntry32 = { 0 };

  auto processHandle = OpenProcess(PROCESS_ALL_ACCESS, false, processId);
  if (processHandle != nullptr)
  {
    auto hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, processId);
    if (hThreadSnap != INVALID_HANDLE_VALUE)
    {
      moduleEntry32.dwSize = sizeof(MODULEENTRY32);

      if (!Module32First(hThreadSnap, &moduleEntry32))
      {
        std::cout << "failed" << std::endl;
      }
      else
      {
        do
        {
          wchar_t filePath[MAX_PATH];
          GetModuleFileNameEx(processHandle, moduleEntry32.hModule, filePath, MAX_PATH);
          std::wcout << "BaseAddress: 0x" << moduleEntry32.modBaseAddr << std::endl;
          std::wcout << "FilePath: " << filePath << std::endl;
        } while (Module32Next(hThreadSnap, &moduleEntry32));
      }
      CloseHandle(hThreadSnap);
    }
    CloseHandle(processHandle);
  }
}

int main(void)
{
  auto processId = 1234;
  ShowAllModules(processId);

  return 0;
}

Ⅳ. サンプルプログラム3(NtQueryInformationProcessを使用する方法)

※対象がWOW64プロセスの場合はNtWow64QueryInformationProcess64を使用します。

#include <windows.h>
#include <winternl.h>
#include <iostream>

void ShowAllModules(uint32_t processId)
{
  auto moduleHandle = LoadLibrary(L"ntdll.dll");
  if (moduleHandle != nullptr)
  {
    auto pNtQueryInformationProcess = reinterpret_cast<decltype(&NtQueryInformationProcess)>(GetProcAddress(moduleHandle, "NtQueryInformationProcess"));

    auto processHandle = OpenProcess(PROCESS_ALL_ACCESS, false, processId);
    if (processHandle != nullptr)
    {
      PROCESS_BASIC_INFORMATION pbi = { 0 };
      if (NT_SUCCESS(pNtQueryInformationProcess(processHandle, PROCESSINFOCLASS::ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), nullptr)))
      {
        if (pbi.PebBaseAddress != nullptr)
        {
          PEB peb = { 0 };
          ReadProcessMemory(processHandle, pbi.PebBaseAddress, &peb, sizeof(PEB), nullptr);

          PEB_LDR_DATA pebLdrData = { 0 };
          ReadProcessMemory(processHandle, peb.Ldr, &pebLdrData, sizeof(PEB_LDR_DATA), nullptr);

          auto pModuleFirstEntry = pebLdrData.InMemoryOrderModuleList.Flink;
          auto pModuleCurrentEntry = pebLdrData.InMemoryOrderModuleList.Flink;
          do
          {
            auto pLdrDataTableEntry = CONTAINING_RECORD(pModuleCurrentEntry, LDR_DATA_TABLE_ENTRY, LDR_DATA_TABLE_ENTRY::InMemoryOrderLinks);

            LDR_DATA_TABLE_ENTRY ldrDataTableEntry = { 0 };
            ReadProcessMemory(processHandle, pLdrDataTableEntry, &ldrDataTableEntry, sizeof(LDR_DATA_TABLE_ENTRY), nullptr);

            wchar_t str[MAX_PATH];
            ReadProcessMemory(processHandle, ldrDataTableEntry.FullDllName.Buffer, &str, ldrDataTableEntry.FullDllName.Length, nullptr);
            auto filePath = std::wstring(str, ldrDataTableEntry.FullDllName.Length / sizeof(wchar_t));

            if (ldrDataTableEntry.DllBase != nullptr)
            {
              std::wcout << "BaseAddress: 0x" << ldrDataTableEntry.DllBase << std::endl;
              std::wcout << "FilePath: " << filePath << std::endl;
            }

            pModuleCurrentEntry = ldrDataTableEntry.InMemoryOrderLinks.Flink;
          } while (pModuleCurrentEntry != pModuleFirstEntry);
        }
      }
      CloseHandle(processHandle);
    }

    FreeModule(moduleHandle);
  }
}

int main(void)
{
  auto processId = 1234;
  ShowAllModules(processId);

  return 0;
}

Ⅴ. 実行結果

BaseAddress: 0x00007FF699110000
FilePath: C:\WINDOWS\system32\notepad.exe
BaseAddress: 0x00007FFBCE1F0000
FilePath: C:\WINDOWS\SYSTEM32\ntdll.dll
BaseAddress: 0x00007FFBCE0F0000
FilePath: C:\WINDOWS\System32\KERNEL32.DLL
BaseAddress: 0x00007FFBCBC10000
FilePath: C:\WINDOWS\System32\KERNELBASE.dll
...
(以下省略)

Ⅵ. 留意点

  • WOW64プロセスから64bitプロセスの情報を取得する事は出来ません。(参考1)
  • NtQueryInformationProcessは非公開APIなので一般向きでは無いです。
  • 私の環境では全て同じ実行結果が得られました。
    使用しているAPIが異なる為実行結果が異なる可能性があります。
    ※一部モジュールが正しく取得できない等。