Skip to content

Commit d275ad3

Browse files
committed
use psapi to locate module addresses, version 1.1.7
1 parent c5dcea7 commit d275ad3

File tree

7 files changed

+145
-66
lines changed

7 files changed

+145
-66
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ repositories {
2323
}
2424
2525
dependencies {
26-
implementation 'com.github.LabyStudio:java-spotify-api:1.1.6:all'
26+
implementation 'com.github.LabyStudio:java-spotify-api:1.1.7:all'
2727
}
2828
```
2929

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66
group 'de.labystudio'
7-
version '1.1.6'
7+
version '1.1.7'
88

99
compileJava {
1010
sourceCompatibility = '1.8'

src/main/java/de/labystudio/spotifyapi/platform/windows/api/WinApi.java

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.sun.jna.platform.win32.WinUser;
1010
import com.sun.jna.ptr.IntByReference;
1111
import de.labystudio.spotifyapi.platform.windows.api.jna.Kernel32;
12+
import de.labystudio.spotifyapi.platform.windows.api.jna.Psapi;
1213
import de.labystudio.spotifyapi.platform.windows.api.jna.Tlhelp32;
1314

1415
import java.util.ArrayList;
@@ -108,44 +109,76 @@ default String getWindowTitle(WinDef.HWND window) {
108109
return Native.toString(Arrays.copyOf(buffer, length));
109110
}
110111

111-
default Map<Long, Long> getModules(long pid) {
112-
Map<Long, Long> map = new HashMap<>();
112+
default Map<String, Psapi.ModuleInfo> getModules(WinNT.HANDLE handle) {
113+
Map<String, Psapi.ModuleInfo> modules = new HashMap<>();
113114

114-
WinNT.HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(
115-
Tlhelp32.TH32CS_SNAPMODULE,
116-
new WinDef.DWORD(pid)
115+
Psapi psapi = Psapi.INSTANCE;
116+
117+
// Get the list of modules
118+
Pointer[] moduleHandles = new Pointer[1024];
119+
psapi.EnumProcessModulesEx(
120+
handle,
121+
moduleHandles,
122+
moduleHandles.length,
123+
null,
124+
Psapi.ModuleFilter.X32BIT
117125
);
118-
if (snapshot == null) {
119-
return map;
120-
}
121126

122-
Tlhelp32.MODULEENTRY32W moduleEntry = new Tlhelp32.MODULEENTRY32W.ByReference();
123-
while (Kernel32.INSTANCE.Module32NextW(snapshot, moduleEntry)) {
124-
map.put(Pointer.nativeValue(moduleEntry.modBaseAddr), moduleEntry.modBaseSize.longValue());
127+
// Iterate over all modules
128+
for (Pointer moduleHandle : moduleHandles) {
129+
if (moduleHandle == null) {
130+
break;
131+
}
132+
133+
// Get module name
134+
char[] characters = new char[1024];
135+
int length = psapi.GetModuleBaseName(handle, moduleHandle, characters, characters.length);
136+
String moduleName = new String(characters, 0, length);
137+
138+
// Get module info
139+
Psapi.ModuleInfo moduleInfo = new Psapi.ModuleInfo();
140+
psapi.GetModuleInformation(handle, moduleHandle, moduleInfo, moduleInfo.size());
141+
modules.put(moduleName, moduleInfo);
125142
}
126-
Kernel32.INSTANCE.CloseHandle(snapshot);
127-
return map;
143+
144+
return modules;
128145
}
129146

130-
default long getModuleAddress(long pid, String moduleName) {
131-
WinNT.HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(
132-
Tlhelp32.TH32CS_SNAPMODULE,
133-
new WinDef.DWORD(pid)
147+
default Psapi.ModuleInfo getModuleInfo(WinNT.HANDLE handle, String moduleName) {
148+
Psapi psapi = Psapi.INSTANCE;
149+
150+
// Get the list of modules
151+
Pointer[] moduleHandles = new Pointer[1024];
152+
psapi.EnumProcessModulesEx(
153+
handle,
154+
moduleHandles,
155+
moduleHandles.length,
156+
null,
157+
Psapi.ModuleFilter.X32BIT
134158
);
135-
if (snapshot == null) {
136-
return 0;
137-
}
138159

139-
Tlhelp32.MODULEENTRY32W moduleEntry = new Tlhelp32.MODULEENTRY32W.ByReference();
140-
while (Kernel32.INSTANCE.Module32NextW(snapshot, moduleEntry)) {
141-
String name = Native.toString(moduleEntry.szModule);
142-
if (name.equals(moduleName)) {
143-
Kernel32.INSTANCE.CloseHandle(snapshot);
144-
return Pointer.nativeValue(moduleEntry.modBaseAddr);
160+
// Iterate over all modules
161+
for (Pointer moduleHandle : moduleHandles) {
162+
if (moduleHandle == null) {
163+
break;
164+
}
165+
166+
// Get module name
167+
char[] characters = new char[1024];
168+
int length = psapi.GetModuleBaseName(handle, moduleHandle, characters, characters.length);
169+
String entryModuleName = new String(characters, 0, length);
170+
171+
// Compare with the name we are looking for
172+
if (entryModuleName.equals(moduleName)) {
173+
174+
// Get module info
175+
Psapi.ModuleInfo moduleInfo = new Psapi.ModuleInfo();
176+
psapi.GetModuleInformation(handle, moduleHandle, moduleInfo, moduleInfo.size());
177+
178+
return moduleInfo;
145179
}
146180
}
147-
Kernel32.INSTANCE.CloseHandle(snapshot);
148-
return -1;
181+
return null;
149182
}
150183

151184
default void pressKey(int keyCode) {

src/main/java/de/labystudio/spotifyapi/platform/windows/api/WinProcess.java

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.sun.jna.platform.win32.WinNT;
77
import com.sun.jna.ptr.IntByReference;
88
import de.labystudio.spotifyapi.platform.windows.api.jna.Kernel32;
9+
import de.labystudio.spotifyapi.platform.windows.api.jna.Psapi;
910

1011
import java.util.Map;
1112

@@ -21,8 +22,6 @@ public class WinProcess implements WinApi {
2122
protected final WinNT.HANDLE handle;
2223
protected final WinDef.HWND window;
2324

24-
protected final long maxContentAddress;
25-
2625
/**
2726
* Creates a new instance of the {@link WinProcess} class.
2827
*
@@ -44,8 +43,6 @@ public WinProcess(String executableName) {
4443
if (this.getWindowTitle().isEmpty()) {
4544
throw new IllegalStateException("Window for process " + this.processId + " not found");
4645
}
47-
48-
this.maxContentAddress = this.getModuleAddress("wow64cpu.dll");
4946
}
5047

5148
/**
@@ -231,7 +228,7 @@ public long findAddressOfText(long start, String text, int index) {
231228
* @return The address of the text at the given index
232229
*/
233230
public long findAddressOfText(long start, String text, SearchCondition condition) {
234-
return this.findInMemory(start, this.maxContentAddress, text.getBytes(), condition);
231+
return this.findInMemory(start, Integer.MAX_VALUE, text.getBytes(), condition);
235232
}
236233

237234
/**
@@ -271,22 +268,22 @@ public long findAddressUsingRules(SearchRule... rules) {
271268
}
272269

273270
/**
274-
* Find the base address of the given module name.
271+
* Get the module information of the given module name.
275272
*
276273
* @param moduleName The name of the module.
277-
* @return The base address of the module.
274+
* @return Information of the given module
278275
*/
279-
public long getModuleAddress(String moduleName) {
280-
return this.getModuleAddress(this.processId, moduleName);
276+
public Psapi.ModuleInfo getModuleInfo(String moduleName) {
277+
return this.getModuleInfo(this.handle, moduleName);
281278
}
282279

283280
/**
284-
* Collect all module addresses and their size of the given process.
281+
* Collect all module names and address information.
285282
*
286-
* @return A list of module addresses and their size.
283+
* @return A map of all modules with their name and address information.
287284
*/
288-
public Map<Long, Long> getModules() {
289-
return this.getModules(this.processId);
285+
public Map<String, Psapi.ModuleInfo> getModules() {
286+
return this.getModules(this.handle);
290287
}
291288

292289
/**
@@ -296,8 +293,8 @@ public Map<Long, Long> getModules() {
296293
*/
297294
public long getFirstModuleAddress() {
298295
long minAddress = Long.MAX_VALUE;
299-
for (Map.Entry<Long, Long> module : this.getModules(this.processId).entrySet()) {
300-
minAddress = Math.min(minAddress, module.getKey() + module.getValue());
296+
for (Map.Entry<String, Psapi.ModuleInfo> module : this.getModules().entrySet()) {
297+
minAddress = Math.min(minAddress, module.getValue().getBaseOfDll());
301298
}
302299
return minAddress;
303300
}
@@ -310,8 +307,8 @@ public long getFirstModuleAddress() {
310307
*/
311308
public long getMaxProcessAddress() {
312309
long maxAddress = 0;
313-
for (Map.Entry<Long, Long> module : this.getModules(this.processId).entrySet()) {
314-
maxAddress = Math.max(maxAddress, module.getKey() + module.getValue());
310+
for (Map.Entry<String, Psapi.ModuleInfo> module : this.getModules().entrySet()) {
311+
maxAddress = Math.max(maxAddress, module.getValue().getBaseOfDll() + module.getValue().getSizeOfImage());
315312
}
316313
return maxAddress;
317314
}
@@ -359,15 +356,6 @@ public void close() {
359356
Kernel32.INSTANCE.CloseHandle(this.handle);
360357
}
361358

362-
/**
363-
* Get the highest memory address with relevant content.
364-
*
365-
* @return The highest memory address with relevant content.
366-
*/
367-
public long getMaxContentAddress() {
368-
return this.maxContentAddress;
369-
}
370-
371359
/**
372360
* Search condition function to check if the address matches additional criteria.
373361
*/
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package de.labystudio.spotifyapi.platform.windows.api.jna;
2+
3+
import com.sun.jna.Native;
4+
import com.sun.jna.Pointer;
5+
import com.sun.jna.Structure;
6+
import com.sun.jna.platform.win32.WinNT;
7+
import com.sun.jna.ptr.IntByReference;
8+
import com.sun.jna.win32.StdCallLibrary;
9+
import com.sun.jna.win32.W32APIOptions;
10+
11+
import java.util.ArrayList;
12+
import java.util.Arrays;
13+
import java.util.List;
14+
15+
public interface Psapi extends WinNT, StdCallLibrary {
16+
17+
Psapi INSTANCE = Native.loadLibrary("psapi", Psapi.class, W32APIOptions.UNICODE_OPTIONS);
18+
19+
boolean EnumProcessModulesEx(HANDLE hProcess, Pointer[] lphModule, int cb, IntByReference lpcbNeeded, int dwFilterFlag);
20+
21+
int GetModuleBaseName(HANDLE hProcess, Pointer hModule, char[] lpBaseName, int nSize);
22+
23+
boolean GetModuleInformation(HANDLE hProcess, Pointer hModule, ModuleInfo moduleInfo, int size);
24+
25+
class ModuleFilter {
26+
public static final int NONE = 0x00;
27+
public static final int X32BIT = 0x01;
28+
public static final int X64BIT = 0x02;
29+
public static final int ALL = 0x03;
30+
}
31+
32+
class ModuleInfo extends Structure {
33+
34+
public Pointer BaseOfDll;
35+
public int SizeOfImage;
36+
public Pointer EntryPoint;
37+
38+
public long getBaseOfDll() {
39+
return Pointer.nativeValue(this.BaseOfDll);
40+
}
41+
42+
public long getEntryPoint() {
43+
return Pointer.nativeValue(this.EntryPoint);
44+
}
45+
46+
public int getSizeOfImage() {
47+
return this.SizeOfImage;
48+
}
49+
50+
@Override
51+
protected List<String> getFieldOrder() {
52+
return new ArrayList<>(Arrays.asList("BaseOfDll", "SizeOfImage", "EntryPoint"));
53+
}
54+
}
55+
}

src/main/java/de/labystudio/spotifyapi/platform/windows/api/spotify/SpotifyProcess.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package de.labystudio.spotifyapi.platform.windows.api.spotify;
22

33
import de.labystudio.spotifyapi.platform.windows.api.WinProcess;
4+
import de.labystudio.spotifyapi.platform.windows.api.jna.Psapi;
45
import de.labystudio.spotifyapi.platform.windows.api.playback.PlaybackAccessor;
56

67
/**
@@ -13,7 +14,6 @@ public class SpotifyProcess extends WinProcess {
1314
private static final boolean DEBUG = System.getProperty("SPOTIFY_API_DEBUG") != null;
1415

1516
// Spotify track id
16-
private static final String CHROME_ELF_DLL_FUNCTION = "DumpHungProcessWithPtype_ExportThunk";
1717
private static final String PREFIX_SPOTIFY_TRACK = "spotify:track:";
1818

1919
// Spotify playback
@@ -41,8 +41,13 @@ public SpotifyProcess() {
4141

4242
long timeScanStart = System.currentTimeMillis();
4343

44+
Psapi.ModuleInfo chromeElf = this.getModuleInfo("chrome_elf.dll");
45+
if (chromeElf == null) {
46+
throw new IllegalStateException("Could not find chrome_elf.dll module");
47+
}
48+
4449
// Find address of track id (Located in the chrome_elf.dll module)
45-
long chromeElfAddress = this.findAddressOfText(0, CHROME_ELF_DLL_FUNCTION, 1);
50+
long chromeElfAddress = chromeElf.getBaseOfDll();
4651
this.addressTrackId = this.findAddressOfText(chromeElfAddress, PREFIX_SPOTIFY_TRACK, 0);
4752

4853
if (this.addressTrackId == -1 || !this.isTrackIdValid(this.getTrackId())) {
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import de.labystudio.spotifyapi.platform.windows.api.WinProcess;
2+
import de.labystudio.spotifyapi.platform.windows.api.jna.Psapi;
23
import de.labystudio.spotifyapi.platform.windows.api.playback.PlaybackAccessor;
34

45
public class SpotifyProcessTest {
@@ -7,13 +8,11 @@ public class SpotifyProcessTest {
78

89
public static void main(String[] args) {
910
WinProcess process = new WinProcess("Spotify.exe");
11+
Psapi.ModuleInfo moduleInfo = process.getModuleInfo("chrome_elf.dll");
12+
System.out.println("chrome_elf.dll address: 0x" + Long.toHexString(moduleInfo.getBaseOfDll()));
1013

11-
long addressTrackId = process.findAddressOfText(
12-
process.getMaxContentAddress() / 2,
13-
"spotify:track:",
14-
(address, index) -> process.hasBytes(address + 1028, 0xDC, 0xA1)
15-
);
16-
System.out.println("Track Id Address: " + Long.toHexString(addressTrackId));
14+
long addressTrackId = process.findAddressOfText(moduleInfo.getBaseOfDll(), "spotify:track:", 0);
15+
System.out.println("Track Id Address: 0x" + Long.toHexString(addressTrackId));
1716

1817
long addressPlayBack = process.findInMemory(
1918
0,
@@ -24,8 +23,7 @@ public static void main(String[] args) {
2423
return accessor.isValid();
2524
}
2625
);
27-
System.out.println("Playback Address: " + Long.toHexString(addressPlayBack));
28-
26+
System.out.println("Playback Address: 0x" + Long.toHexString(addressPlayBack));
2927
}
3028

3129
}

0 commit comments

Comments
 (0)