1
votes

I am using JNA and writing some code to enumerate through all of the modules available in a process. I successfully obtain the snapshot handle via CreateToolhelp32Snapshot, however on my first Module32First call, I am getting error code 87 for "Invalid Parameter".

Below is the relevant code that I am using:

Code in question:

   private void getModule() {
        Pointer hSnapshot = this.kernel32.CreateToolhelp32Snapshot(WinAPI.TH32CS_SNAPMODULE, this.processId);

        if(hSnapshot != null) {
            WinAPI.MODULEENTRY32 moduleEntry32 = new WinAPI.MODULEENTRY32();

            moduleEntry32.write(); // Write the struct in memory (for dwSize to be registered)

            Pointer moduleEntry32Pointer = moduleEntry32.getPointer();

            if(this.kernel32.Module32First(hSnapshot, moduleEntry32Pointer)) {
                 // irrelevant stuff here
            }
        }

        System.out.println(this.kernel32.GetLastError()); // Prints out "87"

        this.kernel32.CloseHandle(hSnapshot);

    }

Kernel32 Mapper Class:

    Pointer CreateToolhelp32Snapshot(int dwFlags, int th32ProcessID);

    boolean Module32First(Pointer hSnapshot, Pointer lpme);

    boolean Module32Next(Pointer hSnapshot, Pointer lpme);

ModuleEntry32 Structure:

    public static class MODULEENTRY32 extends Structure {
        public int dwSize = this.size();
        public int th32ModuleID;
        public int th32ProcessID;
        public int GlblcntUsage;
        public int ProccntUsage;
        public Pointer modBaseAddr;
        public int modBaseSize;
        public Pointer hModule;
        public String szModule;
        public String szExePath;

        public MODULEENTRY32() {
            super();
        }

        public MODULEENTRY32(Pointer p) {
            super(p);
        }

        protected List<String> getFieldOrder() {
            return Arrays.asList(new String[] {"dwSize", "th32ModuleID", "th32ProcessID", "GlblcntUsage", "ProccntUsage", "modBaseAddr", "modBaseSize", "hModule", "szModule", "szExePath"});
        }
    }

And finally, this is "WinAPI.TH32CS_SNAPMODULE":

    public static final int TH32CS_SNAPMODULE = 0x00000008;

Note: The process I am enumerating on is already opened with OpenProcess and is valid. The processId is valid as well, and is obtained properly as well.

Any help would be appreciated.

1

1 Answers

1
votes

Your mapping of a pointer as the second argument, while technically correct, requires you to do a lot more overhead with writing it. It is better to simply put the structure type as an argument. Structures are treated as .ByReference when used as function/method arguments, and handle all of the auto-read and write for you. So if you do this, you can omit your write() call:

boolean Module32First(Pointer hSnapshot, WinAPI.MODULEENTRY32 lpme);
boolean Module32Next(Pointer hSnapshot, WinAPI.MODULEENTRY32 lpme);

That said, the comment on your write call points to the root cause here: you need to set the dwSize value to the size of the structure. However, because of the order of initialization of Java Objects, the Structure does not have the information it needs to determine the size when initializing the dwSize variable, so this part is not giving you the correct size:

public int dwSize = this.size();

To solve this, just declare that variable and set the size later on in the constructor, e.g.,

public static class MODULEENTRY32 extends Structure {
    public int dwSize;
    // other declarations
    
    public MODULEENTRY32() {
        super();
        dwSize = size();
    }   
}

In addition your mappings for szModule and szExePath are incorrect. You've only provided Pointers (a total of 16 bytes) in the structure's memory allocation, but these are fixed length character arrays. They should be defined:

public char[] szModule = new char[MAX_MODULE_NAME32 + 1];
public char[] szExePath = new char[MAX_PATH];

Note the char[] mapping is for the Unicode (_W) version of the mapping which is a reasonably safe assumption.

But rather than write yourself, you should use the mappings that already exists in the JNA user-contributed mappings in jna-platform. The MODULEENTRY32W type is already there (yes it uses DWORD so your version is simpler; but you can look at the code to see how they handled the size.) And the Module32FirstW and Module32NextW functions are also mapped. (The W suffix is for wide characters (Unicode) which all modern Windows systems use now.)