使用JNA获取特定进程的dll的基地址

Getting base address of dll of specific process using JNA

已更新:请参阅问题机器人的更新


我想获取 war3.exe 进程内的 game.dll 的基地址。 我正在尝试通过 JNA 库版本 5.9.0 来实现,但没有成功。

我遇到的问题:我无法从 war3.exe 进程中获取 game.dll 模块。 我尝试使用:

int pid = getProcessId("Warcraft III");
openProcess(PROCESS_ALL_ACCESS, pid);
WinDef.HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle("game.dll")

但是hMod的结果是null

我也尝试获取 war3.exe 进程拥有的所有模块。如您所见,它仅包含 5 个模块,并且不包含 game.dll。但是当我通过 Process Explorer 打开 war3.exe 时,我看到的肯定不止 5 个。

使用 Intellij Idea 执行:

取自 Process Explorer:

请分享您的意见和想法,为什么我只从 IDE 获得 5 个模块。

任何有关如何通过 JNA 获取 game.dll 模块及其基地址的建议将不胜感激。


更新: 根据 Remy 的回答,我又尝试了 EnumProcessModules()。 这是我的代码片段:

import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Psapi;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.ptr.IntByReference;

import java.util.Arrays;
import java.util.List;

import static com.sun.jna.platform.win32.WinNT.PROCESS_ALL_ACCESS;
import static handler.memory.MemoryHandler.openProcess;

public class MemoryHandler {
    static final User32 user32 = User32.INSTANCE;
    static final Psapi psapi = Psapi.INSTANCE;

    public static void main(String[] args) {
        int pid = getProcessId("Warcraft III");
        HANDLE process = openProcess(PROCESS_ALL_ACCESS, pid);

        HMODULE[] hMods = new HMODULE[1024];
        psapi.EnumProcessModules(process, hMods, hMods.length, new IntByReference(1024));

        List<HMODULE> hModList = Arrays.asList(hMods);
        hModList.forEach(hMod ->
                System.out.println(Pointer.nativeValue(hMod.getPointer())));
    }

    public static int getProcessId(String window) {
        IntByReference pid = new IntByReference(0);
        user32.GetWindowThreadProcessId(user32.FindWindow(null, window), pid);

        return pid.getValue();
    }
}

结果如下:

据我所知,我得到了一些指示。但是我应该如何理解他们中哪一个与game.dll相关呢?我假设我应该以某种方式获得模块列表,在那里我可以看到它们的名称和基址。

此外,如果我将 System.out.println(Pointer.nativeValue(hMod.getPointer()))); 更改为 hModList.forEach(System.out::println);,我会看到以下指针和大量空值(大约 1000 个)。

这些地址是否包含game.dll的地址?

GetModuleHandle() 只查看调用进程。由于game.dll没有在自己的进程中加载​​,所以GetModuleHandle()找不到。

要查找在另一个进程中加载​​的模块,您需要使用:

Kernel32Utils.getModules() 使用 CreateToolhelp32Snapshot(TH32CS_SNAPMODULE),因此如果您的 Java 应用是 运行 作为 64 位应用,那么它将仅枚举 64 位模块。但是 war3.exe 在您的屏幕截图中是 运行 作为 32 位进程,因此如果您在 64 位进程中使用 CreateToolhelp32Snapshot() 那么您需要使用 TH32CS_SNAPMODULE32 代替。

更新:

正如我上面提到的,如果你采用 EnumProcessModules() 方法,你可以使用 GetModuleFileNameEx() 来确定每个模块的文件名。这样,您就可以找到 game.dll.

的模块

更重要的是:

  • 您缺少对每个系统调用的错误处理。始终测试 return 失败值。

  • 本身不是错误,但您确实不应该使用 openProcess() 请求 PROCESS_ALL_ACCESS 权限。仅请求您实际需要的权限,仅此而已。在这种情况下,请改用 PROCESS_QUERY_INFORMATION | PROCESS_VM_READ

  • 你不是在查看 EnumProcessModules() 的第 4 个参数的输出来了解数组中实际存储了多少个模块。

  • 你输入的EnumProcessModules()第3、4个参数的值是错误的,需要用bytes表示,而不是元素.

