Skip to content

Commit

Permalink
Added MemManipulator
Browse files Browse the repository at this point in the history
+[ADD] MemManipulator which can read and write to memory addreses, it
also provides various methods for finding dynamic addresses
+[MOD] Reworked SoliScorer to use Mem-Eater-Bug, it now acts as showcase
example
-[MOV] Moved the package /winapi/api/ to /winapi/
  • Loading branch information
Zabuzard committed Jun 23, 2016
1 parent 83e8c89 commit 280b4b5
Show file tree
Hide file tree
Showing 16 changed files with 534 additions and 210 deletions.
57 changes: 47 additions & 10 deletions src/de/zabuza/memeaterbug/MemEaterBug.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,23 @@
import com.sun.jna.platform.win32.WinNT.HANDLE;

import de.zabuza.memeaterbug.locale.ErrorMessages;
import de.zabuza.memeaterbug.memory.MemManipulator;
import de.zabuza.memeaterbug.util.Masks;
import de.zabuza.memeaterbug.util.SystemProperties;
import de.zabuza.memeaterbug.winapi.api.Process;
import de.zabuza.memeaterbug.winapi.jna.util.User32Util;
import de.zabuza.memeaterbug.winapi.Process;
import de.zabuza.memeaterbug.winapi.jna.util.Kernel32Util;
import de.zabuza.memeaterbug.winapi.jna.util.PsapiUtil;

/**
* Provides various methods for memory manipulation on Windows systems using
* JNA. After creation it needs to be hooked to the given process, by using
* {@link #hookProcess()}. Before shutdown the process handle should be closed
* by using {@link #unhookProcess()}.
* by using {@link #unhookProcess()}.<br/>
* <br/>
* If using for 32-bit applications on a 64-bit system, you should use a 32-bit
* Java Runtime Environment to ensure proper execution. You may check this by
* using {@link #is64BitProcess()}.
*
* @author Zabuza
*
Expand All @@ -31,6 +36,11 @@ public final class MemEaterBug {
* If the Mem-Eater-Bug is hooked to a process or not.
*/
private boolean mIsHooked;
/**
* Memory manipulator for the current process handle, if hooked,
* <tt>null</tt> else.
*/
private MemManipulator mMemManipulator;
/**
* Handle to the current process, if hooked, <tt>null</tt> else.
*/
Expand Down Expand Up @@ -62,6 +72,7 @@ public MemEaterBug(final int processId) {

mIsHooked = false;
mProcessHandle = null;
mMemManipulator = null;

if (processId == 0) {
throw new IllegalArgumentException(ErrorMessages.PROCESS_NOT_FOUND);
Expand Down Expand Up @@ -104,21 +115,35 @@ public MemEaterBug(final String processClassName, final String windowTitle) {
this(User32Util.getWindowThreadProcessIdByClassAndTitle(processClassName, windowTitle).getValue());
}

/**
* Gets an object for memory manipulation of the hooked process.
*
* @return An object for memory manipulation of the hooked process.
* @throws IllegalStateException
* If the Mem-Eater-Bug is not hooked to a process
*/
public MemManipulator getMemManipulator() throws IllegalStateException {
ensureIsHooked();
if (mMemManipulator == null) {
mMemManipulator = new MemManipulator(mProcessId, mProcessHandle);
}
return mMemManipulator;
}

/**
* Hooks the Mem-Eater-Bug to the given process. After that, it is able to
* interact with the process and, for example, manipulate its memory. Before
* shutdown, {@link #unhookProcess()} should be used to free resources.<br/>
* <br/>
* It requests the following permissions for interaction with the process:
* <ul>
* <li>
* {@link de.zabuza.memeaterbug.winapi.api.Process#PROCESS_QUERY_INFORMATION
* <li>{@link de.zabuza.memeaterbug.winapi.Process#PROCESS_QUERY_INFORMATION
* PROCESS_QUERY_INFORMATION}</li>
* <li>{@link de.zabuza.memeaterbug.winapi.api.Process#PROCESS_VM_READ
* <li>{@link de.zabuza.memeaterbug.winapi.Process#PROCESS_VM_READ
* PROCESS_VM_READ}</li>
* <li>{@link de.zabuza.memeaterbug.winapi.api.Process#PROCESS_VM_WRITE
* <li>{@link de.zabuza.memeaterbug.winapi.Process#PROCESS_VM_WRITE
* PROCESS_VM_WRITE}</li>
* <li>{@link de.zabuza.memeaterbug.winapi.api.Process#PROCESS_VM_OPERATION
* <li>{@link de.zabuza.memeaterbug.winapi.Process#PROCESS_VM_OPERATION
* PROCESS_VM_OPERATION}</li>
* </ul>
*
Expand Down Expand Up @@ -169,9 +194,7 @@ public void hookProcess(final int permissions) {
* If the Mem-Eater-Bug is not hooked to a process
*/
public boolean is64BitProcess() {
if (!mIsHooked) {
throw new IllegalStateException(ErrorMessages.UNABLE_SINCE_NOT_HOOKED);
}
ensureIsHooked();
return mIs64BitProcess;
}

Expand Down Expand Up @@ -200,9 +223,23 @@ public void unhookProcess() {
throw new IllegalStateException(ErrorMessages.PROCESS_UNABLE_TO_UNHOOK_SINCE_NOT_HOOKED);
}
mProcessHandle = null;
mMemManipulator = null;
mIsHooked = false;
}

/**
* Ensures that the Mem-Eater-Bug is hooked to a process by
* {@link #hookProcess()}.
*
* @throws IllegalStateException
* If the Mem-Eater-Bug is not hooked to a process
*/
private void ensureIsHooked() throws IllegalStateException {
if (!mIsHooked) {
throw new IllegalStateException(ErrorMessages.UNABLE_SINCE_NOT_HOOKED);
}
}

/**
* Ensures that the operating system is a Windows system.
*
Expand Down
140 changes: 36 additions & 104 deletions src/de/zabuza/memeaterbug/examples/SoliScorer.java
Original file line number Diff line number Diff line change
@@ -1,121 +1,53 @@
package de.zabuza.memeaterbug.examples;

import java.util.List;

import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinNT.HANDLE;

import de.zabuza.memeaterbug.winapi.api.Module;
import de.zabuza.memeaterbug.winapi.jna.util.Kernel32Util;
import de.zabuza.memeaterbug.winapi.jna.util.PsapiUtil;
import de.zabuza.memeaterbug.winapi.jna.util.User32Util;
import de.zabuza.memeaterbug.MemEaterBug;
import de.zabuza.memeaterbug.memory.MemManipulator;

/**
* Hack for the popular game Solitaire that allows arbitrary changes of the user score.
* Hack for the popular game Solitaire that allows arbitrary changes of the user
* score.
*
* @author Zabuza
*
*/
public final class SoliScorer {

// final static long baseAddress = 0x10002AFA8L;
final static long baseAddress = 0x341B220L;
final static int[] offsets = new int[] { 0x50, 0x14 };

public static int PROCESS_VM_READ = 0x0010;
public static int PROCESS_VM_WRITE = 0x0020;
public static int PROCESS_VM_OPERATION = 0x0008;
public static int PROCESS_ALL_ACCESS = 0x001F0FFF;
public static int PROCESS_QUERY_INFORMATION = 0x0400;

/**
* Demonstrates the usage of the Mem-Eater-Bug by reading and changing the
* user score in the popular game Solitaire.<br/>
* <br/>
* The program was tested on a Windows 10 64-bit system using the default
* 64-bit Solitaire.exe from Windows 7.
*
* @param args
* Not supported
*/
public static void main(final String[] args) {
int pid = User32Util.getWindowThreadProcessIdByClass("Solitaire").getValue();
System.out.println("PID: " + pid);
if (pid == 0) {
throw new RuntimeException("Process with window class not found: Solitaire");
}
HANDLE process = openProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, pid);
long baseAddress = getBaseAddress(process);
if (baseAddress == -1) {
throw new RuntimeException("Error while finding base address.");
}
System.out.println("Base adress is: 0x" + Long.toHexString(baseAddress));
System.out.println("Offsetting base: 0x" + Long.toHexString(baseAddress) + " + 0xBAFA8 = " + Long.toHexString(baseAddress + 0xBAFA8));
baseAddress += 0xBAFA8;
long dynAddress = findDynAddress(process, offsets, baseAddress);
System.out.println("DynAddress: 0x" + Long.toHexString(dynAddress));

Memory scoreMem = Kernel32Util.readMemory(process, dynAddress, 4);
int score = scoreMem.getInt(0);
System.out.println("Score: " + score);

byte[] newScore = new byte[] { 0x22, 0x22, 0x22, 0x22 };
Kernel32Util.writeMemory(process, dynAddress, newScore);

scoreMem = Kernel32Util.readMemory(process, dynAddress, 4);
score = scoreMem.getInt(0);
System.out.println("New score: " + score);
}

public static HANDLE openProcess(int permissions, int pid) {
HANDLE process = Kernel32Util.openProcess(permissions, true, pid);
return process;
}

public static long findDynAddress(HANDLE process, int[] offsets, long baseAddress) {
int size = Pointer.SIZE;
Memory pTemp = new Memory(size);
long pointerAddress = -1;

for (int i = 0; i < offsets.length; i++) {
if (i == 0) {
pTemp = Kernel32Util.readMemory(process, baseAddress, size);
System.out.println(">Finding dyn address...");
System.out.println("\tStarting at [0x" + Long.toHexString(baseAddress) + "] = 0x" + Long.toHexString(pTemp.getInt(0)));
}

pointerAddress = pTemp.getInt(0) + offsets[i];

if (i != offsets.length - 1) {
System.out.print("\tNext is [0x" + Long.toHexString(pTemp.getInt(0)) + " + 0x" + Long.toHexString(offsets[i]) + " = 0x" + Long.toHexString(pointerAddress) + "] =");
pTemp = Kernel32Util.readMemory(process, pointerAddress, size);
System.out.println(" 0x" + Long.toHexString(pTemp.getInt(0)));
}
}

System.out.println(">Found dyn address 0x" + Long.toHexString(pTemp.getInt(0)) + " + 0x" + Long.toHexString(offsets[offsets.length - 1]) + " = 0x" + Long.toHexString(pointerAddress));

return pointerAddress;
}

public static long getBaseAddress(HANDLE process) {
try {
// TODO 64 or 32? 64 seems to yield wrong addresses, 32 yields error 299...
// Solitaire seems to be a 64bit application on 64bit windows.
List<HMODULE> hModulePointer = PsapiUtil.enumProcessModules(process);

for (HMODULE hModule : hModulePointer) {
Module module = new Module(process, hModule);
System.out.println("Name: " + module.getFileName());
if (module.getFileName().contains("Solitaire.exe")) {
System.out.println((module.getFileName() + ": entry point at - 0x"
+ Long.toHexString(Pointer.nativeValue(module.getEntryPoint()))));
System.out.println("Base of dll : " + module.getLpBaseOfDll());
System.out.println("0x" + Long.toHexString(Pointer.nativeValue(module.getLpBaseOfDll())));
return Long.valueOf("" + Pointer.nativeValue(module.getLpBaseOfDll()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return -1;
// Constants
String exeFileName = "Solitaire.exe";
int nextScore = 1500;
// Offsets are found with a MemReader like CheatEngine
int scoreBaseAddressOffset = 0xBAFA8;
int[] scoreOffsets = new int[] { 0x50, 0x14 };

// Hook to the game
MemEaterBug memEaterBug = new MemEaterBug(exeFileName);
memEaterBug.hookProcess();
MemManipulator memManipulator = memEaterBug.getMemManipulator();

// Find the dynamic address of the score
long scoreBaseAddress = memManipulator.getBaseAddress() + scoreBaseAddressOffset;
long scoreDynAddress = memManipulator.findDynAddress(scoreOffsets, scoreBaseAddress);

// Read the current score
int score = memManipulator.readInt(scoreDynAddress);
System.out.println("The current score is: " + score);

// Write to the current score
memManipulator.writeInt(scoreDynAddress, nextScore);
int readScore = memManipulator.readInt(scoreDynAddress);
System.out.println("Now the score is: " + readScore);

// Unhook from the game
memEaterBug.unhookProcess();
}
}
3 changes: 2 additions & 1 deletion src/de/zabuza/memeaterbug/examples/package-info.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/**
* This is the core package for examples of the Mem-Eater-Bug, e.g. small applications using the API.
* This is the core package for examples of the Mem-Eater-Bug, e.g. small
* applications using the API.
*/
package de.zabuza.memeaterbug.examples;
Loading

0 comments on commit 280b4b5

Please sign in to comment.