From 43405b956df705a24945951cb8c73f364185904c Mon Sep 17 00:00:00 2001 From: "Peter \"Marenthyu\" Fredebold" Date: Sat, 2 May 2020 16:21:38 +0200 Subject: [PATCH] initial commit --- src/META-INF/MANIFEST.MF | 3 + src/de/marenthyu/memedit/Main.java | 50 +++++ .../memedit/bunny/BunnyConstants.java | 16 ++ .../memedit/bunny/BunnyMemoryManager.java | 194 +++++++++++++++++ src/de/marenthyu/memedit/util/Kernel32.java | 38 ++++ src/de/marenthyu/memedit/util/Shared.java | 103 +++++++++ src/de/marenthyu/memedit/util/User32.java | 196 ++++++++++++++++++ 7 files changed, 600 insertions(+) create mode 100644 src/META-INF/MANIFEST.MF create mode 100644 src/de/marenthyu/memedit/Main.java create mode 100644 src/de/marenthyu/memedit/bunny/BunnyConstants.java create mode 100644 src/de/marenthyu/memedit/bunny/BunnyMemoryManager.java create mode 100644 src/de/marenthyu/memedit/util/Kernel32.java create mode 100644 src/de/marenthyu/memedit/util/Shared.java create mode 100644 src/de/marenthyu/memedit/util/User32.java diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF new file mode 100644 index 0000000..3fa558f --- /dev/null +++ b/src/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: de.marenthyu.memedit.Main + diff --git a/src/de/marenthyu/memedit/Main.java b/src/de/marenthyu/memedit/Main.java new file mode 100644 index 0000000..9a9674c --- /dev/null +++ b/src/de/marenthyu/memedit/Main.java @@ -0,0 +1,50 @@ +package de.marenthyu.memedit; + +import de.marenthyu.memedit.bunny.BunnyMemoryManager; +import de.marenthyu.twitch.auth.AuthStore; +import de.marenthyu.twitch.auth.oauth.OAuthToken; +import de.marenthyu.twitch.pubsub.PubSubClient; + +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +public class Main { + private static final String CLIENT_ID = "ckqn7b448jawx00ar8pp5yq8l6a9kc"; + + public static void main(String[] args) { + + AuthStore.init(CLIENT_ID); + while (!AuthStore.isInitialized()) { + try { + TimeUnit.MILLISECONDS.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + OAuthToken token = AuthStore.getToken(); + PubSubClient pubSub = null; + try { + ArrayList topics = new ArrayList(); + topics.add("channel-points-channel-v1." + token.getUserID()); + pubSub = new PubSubClient(token, topics); + + } catch (URISyntaxException e) { + e.printStackTrace(); + } + + BunnyMemoryManager.init(); + + // Okay, we found bunny - time to do some stuff + + assert pubSub != null; + BunnyMemoryManager.addSetHPHandler(pubSub); + BunnyMemoryManager.addFullHealHandler(pubSub); + BunnyMemoryManager.addHealHandler(pubSub); + BunnyMemoryManager.addBadgeHandlers(pubSub); + + System.out.println("============= SETUP COMPLETE! Leaving this running should work now. Close this window to kill it. ============="); + } + + +} \ No newline at end of file diff --git a/src/de/marenthyu/memedit/bunny/BunnyConstants.java b/src/de/marenthyu/memedit/bunny/BunnyConstants.java new file mode 100644 index 0000000..db96eab --- /dev/null +++ b/src/de/marenthyu/memedit/bunny/BunnyConstants.java @@ -0,0 +1,16 @@ +package de.marenthyu.memedit.bunny; + +public class BunnyConstants { + final static String RABI_TITLE = "Rabi-Ribi ver 1.99t"; + final static int RABI_SAVBLOCK_OFFSET = 0x01689290; + final static int[] RABI_HEALTH_OFFSETS_IN_SAVBLOCK = {0x4DC}; + final static int[] RABI_MAX_HEALTH_OFFSETS_IN_SAVBLOCK = {0x4EC}; + final static int[] RABI_MANA_OFFSETS_IN_SAVBLOCK = {0x6BC}; // It's a float! + final static int RABI_BADGE_ARRAY_BASE_POINTER_OFFSET = 0x002ED130; // This still is a pointerpath, with offset 0 as the only one. + final static String[] RABI_BADGES = {"HEALTH_UP", "HEALTH_SURGE", "MANA_UP", "MANA_SURGE", "CRISIS_BOOST", + "ATTACK_GROW", "DEFENSE_GROW", "ATTACK_TRADE", "DEFENSE_TRADE", "ARM_STRENGTH", "CARROT_BOOST", "WEAKEN", + "SELF_DEFENSE", "ARMORED", "LUCKY_SEVEN", "HEX_CANCEL", "PURE_LOVE", "TOXIC_STRIKE", + "FRAME_CANCEL", "HEALTH_WAGER", "MANA_WAGER", "STAMINA_PLUS", "BLESSED", "HITBOX_DOWN", "CASHBACK", + "SURVIVAL", "TOP_FORM", "TOUGH_SKIN", "ERINA_BADGE", "RIBBON_BADGE", "AUTO_TRIGGER", "LILITHS_GIFT"}; + +} diff --git a/src/de/marenthyu/memedit/bunny/BunnyMemoryManager.java b/src/de/marenthyu/memedit/bunny/BunnyMemoryManager.java new file mode 100644 index 0000000..ccef0e0 --- /dev/null +++ b/src/de/marenthyu/memedit/bunny/BunnyMemoryManager.java @@ -0,0 +1,194 @@ +package de.marenthyu.memedit.bunny; + +import com.sun.jna.Memory; +import com.sun.jna.Pointer; +import de.marenthyu.twitch.pubsub.PubSubClient; +import de.marenthyu.twitch.pubsub.channelpoints.ChannelPointsRedemptionHandler; + +import javax.swing.*; +import java.io.IOException; + +import static de.marenthyu.memedit.bunny.BunnyConstants.*; +import static de.marenthyu.memedit.util.Shared.*; + +public class BunnyMemoryManager { + + + static int RABI_BASE_SIZE; + public static Pointer bunnyProcess; + public static int bunnyPID; + + + public static void init() { + bunnyPID = getProcessId(RABI_TITLE); + System.out.println("[BUNNY] Bunny PID: " + bunnyPID); + bunnyProcess = openProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, bunnyPID); + try { + RABI_BASE_SIZE = getBaseAddress("rabiribi.exe"); + if (RABI_BASE_SIZE == 0) { + throw new IOException("Invalid Size Returned from Powershell"); + } + } catch (NumberFormatException | IOException e) { + // e.printStackTrace(); + System.out.println(); + System.out.println("[BUNNY] Error getting the Module base address automatically, asking user."); + String userInput = JOptionPane.showInputDialog("Please Enter the base address of rabiribi.exe\n If you dare, please help me automate this. I am at the end of my knowledge. If you don't know how to do this, ask whoever linked you this software."); + try { + RABI_BASE_SIZE = Integer.decode(userInput); + } catch (Exception y) { + try { + RABI_BASE_SIZE = Integer.decode("0x" + userInput); + } catch (Exception ex) { + System.out.println("[BUNNY] You're stupid. I think. something went wrong."); + e.printStackTrace(); + ex.printStackTrace(); + System.exit(2); + } + + } + } + + if (bunnyPID == 0) { + System.err.println("[BUNNY] COULD NOT LOCATE PID FOR " + RABI_TITLE + " - PLEASE MAKE SURE THE GAME IS RUNNING AND ON THE SPECIFIED VERSION!"); + System.exit(1); + } + } + + public static void addSetHPHandler(PubSubClient pubSub) { + pubSub.addChannelPointsRedemptionHandler(new ChannelPointsRedemptionHandler("[BUNNY][HP]") { + @Override + public void matched(String input) { + try { + int newHeahlth = Integer.parseInt(input); + setHP(newHeahlth); + System.out.println("[BUNNY][HP] HP set to " + newHeahlth); + } catch (NumberFormatException e) { + System.err.println("[BUNNY][HP] Invalid Number."); + } + } + }); + } + + public static void addFullHealHandler(PubSubClient pubSub) { + pubSub.addChannelPointsRedemptionHandler(new ChannelPointsRedemptionHandler("[BUNNY][FULLHEAL]") { + @Override + public void matched(String input) { + fullHeal(); + System.out.println("[BUNNY][FULLHEAL] Healed fully!"); + } + }); + } + + public static void addHealHandler(PubSubClient pubSub) { + pubSub.addChannelPointsRedemptionHandler(new ChannelPointsRedemptionHandler("[BUNNY][HEAL]") { + @Override + public void matched(String input) { + try { + int amount = Integer.parseInt(input); + heal(amount); + System.out.println("[BUNNY][HEAL] Healed by " + amount); + } catch (NumberFormatException e) { + System.err.println("[BUNNY][HEAL] Invalid Number."); + } + } + + }); + } + + + public static void addBadgeHandlers(PubSubClient pubSub) { + for (int i = 0;i < RABI_BADGES.length;i++) { + for (int j = 0;j<=2;j++) { + String type; + switch (j) { + case 0: + { + type = "DELETE"; + break; + } + case 1: { + type = "UNLOCK"; + break; + } + case 2: { + type = "EQUIP"; + break; + } + default: + throw new IllegalStateException("Unexpected value: " + j); + } + final int finalJ = j; + final int finalI = i; + pubSub.addChannelPointsRedemptionHandler(new ChannelPointsRedemptionHandler("[BUNNY][BADGE][" + type + "][" + RABI_BADGES[finalI] + "]") { + @Override + public void matched(String input) { + switch (finalJ) { + case 0: + { + removeBadge(finalI); + break; + } + case 1: { + unluckAndUnequipBadge(finalI); + break; + } + case 2: { + equipBadge(finalI); + break; + } + default: + throw new IllegalStateException("Unexpected value: " + finalJ); + } + System.out.println("[BUNNY][BADGE][" + type + "][" + RABI_BADGES[finalI] + "] Badges changed!"); + } + + }); + } + } + + } + + public static void setHP(int newHeahlth) { + System.out.println("[BUNNY] Got request to change HP - setting it to " + newHeahlth); + long dynAddress = findDynAddress(bunnyProcess, RABI_HEALTH_OFFSETS_IN_SAVBLOCK, RABI_BASE_SIZE + RABI_SAVBLOCK_OFFSET); + int curHealth = getCurHP(); + System.out.println(String.format("[BUNNY] Old health read from memory was: %d, setting it to %d", curHealth, newHeahlth)); + writeMemory(bunnyProcess, dynAddress, intToBytes(newHeahlth)); + } + + public static int getCurHP() { + long dynAddress = findDynAddress(bunnyProcess, RABI_HEALTH_OFFSETS_IN_SAVBLOCK, RABI_BASE_SIZE + RABI_SAVBLOCK_OFFSET); + Memory healthCurrentMem = readMemory(bunnyProcess, dynAddress, 4); + return healthCurrentMem.getInt(0); + } + + public static int getMaxHP() { + long dynAddress = findDynAddress(bunnyProcess, RABI_MAX_HEALTH_OFFSETS_IN_SAVBLOCK, RABI_BASE_SIZE + RABI_SAVBLOCK_OFFSET); + Memory healthCurrentMem = readMemory(bunnyProcess, dynAddress, 4); + return healthCurrentMem.getInt(0); + } + + public static void heal(int amount) { + setHP(getCurHP() + amount); + } + + public static void fullHeal() { + setHP(getMaxHP()); + } + + public static void equipBadge(int badgeID) { + long dynAddress = findDynAddress(bunnyProcess, new int[]{badgeID * 4}, RABI_BASE_SIZE + RABI_BADGE_ARRAY_BASE_POINTER_OFFSET); + writeMemory(bunnyProcess, dynAddress, new byte[]{0x02}); + } + + public static void unluckAndUnequipBadge(int badgeID) { + long dynAddress = findDynAddress(bunnyProcess, new int[]{badgeID * 4}, RABI_BASE_SIZE + RABI_BADGE_ARRAY_BASE_POINTER_OFFSET); + writeMemory(bunnyProcess, dynAddress, new byte[]{0x01}); + } + + public static void removeBadge(int badgeID) { + long dynAddress = findDynAddress(bunnyProcess, new int[]{badgeID * 4}, RABI_BASE_SIZE + RABI_BADGE_ARRAY_BASE_POINTER_OFFSET); + writeMemory(bunnyProcess, dynAddress, new byte[]{0x00}); + } + +} diff --git a/src/de/marenthyu/memedit/util/Kernel32.java b/src/de/marenthyu/memedit/util/Kernel32.java new file mode 100644 index 0000000..e787790 --- /dev/null +++ b/src/de/marenthyu/memedit/util/Kernel32.java @@ -0,0 +1,38 @@ +package de.marenthyu.memedit.util; + +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.win32.StdCallLibrary; + +public interface Kernel32 extends StdCallLibrary { + // description from msdn + //BOOL WINAPI WriteProcessMemory( + //__in HANDLE hProcess, + //__in LPVOID lpBaseAddress, + //__in LPCVOID lpBuffer, + //__in SIZE_T nSize, + //__out SIZE_T *lpNumberOfBytesWritten + //); + boolean WriteProcessMemory(Pointer p, long address, Pointer buffer, int size, IntByReference written); + + + //BOOL WINAPI ReadProcessMemory( + // __in HANDLE hProcess, + // __in LPCVOID lpBaseAddress, + // __out LPVOID lpBuffer, + // __in SIZE_T nSize, + // __out SIZE_T *lpNumberOfBytesRead + // ); + boolean ReadProcessMemory(Pointer hProcess, long inBaseAddress, Pointer outputBuffer, int nSize, IntByReference outNumberOfBytesRead); + + + //HANDLE WINAPI OpenProcess( + // __in DWORD dwDesiredAccess, + // __in BOOL bInheritHandle, + // __in DWORD dwProcessId + //); + Pointer OpenProcess(int desired, boolean inherit, int pid); + + /* derp */ + int GetLastError(); +} diff --git a/src/de/marenthyu/memedit/util/Shared.java b/src/de/marenthyu/memedit/util/Shared.java new file mode 100644 index 0000000..8bf5a55 --- /dev/null +++ b/src/de/marenthyu/memedit/util/Shared.java @@ -0,0 +1,103 @@ +package de.marenthyu.memedit.util; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +public class Shared { + static Kernel32 kernel32 = Native.load("kernel32", Kernel32.class); + static User32 user32 = Native.load("user32", User32.class); + + public static int PROCESS_VM_READ = 0x0010; + public static int PROCESS_VM_WRITE = 0x0020; + public static int PROCESS_VM_OPERATION = 0x0008; + + public static int getProcessId(String window) { + IntByReference pid = new IntByReference(0); + user32.GetWindowThreadProcessId(user32.FindWindowA(null, window), pid); + + return pid.getValue(); + } + + public static Pointer openProcess(int permissions, int pid) { + Pointer process = kernel32.OpenProcess(permissions, true, pid); + return process; + } + + public static long findDynAddress(Pointer process, int[] offsets, long baseAddress) { + + long pointer = baseAddress; + + int size = 4; + Memory pTemp = new Memory(size); + long pointerAddress = 0; + // System.out.println("initial pointerAddress: " + String.format("0x%X", pointer)); + for (int i = 0; i < offsets.length; i++) { + if (i == 0) { + kernel32.ReadProcessMemory(process, pointer, pTemp, size, null); + } + + pointerAddress = ((pTemp.getInt(0) + offsets[i])); + + // System.out.println("Current pointerAddress: " + String.format("0x%X", pointerAddress)); + + if (i != offsets.length - 1) + kernel32.ReadProcessMemory(process, pointerAddress, pTemp, size, null); + + + } + + return pointerAddress; + } + + public static Memory readMemory(Pointer process, long address, int bytesToRead) { + IntByReference read = new IntByReference(0); + Memory output = new Memory(bytesToRead); + + kernel32.ReadProcessMemory(process, address, output, bytesToRead, read); + return output; + } + + public static void writeMemory(Pointer process, long address, byte[] data) { + int size = data.length; + Memory toWrite = new Memory(size); + + for (int i = 0; i < size; i++) { + toWrite.setByte(i, data[i]); + } + + boolean b = kernel32.WriteProcessMemory(process, address, toWrite, size, null); + } + + public static byte[] intToBytes(final int data) { + return new byte[]{ + (byte) ((data >> 0) & 0xff), + (byte) ((data >> 8) & 0xff), + (byte) ((data >> 16) & 0xff), + (byte) ((data >> 24) & 0xff) + }; + } + + public static int getBaseAddress(String executableName) throws IOException { + String command = "powershell.exe \"$modules = Get-Process " + executableName.split("\\.")[0] + " -Module; $modules[0].BaseAddress;\""; + // Executing the command + Process powerShellProcess = Runtime.getRuntime().exec(command); + // Getting the results + powerShellProcess.getOutputStream().close(); + String line; + BufferedReader stdout = new BufferedReader(new InputStreamReader( + powerShellProcess.getInputStream())); + StringBuilder output = new StringBuilder(); + while ((line = stdout.readLine()) != null) { + output.append(line); + } + stdout.close(); + return Integer.parseInt(output.toString()); + } + +} diff --git a/src/de/marenthyu/memedit/util/User32.java b/src/de/marenthyu/memedit/util/User32.java new file mode 100644 index 0000000..9ea0fc8 --- /dev/null +++ b/src/de/marenthyu/memedit/util/User32.java @@ -0,0 +1,196 @@ +package de.marenthyu.memedit.util; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.WinDef.RECT; +import com.sun.jna.ptr.ByteByReference; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.win32.W32APIOptions; + +public interface User32 extends W32APIOptions { + User32 INSTANCE = Native.load("user32", User32.class, DEFAULT_OPTIONS); + + Pointer GetDC(Pointer hWnd); + + int ReleaseDC(Pointer hWnd, Pointer hDC); + + int FLASHW_STOP = 0; + int FLASHW_CAPTION = 1; + int FLASHW_TRAY = 2; + int FLASHW_ALL = (FLASHW_CAPTION | FLASHW_TRAY); + int FLASHW_TIMER = 4; + int FLASHW_TIMERNOFG = 12; + + public static class FLASHWINFO extends Structure { + public int cbSize; + public Pointer hWnd; + public int dwFlags; + public int uCount; + public int dwTimeout; + } + + int IMAGE_BITMAP = 0; + int IMAGE_ICON = 1; + int IMAGE_CURSOR = 2; + int IMAGE_ENHMETAFILE = 3; + int LR_DEFAULTCOLOR = 0x0000; + int LR_MONOCHROME = 0x0001; + int LR_COLOR = 0x0002; + int LR_COPYRETURNORG = 0x0004; + int LR_COPYDELETEORG = 0x0008; + int LR_LOADFROMFILE = 0x0010; + int LR_LOADTRANSPARENT = 0x0020; + int LR_DEFAULTSIZE = 0x0040; + int LR_VGACOLOR = 0x0080; + int LR_LOADMAP3DCOLORS = 0x1000; + int LR_CREATEDIBSECTION = 0x2000; + int LR_COPYFROMRESOURCE = 0x4000; + int LR_SHARED = 0x8000; + + Pointer FindWindowA(String winClass, String title); + + int GetClassName(Pointer hWnd, byte[] lpClassName, int nMaxCount); + + public static class GUITHREADINFO extends Structure { + public int cbSize = size(); + public int flags; + Pointer hwndActive; + Pointer hwndFocus; + Pointer hwndCapture; + Pointer hwndMenuOwner; + Pointer hwndMoveSize; + Pointer hwndCaret; + RECT rcCaret; + } + + boolean GetGUIThreadInfo(int idThread, GUITHREADINFO lpgui); + + public static class WINDOWINFO extends Structure { + public int cbSize = size(); + public RECT rcWindow; + public RECT rcClient; + public int dwStyle; + public int dwExStyle; + public int dwWindowStatus; + public int cxWindowBorders; + public int cyWindowBorders; + public short atomWindowType; + public short wCreatorVersion; + } + + boolean GetWindowInfo(Pointer hWnd, WINDOWINFO pwi); + + boolean GetWindowRect(Pointer hWnd, RECT rect); + + int GetWindowText(Pointer hWnd, byte[] lpString, int nMaxCount); + + int GetWindowTextLength(Pointer hWnd); + + int GetWindowModuleFileName(Pointer hWnd, byte[] lpszFileName, int cchFileNameMax); + + int GetWindowThreadProcessId(Pointer hWnd, IntByReference lpdwProcessId); + + interface WNDENUMPROC extends StdCallCallback { + /** + * Return whether to continue enumeration. + */ + boolean callback(Pointer hWnd, Pointer data); + } + + boolean EnumWindows(WNDENUMPROC lpEnumFunc, Pointer data); + + boolean EnumThreadWindows(int dwThreadId, WNDENUMPROC lpEnumFunc, Pointer data); + + boolean FlashWindowEx(FLASHWINFO info); + + Pointer LoadIcon(Pointer hInstance, String iconName); + + Pointer LoadImage(Pointer hinst, // handle to instance + String name, // image to load + int type, // image type + int xDesired, // desired width + int yDesired, // desired height + int load // load options + ); + + boolean DestroyIcon(Pointer hicon); + + int GWL_EXSTYLE = -20; + int GWL_STYLE = -16; + int GWL_WNDPROC = -4; + int GWL_HINSTANCE = -6; + int GWL_ID = -12; + int GWL_USERDATA = -21; + int DWL_DLGPROC = 4; + int DWL_MSGRESULT = 0; + int DWL_USER = 8; + int WS_EX_COMPOSITED = 0x20000000; + int WS_EX_LAYERED = 0x80000; + int WS_EX_TRANSPARENT = 32; + + int GetWindowLong(Pointer hWnd, int nIndex); + + int SetWindowLong(Pointer hWnd, int nIndex, int dwNewLong); + + int LWA_COLORKEY = 1; + int LWA_ALPHA = 2; + int ULW_COLORKEY = 1; + int ULW_ALPHA = 2; + int ULW_OPAQUE = 4; + + boolean SetLayeredWindowAttributes(Pointer hwnd, int crKey, + byte bAlpha, int dwFlags); + + boolean GetLayeredWindowAttributes(Pointer hwnd, + IntByReference pcrKey, + ByteByReference pbAlpha, + IntByReference pdwFlags); + + /** + * Defines the x- and y-coordinates of a point. + */ + public static class POINT extends Structure { + public int x, y; + } + + /** + * Specifies the width and height of a rectangle. + */ + public static class SIZE extends Structure { + public int cx, cy; + } + + int AC_SRC_OVER = 0x00; + int AC_SRC_ALPHA = 0x01; + int AC_SRC_NO_PREMULT_ALPHA = 0x01; + int AC_SRC_NO_ALPHA = 0x02; + + public static class BLENDFUNCTION extends Structure { + public byte BlendOp = AC_SRC_OVER; // only valid value + public byte BlendFlags = 0; // only valid value + public byte SourceConstantAlpha; + public byte AlphaFormat; + } + + boolean UpdateLayeredWindow(Pointer hwnd, Pointer hdcDst, + POINT pptDst, SIZE psize, + Pointer hdcSrc, POINT pptSrc, int crKey, + BLENDFUNCTION pblend, int dwFlags); + + int SetWindowRgn(Pointer hWnd, Pointer hRgn, boolean bRedraw); + + int VK_SHIFT = 16; + int VK_LSHIFT = 0xA0; + int VK_RSHIFT = 0xA1; + int VK_CONTROL = 17; + int VK_LCONTROL = 0xA2; + int VK_RCONTROL = 0xA3; + int VK_MENU = 18; + int VK_LMENU = 0xA4; + int VK_RMENU = 0xA5; + + boolean GetKeyboardState(byte[] state); + + short GetAsyncKeyState(int vKey); +} \ No newline at end of file