根据 EnumProcessModules() documentation:

cb
The size of the lphModule array, in bytes.

lpcbNeeded
The number of bytes required to store all module handles in the lphModule array.

...

It is a good idea to specify a large array of HMODULE values, because it is hard to predict how many modules there will be in the process at the time you call EnumProcessModules. To determine if the lphModule array is too small to hold all module handles for the process, compare the value returned in lpcbNeeded with the value specified in cb. If lpcbNeeded is greater than cb, increase the size of the array and call EnumProcessModules again.

To determine how many modules were enumerated by the call to EnumProcessModules, divide the resulting value in the lpcbNeeded parameter by sizeof(HMODULE).

最后我找到了解决方案,但在 Java 或 JNA 中找不到。 我使用 C++ 编写了这段代码,我将像 Java.

中的 dll 一样使用它

这是我的 C++ 代码:

#include <conio.h>
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <psapi.h>
using namespace std;

DWORD_PTR GetProcessBaseAddress(DWORD processID)
{
    DWORD_PTR   baseAddress = 0;
    HANDLE      processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
    HMODULE* moduleArray;
    LPBYTE      moduleArrayBytes;
    DWORD       bytesRequired;

    if (processHandle)
    {
        if (EnumProcessModules(processHandle, NULL, 0, &bytesRequired))
        {
            if (bytesRequired)
            {
                moduleArrayBytes = (LPBYTE)LocalAlloc(LPTR, bytesRequired);

                if (moduleArrayBytes)
                {
                    unsigned int moduleCount;

                    moduleCount = bytesRequired / sizeof(HMODULE);
                    moduleArray = (HMODULE*)moduleArrayBytes;

                    if (EnumProcessModules(processHandle, moduleArray, bytesRequired, &bytesRequired))
                    {
                        baseAddress = (DWORD_PTR)moduleArray[0];
                    }

                    LocalFree(moduleArrayBytes);
                }
            }
        }

        CloseHandle(processHandle);
    }

    return baseAddress;
}

DWORD GetProcessId(LPCTSTR ProcessName) // non-conflicting function name
{
    PROCESSENTRY32 pt;
    HANDLE hsnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    pt.dwSize = sizeof(PROCESSENTRY32);
    if (Process32First(hsnap, &pt)) { // must call this first
        do {
            if (!lstrcmpi(pt.szExeFile, ProcessName)) {
                CloseHandle(hsnap);
                return pt.th32ProcessID;
            }
        } while (Process32Next(hsnap, &pt));
    }
    CloseHandle(hsnap); // close handle on failure
    return 0;
}

uintptr_t GetModuleBaseAddress(DWORD procId, const wchar_t* modName)
{
    uintptr_t modBaseAddr = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
    if (hSnap != INVALID_HANDLE_VALUE)
    {
        MODULEENTRY32 modEntry;
        modEntry.dwSize = sizeof(modEntry);
        if (Module32First(hSnap, &modEntry))
        {
            do
            {
                if (!_wcsicmp(modEntry.szModule, modName))
                {
                    modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
                    break;
                }
            } while (Module32Next(hSnap, &modEntry));
        }
    }
    CloseHandle(hSnap);
    return modBaseAddr;
}

int main()
{
    DWORD pid = GetProcessId(TEXT("war3.exe"));
    cout << "Process ID of war3.exe: "<< pid << endl;

    DWORD_PTR war3_exe_base_addr = GetProcessBaseAddress(pid);
    cout <<"Base address of war3.exe: "<< war3_exe_base_addr << endl;

    uintptr_t gameDllBaseAddress = GetModuleBaseAddress(pid, TEXT("game.dll"));
    cout <<"Base address of game.dll: " << gameDllBaseAddress << endl;
}

结果是: