├── .gitignore ├── LICENSE ├── README-MOBILE.md ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── uk │ │ └── lgl │ │ ├── MainActivity.kt │ │ └── modmenu │ │ ├── FloatingModMenuService.kt │ │ ├── Logcat.kt │ │ └── Preferences.kt │ ├── jni │ ├── And64InlineHook │ │ ├── And64InlineHook.cpp │ │ ├── And64InlineHook.hpp │ │ ├── LICENSE │ │ └── README.md │ ├── Android.mk │ ├── Application.mk │ ├── Includes │ │ ├── Logger.h │ │ ├── Utils.h │ │ └── obfuscate.h │ ├── KittyMemory │ │ ├── KittyMemory.cpp │ │ ├── KittyMemory.h │ │ ├── KittyUtils.cpp │ │ ├── KittyUtils.h │ │ ├── MemoryBackup.cpp │ │ ├── MemoryBackup.h │ │ ├── MemoryPatch.cpp │ │ └── MemoryPatch.h │ ├── Main.cpp │ ├── Menu.h │ └── Substrate │ │ ├── Buffer.hpp │ │ ├── CydiaSubstrate.h │ │ ├── SubstrateARM.hpp │ │ ├── SubstrateDebug.cpp │ │ ├── SubstrateDebug.hpp │ │ ├── SubstrateHook.cpp │ │ ├── SubstrateHook.h │ │ ├── SubstrateLog.hpp │ │ ├── SubstratePosixMemory.cpp │ │ ├── SubstrateX86.hpp │ │ ├── SymbolFinder.cpp │ │ ├── SymbolFinder.h │ │ ├── hde64.c │ │ ├── hde64.h │ │ └── table64.h │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ └── activity_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | # release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/workspace.xml 42 | .idea/tasks.xml 43 | .idea/gradle.xml 44 | .idea/assetWizardSettings.xml 45 | .idea/dictionaries 46 | .idea/libraries 47 | # Android Studio 3 in .gitignore file. 48 | .idea/caches 49 | .idea/modules.xml 50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 51 | .idea/navEditor.xml 52 | .idea/* 53 | # Keystore files 54 | # Uncomment the following lines if you do not want to check your keystore files in. 55 | #*.jks 56 | #*.keystore 57 | 58 | # External native build folder generated in Android Studio 2.2 and later 59 | .externalNativeBuild 60 | .cxx/ 61 | 62 | # Google Services (e.g. APIs or Firebase) 63 | # google-services.json 64 | 65 | # Freeline 66 | freeline.py 67 | freeline/ 68 | freeline_project_description.json 69 | 70 | # fastlane 71 | fastlane/report.xml 72 | fastlane/Preview.html 73 | fastlane/screenshots 74 | fastlane/test_output 75 | fastlane/readme.md 76 | 77 | # Version control 78 | vcs.xml 79 | 80 | # lint 81 | lint/intermediates/ 82 | lint/generated/ 83 | lint/outputs/ 84 | lint/tmp/ 85 | # lint/reports/ 86 | 87 | # Android Profiling 88 | *.hprof -------------------------------------------------------------------------------- /README-MOBILE.md: -------------------------------------------------------------------------------- 1 | **This is for Android mobile users who do not have a PC. Floating apps or similar is recommended to read this page while working at the same time** 2 | 3 | **BEST VIEWED ON MOBILE!** 4 | 5 | # Quick links 6 | - [Prerequisites](https://github.com/LGLTeam/Android-Mod-Menu/blob/master/README.md#prerequisites) 7 | - [What you need](#what-you-need) 8 | - [Video Tutorial](#video-tutorial) 9 | - [Download/clone](#downloadclone) 10 | - [Setting up AIDE](#setting-up-aide) 11 | - [Files to work with and making changes](#files-to-work-with-and-making-changes) 12 | - [Implementing the menu to the target game](#implementing-the-menu-to-the-target-game) 13 | - [FAQ](#faq) 14 | - [Troubleshooting](#troubleshooting) 15 | - [Credits/Acknowledgements](#creditsacknowledgements) 16 | 17 | # What you need 18 | * Modded AIDE app, choose one of them you like to use. The official AIDE from Play Store will not work with this project 19 | * [AIDE CMODs](https://secufiles.com/nE9J/AIDE_CMODs_3.2.200108.apk) 20 | * [AIDE Lite Mod](https://mega.nz/file/bIpCQL6I#BzyLf1pB1Sf7EayW_PEJHl3f50qHHZDdb0BpB8FYdVo) 21 | * NDK support for modded AIDE 22 | * NDK for 32-bit/ARMv7 phones: [ndk_arm.tar.gz](https://mega.nz/folder/2c1TWIJD#UCzO7kIo1e4WpFwZHIMYVw/file/XRlRTIjJ) 23 | * NDK for 64-bit/ARM64 phones: [ndk_arm64.tar.gz](https://mega.nz/folder/2c1TWIJD#UCzO7kIo1e4WpFwZHIMYVw/file/7RdTzYxQ) 24 | * Any File Manager app you like to use but these are recommended 25 | * [X-plore](https://play.google.com/store/apps/details?id=com.lonelycatgames.Xplore&hl=en): 26 | * [MT Manager](https://bbs.binmt.cc/forum-2-1.html) [(Mirror link)](https://secufiles.com/js6i/MT2.9.2.apk) With build-in Apktool and editors to modify APK file. Note: It requires VIP to edit `AndroidManifest.xml` more than 200 lines 27 | * Apktool app to edit `AndroidManifest.xml` for free: [Modded APK Editor Pro app](https://mega.nz/file/zQxA0YDY#eNRgcyrHwpWh1nSTHhcc4quxzeXrXcUHlYPoRyltKEw) or [APK Repacker](https://mega.nz/file/LIom0DDL#hJyIchPDCk2n_gcDmutNsOKS86WXQN58qpEGa9JsLrU) (We use APK Editor Pro) 28 | * [Floating apps](https://play.google.com/store/apps/details?id=com.lwi.android.flappsfull&hl=en) (optional): You can use it to read this page and working at the same time or use a build-in feature by OEM 29 | 30 | # Video Tutorial 31 | Note: Videos may be quite outdated 32 | 33 | Mahmoud Gaming: https://www.youtube.com/watch?v=SMCsUy60Hs8 34 | 35 | NSRAÎNA HACKER: https://www.youtube.com/watch?v=MkkZ_loEDTU 36 | 37 | BROKE MODS OFC (Customized menu): https://www.youtube.com/watch?v=IYREVGc-quM 38 | 39 | # Download/Clone 40 | 41 | Go to releases page https://github.com/LGLTeam/Android-Mod-Menu/releases/ and download **Source code (zip)** 42 | 43 | To download latest commit, enable desktop mode on your browser then click **Code**, and click **Download ZIP** 44 | 45 | ![](https://i.imgur.com/EZnbd10.png) 46 | 47 | # Setting up AIDE 48 | 49 | Now let's begin 50 | 51 | Firstly, make sure you know your phone's hardware well, and download correct NDK file coresponding to your phone's architecture. ndk_arm.tar.gz for 32-bit/ARMv7 phones, ndk_arm64.tar.gz for 64-bit/ARM64 phones. Installing incorrect version will cause problems 52 | 53 | Now install NDK support for modded AIDE. Click on 3 dots on the right-corner. Click **More... - Settings** 54 | 55 | ![](https://i.imgur.com/LyZMkK1.png) 56 | 57 | Go to **Build & Run**, and click on **Manage native code support**. 58 | 59 | ![](https://images2.imgbox.com/6e/5c/DootVB4P_o.png) 60 | 61 | A prompt will ask to input the path of NDK file. 62 | 63 | If you use X-plore, you can show details of the file and copy file path easly. 64 | 65 | ![](https://images2.imgbox.com/54/2b/G0gVbhrN_o.png) 66 | 67 | Paste it in the prompt box. 68 | 69 | ![](https://images2.imgbox.com/e1/c0/HSj9yQS9_o.png) 70 | 71 | Click install and wait 72 | 73 | ![](https://images2.imgbox.com/1d/55/7LPXB7CI_o.png) 74 | 75 | After installiation, you can now use AIDE with NDK support 76 | 77 | # Opening project in AIDE 78 | 79 | On the main screen, it says **No open files**. We simply click on **No open files** to show file explorer. Navigate to the directory of the project and open **app** folder 80 | 81 | An option **Open Android app Project** will appear. Click on it to open 82 | 83 | ![](https://images2.imgbox.com/dc/7f/Jtq8ZEl1_o.png) 84 | 85 | Now that the file explorer will look like this, means the project has been opened 86 | 87 | ![](https://images2.imgbox.com/2f/9a/EA0zZr8R_o.png) 88 | 89 | Press play to compile the project whether it works or not 90 | 91 | If successful, it will ask you to install the APK. It may ask you to allow installation from unknown sources. Please allow when asked 92 | 93 | Open the app to test 94 | 95 | # Files to work with and making changes 96 | 97 | See more: https://github.com/LGLTeam/Android-Mod-Menu#files-to-work-with-and-making-changes 98 | 99 | Important for 32-bit users: Please remove arm64-v8a from application.mk 100 | 101 | # Implementing the menu to the target game 102 | 103 | ### 1. Exporting to APK 104 | 105 | We need to compile the project into APK file 106 | 107 | Click on 3 dots icon on the corner. **More... - Project - Publish project** 108 | 109 | ![](https://images2.imgbox.com/e3/2b/8uBsyDwe_o.png) 110 | 111 | This dialog will show but why is export greyed? Because you need to create your own keystore first. Click **Create keystore** 112 | 113 | ![](https://images2.imgbox.com/ec/86/pQMmCpiN_o.png) 114 | 115 | There is no need to put your organization info. Just your alias, password and name are fine. Don't forget your password! 116 | 117 | After you created your keystore, you can now export 118 | 119 | ![](https://images2.imgbox.com/7c/4b/dkSUTXpx_o.png) 120 | 121 | Enter your keystore password 122 | 123 | ![](https://images2.imgbox.com/3f/05/0Ois42P5_o.png) 124 | 125 | After that, it will tell you the APK has been experted 126 | 127 | ![](https://images2.imgbox.com/4b/7b/GZuUcUOi_o.png) 128 | 129 | ### 2. Downloading standalone APK from apkcombo 130 | 131 | It is not a good idea to pull out installed APK from phone because sometimes it comes with splitted APKs, it's a dumb feature, we should use Apkcombo to download standalone APK 132 | 133 | Try to use armv7 standalone APK as possible. It support on all armv7, x86 and arm64 devices 134 | 135 | https://apkcombo.com/ 136 | 137 | ### 3. Know the game's main activity 138 | 139 | We are looking for main activity. X-plore app can get main activity of the app so we will use that 140 | 141 | Click **Show**, check **App manager**. 142 | 143 | ![](https://images2.imgbox.com/d9/f2/wFQs1Nwi_o.png) 144 | 145 | Long press on an app and click **Show details**, then click **App** and expand **Activity** 146 | 147 | Here we can see the main activity. It's always on top 148 | 149 | ![](https://images2.imgbox.com/fd/e4/CQFS4fJJ_o.png) 150 | 151 | Note it down somewhere to remember it. We will explain this later 152 | 153 | ### 4. Adding dex and lib file 154 | 155 | We will use MT Manager to modify APK. Edit the files inside APK is pretty much straight forward, we do not need to decompile the whole APK to storage at all. 156 | 157 | Open the APK file. Click **View** to show its content 158 | 159 | ![](https://images2.imgbox.com/f6/6c/6xd8CxIa_o.png) 160 | 161 | You will now see the content structure inside the APK 162 | 163 | ![](https://images2.imgbox.com/68/b6/ejdGhu1M_o.png) 164 | 165 | Do the same on compiled mod menu APK on the other pane 166 | 167 | We need to rename the dex on our mod menu APK to add dex into the game APK. We name it to classes2.dex since it contain only single dex. If the game have multiple dexes, like classes.dex, classes2.dex, classes3.dex, we would name it to classes4.dex. Mod menu dex must always be last 168 | 169 | ![](https://images2.imgbox.com/2d/f2/0ySkQexV_o.png) 170 | 171 | Press and hold on our dex, and click **+ Add**. This dialog will show. Enable **Auto Sign**, leave Update mode **Replace All** 172 | 173 | ![](https://images2.imgbox.com/42/94/6awBaOeG_o.png) 174 | 175 | Click OK, it will copy and auto sign. 176 | 177 | Copy your library file (.so file) too. Make sure to copy to the correct architecture 178 | armeabi-v7a is armeabi-v7a, arm64-v8a is arm64-v8a, and so on. 179 | 180 | PUTTING THE .SO file ON A WRONG ARCHITECTURE WILL RESULT IN A CRASH! 181 | 182 | ![](https://images2.imgbox.com/34/93/NqI2kgOl_o.png) 183 | 184 | ### 5. Making corresponding changes and compile 185 | 186 | Ok, we go back to the main directory inside APK. You can press **..** to go back 187 | 188 | **I don't want to explain it here again, so please go to main README.md to read: https://github.com/LGLTeam/Android-Mod-Menu/blob/master/README.md#2-making-corresponding-changes-in-the-files** 189 | 190 | **Editing DEX file using MT Manager** 191 | 192 | Open `classes.dex` directly, choose **Dex Editor Plus** 193 | 194 | ![](https://images2.imgbox.com/d4/65/UlDjc5EY_o.png) 195 | 196 | This dialog will show if it have multidex. **SELECT ALL** and click OK 197 | 198 | ![](https://images2.imgbox.com/c3/65/3vpID6Um_o.png) 199 | 200 | The editor opens. 201 | 202 | Make some changes. After you're done, save it 203 | 204 | ![](https://images2.imgbox.com/ad/7c/JyAKmAwA_o.png) 205 | 206 | Go back and **save and exit** 207 | 208 | ![](https://images2.imgbox.com/f2/a3/x0V6dgjH_o.png) 209 | 210 | Click OK to update the changes to the game's APK file with auto sign on 211 | 212 | ![](https://images2.imgbox.com/1a/5a/4Dw9YFv9_o.png) 213 | 214 | Go back outside APK. You will now see a green text which tells you that you have recently modified the file 215 | 216 | ![](https://images2.imgbox.com/dc/c3/SX5pHGPj_o.png) 217 | 218 | **Editing XML file using APK Editor Pro** 219 | 220 | MT Manage requires an account to edit `AndroidManifest.xml` and VIP account to edit more than 200 lines. No, we don't need VIP, we will use APK Editor Pro to edit 221 | 222 | Open APK Editor Pro, click "Select an Apk file". Navigate to the location where you have stored APK, and select it to edit 223 | 224 | ![](https://images2.imgbox.com/97/05/bIwx800E_o.png) 225 | 226 | Open `AndroidManifest.xml` 227 | 228 | ![](https://images2.imgbox.com/01/11/eP695bXz_o.png) 229 | 230 | Make some changes. After you're done, save it 231 | 232 | ![](https://images2.imgbox.com/e4/e5/CgmEHFof_o.png) 233 | 234 | Go back, save the APK file 235 | 236 | ![](https://images2.imgbox.com/ca/4d/0zeGiS91_o.png) 237 | 238 | Wait until it finish compiling. This screen will show after it's done. You can choose to install the APK right now. You may need to uninstall original APK first 239 | 240 | ![](https://images2.imgbox.com/30/3e/hNbeXhYr_o.png) 241 | 242 | It's obvious that it saved an APK to the strange location `/storage/emulated/0/ApkEditor/tmp/gen_signed.apk`, just move it somewhere if you like to. 243 | 244 | If it works, congratulations! 245 | 246 | # Troubleshooting 247 | 248 | Problem with the project like app crashes: click **More... -> Project -> Refresh Build**. This will clear the project cache and fix problems 249 | 250 | Problem with AIDE: Open System Settings -> Apps and clear data of AIDE app. This will reset everything and you need to install NDK again 251 | 252 | AIDE has a lot of compatibility issues, you need to research a lot and do some trial and errors until you fix something. 253 | 254 | # FAQ 255 | 256 | See: https://github.com/LGLTeam/Android-Mod-Menu#faq 257 | 258 | # Credits/Acknowledgements 259 | 260 | * RAUNAK MODS for help in modding games via phone, and testing the template in AIDE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | **Base Credits to LGLTeam ||** 3 | 4 | **WARNING: THIS TEMPLATE IS NOT FOR NEWBIES, THIS IS FOR EXPERIENCES PROGRAMMERS ONLY. NEWBIES SHOULD NOT PROCEED TO TRY IT** 5 | 6 | **This won't cover how to mod games in general, hooking functions, etc that every other online tutorial already covers. This template simply tells you how to use them** 7 | 8 | **For Android mobile users who don't have a PC, please read [README-MOBILE.md](https://github.com/springmusk026/Android-Mod-Menu-Kotlin/blob/master/README-MOBILE.md)** 9 | 10 | 11 | 12 | **MY WEBSITE HERE(https://muskmods.club)** 13 | # Quick links 14 | - [Prerequisites](#prerequisites) 15 | - [What you need](#what-you-need) 16 | - [Download/clone](#downloadclone) 17 | - [Video Tutorial](#video-tutorial) 18 | - [Setting up](#setting-up) 19 | - [Files to work with and making changes](#files-to-work-with-and-making-changes) 20 | - [Implementing the menu to the target game](#implementing-the-menu-to-the-target-game) 21 | - [Loading lib without mod menu](#loading-lib-without-mod-menu) 22 | - [Leeching concerns](#leeching-concerns) 23 | - [FAQ](#faq) 24 | - [Reporting issues/Cоntact](reporting-issuescоntact) 25 | - [Credits/Acknowledgements](#creditsacknowledgements) 26 | 27 | # Introduction 28 | Floating mod menu for il2cpp and other native android games, originally based on [VanHoevenTRs](https://platinmods.com/threads/template-menu-free-for-mod-menu-il2cpp-and-other-native-games.67429/), and some codes used from [Octowolve](https://github.com/Octowolve/Hooking-Template-With-Mod-Menu). KittyMemory, MSHook, And64InlineHook and AY Obfuscator included. Assets are stored as base64 in cpp and does not need to be stored under assets folder. 29 | 30 | Support Android 4.4.x up to Android S Preview. ARMv7, x86 and ARM64 architecture supported. However x86 is deprecated for Unity games so x86 is not our priority 31 | 32 | Preview: 33 | 34 | ![](https://i.imgur.com/zeumkBG.gif) 35 | 36 | # Prerequisites 37 | Before we can jump head first into working a template, we need to go over a few things. 38 | 39 | * **AN EXPERIENCED MODDER, NOT A BEGINNER:** You should be able to mod any games in general, like modifying .so files, dll files, smali files, etc. 40 | * Basic knowledge of smali dalvik opcodes to modify smali 41 | * Basic knowledge ARM and ARM64 assembly, to be able to patch hex (No need x86) 42 | * Basic knowledge of C++ and java (JNI is optional) 43 | * Be able to write hook function in C++ (Not really needed, but recommended if you want to do advanced modding in the future) 44 | * Basic awareness of how Android layout works in XML and Java. This project only use Java for layout but you will learn it easly 45 | * Time and patience: Don't start working on this if you have deadlines or important work. Take your time to read, learn and get used to work with this project. 46 | * DIY (Do it yourself): Yes, you must be able to do things yourself, not depending being spoonfeed. We are not the teachers. 47 | * An inquisitive mind 48 | 49 | # What you need 50 | * Android Studio 4 and up: https://developer.android.com/studio 51 | * NDK Installed via Android Studio (Cmake is not needed) https://developer.android.com/studio/projects/install-ndk#default-version 52 | * Apktool: [Apktool.jar](https://ibotpeaches.github.io/Apktool/) or any 3rd party tools 53 | * [APK Easy Tool](https://forum.xda-developers.com/android/software-hacking/tool-apk-easy-tool-v1-02-windows-gui-t3333960). To get main activity: 54 | * Any text editor. We use [Notepad++](https://notepad-plus-plus.org/downloads/) 55 | * Any png compression to compress your png file: We use https://compresspng.com/ 56 | * Any base64 encoding to encode your file: We use https://www.base64encode.org/ 57 | * ARM converter, to convert ARM instruction to hex: https://armconverter.com/ 58 | 59 | # Download/Clone 60 | Download this repo as ZIP, or clone using any git tools 61 | 62 | Or download Releases here https://github.com/springmusk026/Android-Mod-Menu-Kotlin/releases 63 | 64 | Extract the source to your desired location. The location must **NOT** contain any spaces or symbols 65 | 66 | # Video Tutorial 67 | 68 | Big thanks to modders who created a video tutorial for me. Be warned, those videos might be outdated 69 | 70 | PMT DVA: https://www.youtube.com/watch?v=ieMclBtL6Ig 71 | 72 | Pasha Production: https://www.youtube.com/watch?v=RvrZKIe-QGc 73 | 74 | # Setting up 75 | 76 | Make sure you have everything you need to prepare to work. 77 | 78 | Extract the project to the location **WITHOUT** spaces and weird symbols. Spaces and symbols can cause problems 79 | 80 | Open the project 81 | 82 | ![](https://i.imgur.com/3etm4qX.png) 83 | 84 | Please wait for a while, it will index and sync the project for the first time, takes around a minute depending your computer performance 85 | 86 | After it's done, you can start working! 87 | 88 | # Files to work with and making changes 89 | 90 | #### **modmenu/Logcat.kt** 91 | To save and clear logcat on menu settings without root and without file permission. Can be helpful for modders to diagnose the issues 92 | 93 | Android 11: /storage/emulated/0/Documents/Mod Menu logs/ 94 | 95 | Android 10 and below: /storage/emulated/0/Android/data/(package name)/files/Mod Menu logs 96 | 97 | #### **modmenu/Preferences.kt** 98 | 99 | Saving the menu feature preferences and calling changes via JNI 100 | 101 | #### **modmenu/FloatingModMenuService.kt** 102 | 103 | Main codes of floating mod menu design 104 | 105 | You don't need to change unless you want to redesign it. The codes are explained in the comments 106 | 107 | - `GradientDrawable`: A code for setting corner and stroke/inner border. Works for any View Components 108 | 109 | ```kotlin 110 | GradientDrawable gdMenuBody = GradientDrawable() 111 | gdMenuBody.cornerRadius = MENU_CORNER 112 | gdMenuBody.setColor(MENU_BG_COLOR) 113 | gdMenuBody.setStroke(1, Color.parseColor("#32cb00")) 114 | ``` 115 | 116 | Set the gradient drawable to the view component 117 | 118 | ```kotlin 119 | [name of your view component].setBackground(gradientdrawable); 120 | ``` 121 | 122 | - Resizing menu box 123 | 124 | I've added variables so you can find it easly to resize 125 | 126 | ```kotlin 127 | var MENU_WIDTH = 290 128 | var MENU_HEIGHT = 210 129 | ``` 130 | 131 | Note: You may need to implement auto sizing due to many types of phone with different DPIs and resolutions 132 | 133 | - Color Animation: The codes can be seen in `startAnimation()` 134 | 135 | - Adding new view 136 | 137 | Normally the Android development documentation does not explain the code in java. If you read the Android development documentation and you see an example like TextView 138 | 139 | ```java 140 | TextView textView = (TextView) findViewById(R.id.textView); 141 | textView.setFontVariationSettings("'wdth' 150"); 142 | ``` 143 | 144 | This is for xml. Instead, create an instance for java and add view to your Layout 145 | 146 | ```kotlin 147 | TextView heading = TextView(this) 148 | heading.ellipsize = TextUtils.TruncateAt.MARQUEE 149 | heading.marqueeRepeatLimit = -1 150 | heading.isSingleLine = true 151 | heading.isSelected = true 152 | heading.setTextColor(TEXT_COLOR) 153 | heading.textSize = 10.0f 154 | heading.gravity = Gravity.CENTER 155 | heading.setPadding(0, 0, 0, 5) 156 | ``` 157 | 158 | While we can't explain much here, you can use Google. Search like `create a textview programmatically android`, `create a button programmatically android` etc. for more infomation 159 | 160 | #### **MainActivity.kt** 161 | 162 | The Main Activity. Checks if device running Android 6.0 or above and if have overlay permission enabled before starting menu service. 163 | 164 | You pretty don't need to work with it unless you are implementing something like login layout. 165 | 166 | #### **jni/Menu.h** 167 | 168 | Menu related with JNI calls 169 | 170 | - `Title`: Big text 171 | 172 | - `Heading`: Little text. Semi HTML is supported. Text will scroll if the text is too long 173 | 174 | - `Icon`: Compressed image that is encoded to base64 175 | 176 | You can pretty much use any tools for base64 encoding. 177 | 178 | We use a simple website https://www.base64encode.org/ 179 | 180 | Scroll down till you see `Encode files into Base64 format`. Click or tap on the box to select a file 181 | 182 | Click on `ENCODE` button and click on `CLICK OR TAP HERE` to download your encoded file. Now you can paste it in cpp code 183 | 184 | - `IconWebViewData`: Use icon in Web view with GIF animation support. URL requires internet permission `android.permission.INTERNET` 185 | 186 | Examples 187 | 188 | ```cpp 189 | //From internet: (Requires android.permission.INTERNET) 190 | return env->NewStringUTF("https://i.imgur.com/SujJ85j.gif"); 191 | 192 | //From assets folder: (Requires android.permission.INTERNET) 193 | return env->NewStringUTF("file:///android_asset/example.gif"); 194 | 195 | //Base64 html: 196 | return env->NewStringUTF("data:image/png;base64, "); 197 | 198 | //Nothing: 199 | return NULL 200 | ``` 201 | 202 | - `settingsList`: Feature list for settings 203 | 204 | #### **jni/Main.cpp** 205 | 206 | In this file, you will work with your mods. Below `hack_thread`, you write your code to patch with KittyMemory or hook with MShook. You must have learned it already 207 | 208 | It has a macro to detect if the ARM architecture is 32-bit or 64-bit on compile-time, it's to avoid using wrong offsets, like using ARMv7 offsets on an ARM64 lib. Check the game's APK what libs it contains before you proceed. If you want to target armeabi-v7a lib, write the code below `#else`. If you want to target arm64-v8a libs, write the code below `#if defined(__aarch64__)`. If the game has both armeabi-v7a and arm64-v8a, save your time and delete arm64-v8a folder, only target on armv7. Don't worry, the game will still work on ARM64 209 | 210 | We know we could do `#if defined(__arm__)` for ARMv7 and `#if defined(__i386__)` for x86, but we will leaving `#else`, so AS doesn't make that part greyed out. We will still using ARMv7 as a primary target 211 | 212 | - `Changes`: Get values to apply mods. BE CAREFUL NOT TO ACCIDENTLY REMOVE break; 213 | 214 | - `settingsList`: Settings assigned in negative numbers, we keep the positive numbers for mods. Works same as mod features but the call must be implemented in `localChanges(int featureNum, boolean toggle)` in `FloatingModMenuService.java` 215 | 216 | - `getFeatureList`: Mod features 217 | 218 | Assigning feature numbers is optional. Without it, it will automatically count for you, starting from 0 219 | 220 | Assigned feature numbers can be like any numbers 1,3,200,10... instead in order 0,1,2,3,4,5... 221 | 222 | Do not change or translate the first text unless you know what you are doing 223 | 224 | Toggle, ButtonOnOff and Checkbox can be switched on by default, if you add `True_`. Example: `CheckBox_True_The Check Box` 225 | 226 | To learn HTML, go to this page: https://www.w3schools.com/ 227 | 228 | Usage: 229 | 230 | ```cpp 231 | (Optional feature number)_Toggle_(feature name) 232 | (Optional feature number)_True_Toggle_(feature name) 233 | (Optional feature number)_SeekBar_(feature name)_(min value)_(max value) 234 | (Optional feature number)_Spinner_(feature name)_(Items e.g. item1,item2,item3) 235 | (Optional feature number)_Button_(feature name) 236 | (Optional feature number)_ButtonOnOff_(feature name) 237 | (Optional feature number)_InputValue_(feature name) 238 | (Optional feature number)_CheckBox_(feature name) 239 | (Optional feature number)_RadioButton_(feature name)_(Items e.g. radio1,radio2,radio3) 240 | RichTextView_(Text with limited HTML support) 241 | RichWebView_(Full HTML support) 242 | ButtonLink_(feature name)_(URL/Link here) 243 | Category_(text) 244 | ``` 245 | 246 | To add a collapse, create a new instance 247 | ```cpp 248 | Collapse_The collapse 1 249 | ``` 250 | 251 | Then you can add component views to collapse like 252 | 253 | ```cpp 254 | CollapseAdd_Toggle_The toggle 255 | 123_CollapseAdd_Toggle_The toggle 256 | CollapseAdd_Button_The button 257 | ``` 258 | 259 | #### KittyMemory usage: 260 | ```cpp 261 | MemoryPatch::createWithHex([Lib Name], [offset], "[hex. With or without spaces]"); 262 | [Struct].get_CurrBytes().Modify(); 263 | [Struct].get_CurrBytes().Restore(); 264 | 265 | [Struct].get_TargetAddress(); 266 | [Struct].get_PatchSize(); 267 | [Struct].get_CurrBytes().c_str(); 268 | ``` 269 | 270 | Example: https://github.com/MJx0/KittyMemory/blob/master/Android/test/src/main.cpp 271 | 272 | Use ARM Converter to convert ARM to HEX: https://armconverter.com/ 273 | 274 | #### Hook usage: 275 | ARM64: 276 | ```cpp 277 | A64HookFunction((void *) getAbsoluteAddress([Lib Name], [offset]), (void *)[function], (void **)&[old function]); 278 | ``` 279 | 280 | ARMv7/x86: 281 | ```cpp 282 | MSHookFunction((void *) getAbsoluteAddress([Lib Name], [offset]), (void *)[function], (void **)&[old function]); 283 | ``` 284 | 285 | #### **Android.mk** 286 | 287 | The make file for the c++ compiler. In that file, you can change the lib name on the `LOCAL_MODULE` line 288 | When you change the lib name, change also on `System.loadLibrary("")` under OnCreate method on `MainActivity.java` 289 | Both must have same name 290 | 291 | # Testing 292 | 293 | Connect your device to the computer or run your emulator. Android Studio will detect and you can click Play to run your app. 294 | 295 | ![](https://i.imgur.com/ZegjeM8.png) 296 | 297 | On physical device, make sure you have USB-Debugging enabled in the developer option of your device. To make it visible, go to **Settings** > **About phone** and tap Build number seven times. Return to the previous screen to find Developer options at the bottom. The Developer options screen might be located or named differently on some devices 298 | 299 | Sometimes Android Studio does not detect emulators such as NOX or MEMU fail to connect to adb automatically, reboot can help sometimes. Please refer to the support page. 300 | 301 | # Implementing the menu to the target game 302 | 303 | ### 1. Know your game's main activity 304 | 305 | Now we are looking for main activity, there are 2 ways to do 306 | 307 | 1. Decompile the game's APK file. Open `AndroidManifest.xml` and search after ``. 308 | 309 | Example the game's main activity was `com.unity3d.player.UnityPlayerActivity` 310 | 311 | ![](https://i.imgur.com/FfOtc1K.png) 312 | 313 | Be sure to enable Word wrap so it is easier to read 314 | 315 | ![](https://i.imgur.com/7DzU8d0.png) 316 | 317 | 2. APK Easy Tool since it can read out location of main activity without decompiling APK 318 | 319 | ![](https://i.imgur.com/JQdPjyZ.png) 320 | 321 | Note it somewhere to remember it 322 | 323 | ### 2. Making corresponding changes in the files 324 | 325 | Decompile the game APK 326 | 327 | Open the game's `AndroidManifest.xml` 328 | 329 | Add the `SYSTEM_ALERT_WINDOW` permission besides other permissions if it doesn't exist. Doesn't matter where you place it as long as it's above the application tag 330 | ```xml 331 | 332 | ``` 333 | 334 | If you don't add it, you can't allow overlay permission. 335 | 336 | ![](https://i.imgur.com/XOxLU91.png) 337 | 338 | Add the service above the end of application tag (change the package name of your menu if you had changed it) 339 | ```xml 340 | 341 | ``` 342 | 343 | ![](https://i.imgur.com/rw0hawa.png) 344 | 345 | Now we need to call your mod menu activity 346 | 347 | There are 2 ways to call your mod menu activity. Choose one of them you like to try. Don't know? just choose METHOD 1 348 | 349 | **METHOD 1** 350 | 351 | This simple way, we will call to `MainActivity.java`. `MainActivity.java` will never be used 352 | 353 | Locate to the game's path of main activity and open the **smali** file. If the game have multi dexes, it may be located in smali_classes2.. please check all 354 | 355 | With the path of the target game’s main activity which we determined earlier `com.unity3d.player.UnityPlayerActivity`. Think of it like a path `/com/unity3d/player/UnityPlayerActivity.smali` 356 | 357 | Open the main acitivity's smali file, search for OnCreate method and paste this code inside (change the package name if you had changed it) 358 | ``` 359 | invoke-static {p0}, Luk/lgl/MainActivity;->Start(Landroid/content/Context;)V 360 | ``` 361 | 362 | ![](https://i.imgur.com/7CxTCl8.png) 363 | 364 | Save the file 365 | 366 | **METHOD 2** 367 | 368 | You can follow this it if the first method really fails, or if you really want to use `MainActivity.java` for a reason. Since this involve changing activites, it may cause some problems. 369 | 370 | On your `MainActivity.java`, put the game's main activity to `public String GameActivity` 371 | 372 | ![](https://i.imgur.com/jdacwvH.png) 373 | 374 | Uncomment this code 375 | 376 | ``` 377 | Toast.makeText(MainActivity.this, "Error. Game's main activity does not exist", Toast.LENGTH_LONG).show(); 378 | ``` 379 | 380 | On `AndroidManifest.xml`, remove `` from the game's activity, like this: 381 | 382 | ![](https://i.imgur.com/z1RxPjc.png) 383 | 384 | If you don't remove `` from the game's activity, your menu will not launch. `AndroidManifest.xml` can ONLY contain one `` 385 | 386 | near the end of application tag ``, add your main activity above it. `uk.lgl.MainActivity` is your main activity 387 | 388 | ```xml 389 | 390 | 391 | 392 | 393 | 394 | 395 | ``` 396 | 397 | ![](https://i.imgur.com/33lMPhc.png) 398 | 399 | Save the file 400 | 401 | _Do NOT use both methods at the same time_ 402 | 403 | ### 3. Building your project and copying files 404 | 405 | Build the project to the APK file. 406 | **Build** -> **Build Bundle(s)/APK(s)** -> **Build APK(s)** 407 | 408 | If no errors occured, you did everything right and build will succeded. You will be notified that it build successfully 409 | 410 | ![](https://i.imgur.com/WpSKV1L.png) 411 | 412 | Click on **locate** to show you the location of **build.apk**. It is stored at `(your-project)\app\build\outputs\apk\app-debug.apk` 413 | 414 | ![](https://i.imgur.com/wBTPSLi.png) 415 | 416 | Decompile your **app-debug.apk**. 417 | 418 | Copy your mod menu from decompiled app-debug.apk smali to the game's smali folder. Example ours is uk.lgl.modmenu, we copy the `uk` folder from **app-debug** `(app-debug\smali\uk)` to the game's decompiled directory `(game name)\smali` 419 | 420 | ![](https://i.imgur.com/aO6eEab.png) 421 | 422 | If the game have multidexes, just add your smali to the last `smali_classes` if possible to prevent compilation errors such as `Unsigned short value out of range: xxxxx` (Smali limit error) 423 | 424 | Copy the library file (.so) from **app-debug.apk** to the target game. Watch out the folder names, we don't want to mix them up, so make sure to copy our lib file to the correct architecture. Like our armeabi-v7a lib goes to the target games armeabi-v7a folder, arm64-v8a lib goes to the arm64-v8a folder... 425 | 426 | PUTTING THE LIB FILE ON A WRONG ARCHITECTURE LIKE PUTTING ARM64 LIB TO ARMV7 WILL RESULT A CRASH! 427 | 428 | ![](https://i.imgur.com/oZq1Wq7.png) 429 | 430 | ### 4. Compiling game apk 431 | 432 | Now compile and sign the apk, and install it on your device 433 | 434 | Congrats. You have successfully implemented a mod menu. 435 | 436 | Compile failed? read the log and look up on Google 437 | 438 | If you face any problem, please read the [FAQ](#faq) 439 | 440 | # Leeching concerns 441 | 442 | Leeching as known as stealing code and offsets via reverse enginnering, and editing credits via file editing and recompiling. We all know that, right? 443 | 444 | There are some simple protections in the template: 445 | - Simple C++ string obfuscation called AY Obfuscator. Usage `OBFUSCATE("string here")` and with a key `OBFUSCATE_KEY("string here", 64-bit key here)`. Example `OBFUSCATE_KEY("Hello", 2353474243)` or in hex `OBFUSCATE_KEY("Hello", 0x3FE63DF21A3B)`. The key must not be too long or too short 446 | - `string2Offset("")` to protect offsets 447 | - Simple anti-leech measures that crashes if JNI functions are not called 448 | - Quite harder to edit credits via smali 449 | - Toast hidden inside `getFeatureList` in Main.cpp 450 | 451 | These protection are NOT full protection, it does not stop them, it will only slow them down, this intent is to help you improve on your own by yourself. You should: 452 | - Improve anti-leech measures on your own way 453 | - Protect and encrypt your dex and lib. Find the tools or the projects by yourself, chinese based tools is not recommended as anti virus may flag your mod for malware (false positive). Don't tell anyone what protection you are using, don't let game developers get a hand of it 454 | - Improve string obfuscators a lot more or use others which are not known. Make sure that obfuscator is not too simple 455 | - Enable proguard, and add filters to make sure it does not break your project. See https://developer.android.com/studio/build/shrink-code 456 | - Never share your project to someone 457 | - Do not include any important stuff such as 'offline' username and password, instead add an additional layer, e.g. a web service handling the protected request 458 | - And etc. 459 | 460 | Never contact how to protect more, never complain that your mod has been leeched, that's all your responsibility! If you are really worry about leeching, or getting constantly leeched, and can't protect, just upload your project on Github. They will download from your Github instead leeching. 461 | 462 | **Never tell us how to leech stuff, we are not interested getting involved in it, You will get blocked immediately!** 463 | 464 | # FAQ 465 | ### My game crashing or freezing/My mod menu does not work 466 | There are many many reasons why, it could be your code fault, wrong offsets, bad c++ code, bad layout implementations, game protections etc. 467 | 468 | This is very hard to diagnose. Check logcat on Android Studio to see the error logs, open **Logcat** window from below, and select your device, process and filter to **Error**. Once you do, you can see the errors in logcat 469 | 470 | ![](https://i.imgur.com/cutKC29.png) 471 | 472 | Error on Java side is really easy to spot, if an the error occured natively, a huge `CRASH` report will be logged but it can be hard to figure out. You would have to dig deeper and figure out yourself. Google it if possible. 473 | 474 | If that caused by your hacks, check if your patches and hooks are correct. For hooks, write down the logs such as `LOGD("whatever");` like this below: 475 | 476 | ```cpp 477 | bool get_BoolExample(void *instance) { 478 | LOGD("Bool step 1"); 479 | if (instance != NULL && featureHookToggle) { 480 | LOGD("Bool step 2"); 481 | return true; 482 | } 483 | LOGD("Bool step 3"); 484 | return old_get_BoolExample(instance); 485 | } 486 | ``` 487 | 488 | This can help you what part of your code faced the problem. 489 | 490 | If you believe the game has protection, bypass it or drop it! 491 | 492 | See more about logcat: https://developer.android.com/studio/debug/am-logcat 493 | 494 | ### I have a problem decompiling or compiling APK file 495 | Search for the related issues on Google or on Apktool Github page: https://github.com/iBotPeaches/Apktool/issues 496 | 497 | ### I'm getting an error `Unsigned short value out of range: 65536` if I compile 498 | The method index can't fit into an unsigned 16-bit value, means you have too many methods in the smali due to the limit 65535. Place your code on other classes, such as smali_classes2 instead. This work for Android 5 (Lollipop) and above only. 499 | 500 | ### I'm getting an error `ERROR: executing external native build for ndkBuild Android.mk. Affected Modules: app` 501 | See: https://github.com/LGLTeam/Android-Studio-Solutions/wiki/Executing-external-native-build-for-ndkBuild-Android.mk 502 | 503 | ### I'm getting strange issues on Android Studio or Gradle 504 | See: https://github.com/LGLTeam/Android-Studio-Solutions/wiki 505 | 506 | ### How to get older version of the template? or how to see updates/commits? 507 | 508 | Go to the commit page https://github.com/springmusk026/Android-Mod-Menu-Kotlin/commits/master 509 | 510 | ### Why can't you just add the feature I want? For example closing animation? 511 | 512 | Also known as: "You're a developer after all — it shouldn't be that hard!" 513 | 514 | Since we can't spend my days on it, we have to prioritize the features and fixes that are likely to benefit the larger number of people. Features that is specific to your usage is not going to benefit that many users after all. This is important that you do not expect that anyone can do everything for you. Developing *is* hard, and even outside of supporting a feature, adding the code can take longer than you think! 515 | 516 | With all this being said, remember that this is 100% Open Source. So if you really want a specific feature, try to do it yourself, or ask someone who are willing to help you. 517 | 518 | ### Why can't you just help or teach me modding the game? For example, hooking? 519 | 520 | Anything else, such as how to hook, how to patch, how to bypass, what functions to mod, how il2cpp works, etc. is out of scope. We will not cover anything and trivial stuff every other tutorial online already covers. Instead, try to find a couple of tutorials to learn and mod the game yourself. It's a lot easier than you think. If you can't, find a couple of forums where you can ask your questions or ask the right modder for specific special features 521 | 522 | ### When there is a new update? I have waited for so long time 523 | 524 | There is no ETA, we only push a commit when we want to. Please don't ask the same question over and over again. You could make something better yourself without having to wait 525 | 526 | # Reporting issues/Cоntact 527 |
528 | Cоntact: 529 | Please stop and read this carefully. 530 | 531 | Make sure you have readed FAQ and at least searching for answers. 532 | 533 | If you have usage problems, try asking your questions on any forum sites. For example, if you have an issue with hooking or patching, bypassing security, or wanna mod PUBG and Free Fire, you should go to the **forums**. Here there are no teachers, or who deal with such issues. 534 | 535 | Beginner/newbie/noobs and toxic peoples are **NOT** allowed to cоntact. They are annoying, you would be left **unanswered** and possibly get **BLOCKED**. Known leechers will be instant **BLOCKED** 536 | 537 | Issue tracker is permanently disabled 538 | 539 | Tеlеgram: @ThеᒪGᒪ 540 | 541 | Disсоrd: Deleted because its community has become more toxic, and its security are bad 542 | 543 | You can find @ThеᒪGᒪ on some forum modding communities 544 |
545 | 546 | # Credits/Acknowledgements 547 | Thanks to the following individuals whose code helped me develop this mod menu 548 | 549 | * Octowolve/Escanor - Mod menu: https://github.com/z3r0Sec/Substrate-Template-With-Mod-Menu and Hooking: https://github.com/z3r0Sec/Substrate-Hooking-Example 550 | * VanHoevenTR - Mod menu - https://github.com/LGLTeam/VanHoevenTR_Android_Mod_Menu 551 | * MrIkso - First mod menu template https://github.com/MrIkso/FloatingModMenu 552 | * MJx0 A.K.A Ruit - https://github.com/MJx0/KittyMemory 553 | * Rprop - https://github.com/Rprop/And64InlineHook 554 | * And everyone else who provided input and contributions to this project! 555 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | 4 | android { 5 | compileSdkVersion 30 6 | defaultConfig { 7 | applicationId "uk.lgl" 8 | minSdkVersion 19 9 | targetSdkVersion 30 10 | versionCode 1 11 | versionName "2.8" 12 | ndk { 13 | abiFilters 'armeabi-v7a' 14 | } 15 | signingConfig signingConfigs.debug 16 | } 17 | buildTypes { 18 | release { 19 | shrinkResources false 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | debug { 24 | shrinkResources false 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | externalNativeBuild { 30 | ndkBuild { 31 | path file('src/main/jni/Android.mk') 32 | } 33 | } 34 | ndkVersion = '22.0.7026061' 35 | } 36 | 37 | //dependencies must be placed below 'android' brackets to get it work on AIDE 38 | dependencies { 39 | implementation fileTree(dir: 'libs', include: ['*.jar']) 40 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 41 | } 42 | repositories { 43 | mavenCentral() 44 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | #-dontobfuscate 23 | -keepclassmembers class ** { 24 | public static void Start (***); 25 | } 26 | -keep public class uk.lgl.MainActivity 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/uk/lgl/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package uk.lgl 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.os.Build 6 | import android.widget.Toast 7 | import android.content.Intent 8 | import android.net.Uri 9 | import uk.lgl.modmenu.FloatingModMenuService 10 | import android.os.Bundle 11 | import android.os.Handler 12 | import android.provider.Settings 13 | 14 | class MainActivity : Activity() { 15 | var GameActivity = "com.unity3d.player.UnityPlayerActivity" 16 | var hasLaunched = false 17 | 18 | companion object { 19 | fun Start(context: Context) { 20 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context)) { 21 | Toast.makeText( 22 | context.applicationContext, 23 | "Overlay permission is required in order to show mod menu. Restart the game after you allow permission", 24 | Toast.LENGTH_LONG 25 | ).show() 26 | Toast.makeText( 27 | context.applicationContext, 28 | "Overlay permission is required in order to show mod menu. Restart the game after you allow permission", 29 | Toast.LENGTH_LONG 30 | ).show() 31 | context.startActivity( 32 | Intent( 33 | "android.settings.action.MANAGE_OVERLAY_PERMISSION", 34 | Uri.parse("package:" + context.packageName) 35 | ) 36 | ) 37 | val handler = Handler() 38 | handler.postDelayed({ System.exit(1) }, 5000) 39 | return 40 | } else { 41 | val handler = Handler() 42 | handler.postDelayed({ 43 | context.startService( 44 | Intent( 45 | context, 46 | FloatingModMenuService::class.java 47 | ) 48 | ) 49 | }, 500) 50 | } 51 | } 52 | 53 | init { 54 | System.loadLibrary("MyLibName") 55 | } 56 | } 57 | 58 | override fun onCreate(savedInstanceState: Bundle?) { 59 | super.onCreate(savedInstanceState) 60 | setContentView(R.layout.activity_main) 61 | 62 | Start(this) 63 | 64 | if (!hasLaunched) { 65 | hasLaunched = try { 66 | this@MainActivity.startActivity( 67 | Intent( 68 | this@MainActivity, Class.forName( 69 | GameActivity 70 | ) 71 | ) 72 | ) 73 | true 74 | } catch (e: ClassNotFoundException) { 75 | e.printStackTrace() 76 | return 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/uk/lgl/modmenu/Logcat.kt: -------------------------------------------------------------------------------- 1 | package uk.lgl.modmenu 2 | 3 | import android.content.Context 4 | import android.widget.Toast 5 | import android.os.Build 6 | import android.util.Log 7 | import java.io.* 8 | import java.lang.StringBuilder 9 | 10 | object Logcat { 11 | fun Clear(context: Context?) { 12 | try { 13 | Runtime.getRuntime().exec("logcat -c") 14 | Toast.makeText(context, "Logcat cleared", Toast.LENGTH_LONG).show() 15 | } catch (e: IOException) { 16 | Toast.makeText(context, "There was an error saving logcat to file", Toast.LENGTH_LONG) 17 | .show() 18 | e.printStackTrace() 19 | } 20 | } 21 | 22 | fun Save(context: Context) { 23 | var path: File? = null 24 | try { 25 | val process = Runtime.getRuntime().exec("logcat -d") 26 | val bufferedReader = BufferedReader( 27 | InputStreamReader(process.inputStream) 28 | ) 29 | val log = StringBuilder() 30 | var line = "" 31 | while (bufferedReader.readLine().also { line = it } != null) { 32 | log.append( 33 | """ 34 | $line 35 | 36 | """.trimIndent() 37 | ) 38 | } 39 | val unixTime = System.currentTimeMillis() / 1000L 40 | path = 41 | if (Build.VERSION.SDK_INT >= 30) { //Android R. AIDE didn't support Build.VERSION_CODES.R 42 | File("/storage/emulated/0/Documents/") 43 | } else { 44 | File(context.getExternalFilesDir(null).toString() + "/Mod Menu") 45 | } 46 | val folder = File(path.toString()) 47 | folder.mkdirs() 48 | val file = File(path.toString() + "/Mod menu log - " + context.packageName + ".txt") 49 | file.createNewFile() 50 | try { 51 | //BufferedWriter for performance, true to set append to file flag 52 | val buf = BufferedWriter(FileWriter(file)) 53 | buf.append(log.toString()) 54 | buf.newLine() 55 | buf.close() 56 | Toast.makeText(context, "Logcat saved successfully to: $file", Toast.LENGTH_LONG) 57 | .show() 58 | Toast.makeText(context, "Logcat saved successfully to: $file", Toast.LENGTH_LONG) 59 | .show() 60 | } catch (e: IOException) { 61 | Toast.makeText( 62 | context, 63 | "There was an error saving logcat to file: " + e.localizedMessage, 64 | Toast.LENGTH_LONG 65 | ).show() 66 | e.printStackTrace() 67 | } 68 | } catch (e: IOException) { 69 | Toast.makeText( 70 | context, 71 | "There was an error saving logcat to file: " + Log.getStackTraceString(e), 72 | Toast.LENGTH_LONG 73 | ).show() 74 | e.printStackTrace() 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /app/src/main/java/uk/lgl/modmenu/Preferences.kt: -------------------------------------------------------------------------------- 1 | package uk.lgl.modmenu 2 | 3 | import android.annotation.TargetApi 4 | import android.content.Context 5 | import android.os.Build 6 | import android.content.SharedPreferences 7 | import java.lang.ClassCastException 8 | import java.util.LinkedHashSet 9 | 10 | class Preferences { 11 | private constructor(context: Context?) { 12 | sharedPreferences = context!!.applicationContext.getSharedPreferences( 13 | context.packageName + "_preferences", 14 | Context.MODE_PRIVATE 15 | ) 16 | } 17 | 18 | private constructor(context: Context, preferencesName: String) { 19 | sharedPreferences = context.applicationContext.getSharedPreferences( 20 | preferencesName, 21 | Context.MODE_PRIVATE 22 | ) 23 | } 24 | 25 | fun readString(what: String?): String? { 26 | return sharedPreferences.getString(what, DEFAULT_STRING_VALUE) 27 | } 28 | 29 | fun readString(what: Int): String? { 30 | return try { 31 | sharedPreferences.getString(what.toString(), DEFAULT_STRING_VALUE) 32 | } catch (ex: ClassCastException) { 33 | "" 34 | } 35 | } 36 | 37 | fun readString(what: String?, defaultString: String?): String? { 38 | return sharedPreferences.getString(what, defaultString) 39 | } 40 | 41 | fun writeString(where: String?, what: String?) { 42 | sharedPreferences.edit().putString(where, what).apply() 43 | } 44 | 45 | fun writeString(where: Int, what: String?) { 46 | sharedPreferences.edit().putString(where.toString(), what).apply() 47 | } 48 | 49 | fun readInt(what: String?): Int { 50 | return sharedPreferences.getInt(what, DEFAULT_INT_VALUE) 51 | } 52 | 53 | fun readInt(what: Int): Int { 54 | return try { 55 | sharedPreferences.getInt(what.toString(), DEFAULT_INT_VALUE) 56 | } catch (ex: ClassCastException) { 57 | 0 58 | } 59 | } 60 | 61 | fun readInt(what: String?, defaultInt: Int): Int { 62 | return sharedPreferences.getInt(what, defaultInt) 63 | } 64 | 65 | fun writeInt(where: String?, what: Int) { 66 | sharedPreferences.edit().putInt(where, what).apply() 67 | } 68 | 69 | fun writeInt(where: Int, what: Int) { 70 | sharedPreferences.edit().putInt(where.toString(), what).apply() 71 | } 72 | 73 | fun readDouble(what: String?): Double { 74 | return if (!contains(what)) DEFAULT_DOUBLE_VALUE else java.lang.Double.longBitsToDouble( 75 | readLong(what) 76 | ) 77 | } 78 | 79 | 80 | fun readDouble(what: String?, defaultDouble: Double): Double { 81 | return if (!contains(what)) defaultDouble else java.lang.Double.longBitsToDouble( 82 | readLong( 83 | what 84 | ) 85 | ) 86 | } 87 | 88 | fun writeDouble(where: String?, what: Double) { 89 | writeLong(where, java.lang.Double.doubleToRawLongBits(what)) 90 | } 91 | 92 | fun readFloat(what: String?): Float { 93 | return sharedPreferences.getFloat(what, DEFAULT_FLOAT_VALUE) 94 | } 95 | 96 | fun readFloat(what: String?, defaultFloat: Float): Float { 97 | return sharedPreferences.getFloat(what, defaultFloat) 98 | } 99 | 100 | fun writeFloat(where: String?, what: Float) { 101 | sharedPreferences.edit().putFloat(where, what).apply() 102 | } 103 | 104 | fun readLong(what: String?): Long { 105 | return sharedPreferences.getLong(what, DEFAULT_LONG_VALUE) 106 | } 107 | 108 | fun readLong(what: String?, defaultLong: Long): Long { 109 | return sharedPreferences.getLong(what, defaultLong) 110 | } 111 | 112 | fun writeLong(where: String?, what: Long) { 113 | sharedPreferences.edit().putLong(where, what).apply() 114 | } 115 | 116 | fun readBoolean(what: String?): Boolean { 117 | return sharedPreferences.getBoolean(what, DEFAULT_BOOLEAN_VALUE) 118 | } 119 | 120 | fun readBoolean(what: Int): Boolean { 121 | return sharedPreferences.getBoolean(what.toString(), DEFAULT_BOOLEAN_VALUE) 122 | } 123 | 124 | fun readBoolean(what: String?, defaultBoolean: Boolean): Boolean { 125 | return sharedPreferences.getBoolean(what, defaultBoolean) 126 | } 127 | 128 | fun readBoolean(what: Int, defaultBoolean: Boolean): Boolean { 129 | return try { 130 | sharedPreferences.getBoolean(what.toString(), defaultBoolean) 131 | } catch (ex: ClassCastException) { 132 | defaultBoolean 133 | } 134 | } 135 | 136 | fun writeBoolean(where: String?, what: Boolean) { 137 | sharedPreferences.edit().putBoolean(where, what).apply() 138 | } 139 | 140 | fun writeBoolean(where: Int, what: Boolean) { 141 | sharedPreferences.edit().putBoolean(where.toString(), what).apply() 142 | } 143 | 144 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 145 | fun putStringSet(key: String, value: Set) { 146 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 147 | sharedPreferences.edit().putStringSet(key, value).apply() 148 | } else { 149 | putOrderedStringSet(key, value) 150 | } 151 | } 152 | 153 | fun putOrderedStringSet(key: String, value: Set) { 154 | var stringSetLength = 0 155 | if (sharedPreferences.contains(key + LENGTH)) { 156 | stringSetLength = readInt(key + LENGTH) 157 | } 158 | writeInt(key + LENGTH, value.size) 159 | var i = 0 160 | for (aValue in value) { 161 | writeString("$key[$i]", aValue) 162 | i++ 163 | } 164 | while (i < stringSetLength) { 165 | 166 | remove("$key[$i]") 167 | i++ 168 | } 169 | } 170 | 171 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 172 | fun getStringSet(key: String, defValue: Set?): Set? { 173 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 174 | sharedPreferences.getStringSet(key, defValue) 175 | } else { 176 | getOrderedStringSet(key, defValue) 177 | } 178 | } 179 | 180 | fun getOrderedStringSet(key: String, defValue: Set?): Set? { 181 | if (contains(key + LENGTH)) { 182 | val set = LinkedHashSet() 183 | val stringSetLength = readInt(key + LENGTH) 184 | if (stringSetLength >= 0) { 185 | for (i in 0 until stringSetLength) { 186 | set.add(readString("$key[$i]")) 187 | } 188 | } 189 | return set 190 | } 191 | return defValue 192 | } 193 | 194 | fun remove(key: String) { 195 | if (contains(key + LENGTH)) { 196 | val stringSetLength = readInt(key + LENGTH) 197 | if (stringSetLength >= 0) { 198 | sharedPreferences.edit().remove(key + LENGTH).apply() 199 | for (i in 0 until stringSetLength) { 200 | sharedPreferences.edit().remove("$key[$i]").apply() 201 | } 202 | } 203 | } 204 | sharedPreferences.edit().remove(key).apply() 205 | } 206 | 207 | operator fun contains(key: String?): Boolean { 208 | return sharedPreferences.contains(key) 209 | } 210 | 211 | fun clear() { 212 | sharedPreferences.edit().clear().apply() 213 | } 214 | 215 | companion object { 216 | private lateinit var sharedPreferences: SharedPreferences 217 | private var prefsInstance: Preferences? = null 218 | var context: Context? = null 219 | var loadPref = false 220 | var isExpanded = false 221 | private const val LENGTH = "_length" 222 | private const val DEFAULT_STRING_VALUE = "" 223 | private const val DEFAULT_INT_VALUE = 0 //-1 224 | private const val DEFAULT_DOUBLE_VALUE = 0.0 //-1d 225 | private const val DEFAULT_FLOAT_VALUE = 0f //-1f 226 | private const val DEFAULT_LONG_VALUE = 0L //-1L 227 | private const val DEFAULT_BOOLEAN_VALUE = false 228 | 229 | 230 | 231 | 232 | fun with(context: Context?): Preferences? { 233 | if (prefsInstance == null) { 234 | prefsInstance = Preferences(context) 235 | } 236 | return prefsInstance 237 | } 238 | 239 | fun with(context: Context?, forceInstantiation: Boolean): Preferences? { 240 | if (forceInstantiation) { 241 | prefsInstance = Preferences(context) 242 | } 243 | return prefsInstance 244 | } 245 | 246 | fun with(context: Context, preferencesName: String): Preferences? { 247 | if (prefsInstance == null) { 248 | prefsInstance = Preferences(context, preferencesName) 249 | } 250 | return prefsInstance 251 | } 252 | 253 | fun with( 254 | context: Context, preferencesName: String, 255 | forceInstantiation: Boolean 256 | ): Preferences? { 257 | if (forceInstantiation) { 258 | prefsInstance = Preferences(context, preferencesName) 259 | } 260 | return prefsInstance 261 | } 262 | } 263 | } -------------------------------------------------------------------------------- /app/src/main/jni/And64InlineHook/And64InlineHook.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * @date : 2018/04/18 3 | * @author : Rprop (r_prop@outlook.com) 4 | * https://github.com/Rprop/And64InlineHook 5 | */ 6 | /* 7 | MIT License 8 | 9 | Copyright (c) 2018 Rprop (r_prop@outlook.com) 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | #pragma once 30 | #define A64_MAX_BACKUPS 256 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | void A64HookFunction(void *const symbol, void *const replace, void **result); 37 | void *A64HookFunctionV(void *const symbol, void *const replace, void *const rwx, const uintptr_t rwx_size); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif -------------------------------------------------------------------------------- /app/src/main/jni/And64InlineHook/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 RLib 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/src/main/jni/And64InlineHook/README.md: -------------------------------------------------------------------------------- 1 | # And64InlineHook 2 | Lightweight ARMv8-A(ARM64, AArch64, Little-Endian) Inline Hook Library for Android C/C++ 3 | 4 | # References 5 | [Arm Compiler armasm User Guide](http://infocenter.arm.com/help/topic/com.arm.doc.100069_0610_00_en/pge1427898258836.html) 6 | [Procedure Call Standard for the Arm® 64-bit Architecture (AArch64)](https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst) 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | MAIN_LOCAL_PATH := $(call my-dir) 3 | include $(CLEAR_VARS) 4 | 5 | # Here is the name of your lib. 6 | # When you change the lib name, change also on System.loadLibrary("") under OnCreate method on StaticActivity.java 7 | # Both must have same name 8 | LOCAL_MODULE := MyLibName 9 | 10 | # Code optimization 11 | # -std=c++17 is required to support AIDE app with NDK 12 | LOCAL_CFLAGS := -Wno-error=format-security -fvisibility=hidden -ffunction-sections -fdata-sections -w 13 | LOCAL_CFLAGS += -fno-rtti -fno-exceptions -fpermissive 14 | LOCAL_CPPFLAGS := -Wno-error=format-security -fvisibility=hidden -ffunction-sections -fdata-sections -w -Werror -s -std=c++17 15 | LOCAL_CPPFLAGS += -Wno-error=c++11-narrowing -fms-extensions -fno-rtti -fno-exceptions -fpermissive 16 | LOCAL_LDFLAGS += -Wl,--gc-sections,--strip-all, -llog 17 | LOCAL_ARM_MODE := arm 18 | 19 | LOCAL_C_INCLUDES += $(MAIN_LOCAL_PATH) 20 | 21 | # Here you add the cpp file 22 | LOCAL_SRC_FILES := Main.cpp \ 23 | Substrate/hde64.c \ 24 | Substrate/SubstrateDebug.cpp \ 25 | Substrate/SubstrateHook.cpp \ 26 | Substrate/SubstratePosixMemory.cpp \ 27 | Substrate/SymbolFinder.cpp \ 28 | KittyMemory/KittyMemory.cpp \ 29 | KittyMemory/MemoryPatch.cpp \ 30 | KittyMemory/MemoryBackup.cpp \ 31 | KittyMemory/KittyUtils.cpp \ 32 | And64InlineHook/And64InlineHook.cpp \ 33 | 34 | LOCAL_LDLIBS := -llog -landroid -lGLESv2 35 | 36 | include $(BUILD_SHARED_LIBRARY) 37 | -------------------------------------------------------------------------------- /app/src/main/jni/Application.mk: -------------------------------------------------------------------------------- 1 | # To AIDE Users: If you are using 32-bit/ARMv7 phone, please remove arm64-v8a 2 | APP_ABI := armeabi-v7a 3 | # APP_PLATFORM := android-18 #APP_PLATFORM does not need to be set. It will automatically defaulting 4 | APP_STL := c++_static 5 | APP_OPTIM := release 6 | APP_THIN_ARCHIVE := true 7 | APP_PIE := true 8 | -------------------------------------------------------------------------------- /app/src/main/jni/Includes/Logger.h: -------------------------------------------------------------------------------- 1 | #ifndef DAWN_LOGGER_H 2 | #define DAWN_LOGGER_H 3 | 4 | #include 5 | 6 | enum daLogType { 7 | daDEBUG = 3, 8 | daERROR = 6, 9 | daINFO = 4, 10 | daWARN = 5 11 | }; 12 | 13 | //Change this to another Log Tag if ya want. IN the batch script I provide you change the log tag then too 14 | #define TAG OBFUSCATE("Mod_Menu") 15 | 16 | #define LOGD(...) ((void)__android_log_print(daDEBUG, TAG, __VA_ARGS__)) 17 | #define LOGE(...) ((void)__android_log_print(daERROR, TAG, __VA_ARGS__)) 18 | #define LOGI(...) ((void)__android_log_print(daINFO, TAG, __VA_ARGS__)) 19 | #define LOGW(...) ((void)__android_log_print(daWARN, TAG, __VA_ARGS__)) 20 | 21 | #endif //DAWN_LOGGER_H -------------------------------------------------------------------------------- /app/src/main/jni/Includes/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS 2 | #define UTILS 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "Logger.h" 11 | 12 | typedef unsigned long DWORD; 13 | static uintptr_t libBase; 14 | 15 | bool isGameLibLoaded = false; 16 | 17 | DWORD findLibrary(const char *library) { 18 | char filename[0xFF] = {0}, 19 | buffer[1024] = {0}; 20 | FILE *fp = NULL; 21 | DWORD address = 0; 22 | 23 | sprintf(filename, OBFUSCATE("/proc/self/maps")); 24 | 25 | fp = fopen(filename, OBFUSCATE("rt")); 26 | if (fp == NULL) { 27 | perror(OBFUSCATE("fopen")); 28 | goto done; 29 | } 30 | 31 | while (fgets(buffer, sizeof(buffer), fp)) { 32 | if (strstr(buffer, library)) { 33 | address = (DWORD) strtoul(buffer, NULL, 16); 34 | goto done; 35 | } 36 | } 37 | 38 | done: 39 | 40 | if (fp) { 41 | fclose(fp); 42 | } 43 | 44 | return address; 45 | } 46 | 47 | DWORD getAbsoluteAddress(const char *libraryName, DWORD relativeAddr) { 48 | libBase = findLibrary(libraryName); 49 | if (libBase == 0) 50 | return 0; 51 | return (reinterpret_cast(libBase + relativeAddr)); 52 | } 53 | 54 | extern "C" { 55 | JNIEXPORT jboolean JNICALL 56 | Java_uk_lgl_modmenu_FloatingModMenuService_isGameLibLoaded(JNIEnv *env, jobject thiz) { 57 | return isGameLibLoaded; 58 | } 59 | } 60 | 61 | bool isLibraryLoaded(const char *libraryName) { 62 | //isGameLibLoaded = true; 63 | char line[512] = {0}; 64 | FILE *fp = fopen(OBFUSCATE("/proc/self/maps"), OBFUSCATE("rt")); 65 | if (fp != NULL) { 66 | while (fgets(line, sizeof(line), fp)) { 67 | std::string a = line; 68 | if (strstr(line, libraryName)) { 69 | isGameLibLoaded = true; 70 | return true; 71 | } 72 | } 73 | fclose(fp); 74 | } 75 | return false; 76 | } 77 | 78 | //Credit: Octowolve 79 | void MakeToast(JNIEnv *env, jobject thiz, const char *text, int length) { 80 | //Add our toast in here so it wont be easy to change by simply editing the smali and cant 81 | //be cut out because this method is needed to start the hack (Octowolve is smart) 82 | jstring jstr = env->NewStringUTF(text); //Edit this text to your desired toast message! 83 | jclass toast = env->FindClass(OBFUSCATE("android/widget/Toast")); 84 | jmethodID methodMakeText = 85 | env->GetStaticMethodID( 86 | toast, 87 | OBFUSCATE("makeText"), 88 | OBFUSCATE( 89 | "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;")); 90 | if (methodMakeText == NULL) { 91 | LOGE(OBFUSCATE("toast.makeText not Found")); 92 | return; 93 | } 94 | //The last int is the length on how long the toast should be displayed 95 | //0 = Short, 1 = Long 96 | jobject toastobj = env->CallStaticObjectMethod(toast, methodMakeText, 97 | thiz, jstr, length); 98 | 99 | jmethodID methodShow = env->GetMethodID(toast, OBFUSCATE("show"), OBFUSCATE("()V")); 100 | if (methodShow == NULL) { 101 | LOGE(OBFUSCATE("toast.show not Found")); 102 | return; 103 | } 104 | env->CallVoidMethod(toastobj, methodShow); 105 | } 106 | 107 | uintptr_t string2Offset(const char *c) { 108 | int base = 16; 109 | // See if this function catches all possibilities. 110 | // If it doesn't, the function would have to be amended 111 | // whenever you add a combination of architecture and 112 | // compiler that is not yet addressed. 113 | static_assert(sizeof(uintptr_t) == sizeof(unsigned long) 114 | || sizeof(uintptr_t) == sizeof(unsigned long long), 115 | "Please add string to handle conversion for this architecture."); 116 | 117 | // Now choose the correct function ... 118 | if (sizeof(uintptr_t) == sizeof(unsigned long)) { 119 | return strtoul(c, nullptr, base); 120 | } 121 | 122 | // All other options exhausted, sizeof(uintptr_t) == sizeof(unsigned long long)) 123 | return strtoull(c, nullptr, base); 124 | } 125 | 126 | namespace Toast { 127 | inline const int LENGTH_LONG = 1; 128 | inline const int LENGTH_SHORT = 0; 129 | } 130 | 131 | #endif -------------------------------------------------------------------------------- /app/src/main/jni/Includes/obfuscate.h: -------------------------------------------------------------------------------- 1 | /* --------------------------------- ABOUT ------------------------------------- 2 | Original Author: Adam Yaxley 3 | Website: https://github.com/adamyaxley 4 | License: See end of file 5 | Obfuscate 6 | Guaranteed compile-time string literal obfuscation library for C++14 7 | Usage: 8 | Pass string literals into the AY_OBFUSCATE macro to obfuscate them at compile 9 | time. AY_OBFUSCATE returns a reference to an ay::obfuscated_data object with the 10 | following traits: 11 | - Guaranteed obfuscation of string 12 | The passed string is encrypted with a simple XOR cipher at compile-time to 13 | prevent it being viewable in the binary image 14 | - Global lifetime 15 | The actual instantiation of the ay::obfuscated_data takes place inside a 16 | lambda as a function level static 17 | - Implicitly convertable to a char* 18 | This means that you can pass it directly into functions that would normally 19 | take a char* or a const char* 20 | Example: 21 | const char* obfuscated_string = AY_OBFUSCATE("Hello World"); 22 | std::cout << obfuscated_string << std::endl; 23 | ----------------------------------------------------------------------------- */ 24 | #include 25 | #include 26 | 27 | #ifndef AY_OBFUSCATE_DEFAULT_KEY 28 | // The default 64 bit key to obfuscate strings with. 29 | // This can be user specified by defining AY_OBFUSCATE_DEFAULT_KEY before 30 | // including obfuscate.h 31 | #define AY_OBFUSCATE_DEFAULT_KEY 0x3AD51D91CCE7BFC9 32 | #endif 33 | 34 | namespace ay 35 | { 36 | using size_type = unsigned long long; 37 | using key_type = unsigned long long; 38 | 39 | constexpr void cipher(char* data, size_type size, key_type key) 40 | { 41 | // Split the key into unaligned chunks 42 | const char chunks[8] = { 43 | char(key >> 41), 44 | char(key >> 31), 45 | char(key >> 7), 46 | char(key >> 17), 47 | char(key >> 47), 48 | char(key), 49 | char(key >> 55), 50 | char(key >> 25) 51 | }; 52 | 53 | // Obfuscate with an XOR cipher based on key 54 | for (size_type i = 0; i < size; i++) 55 | { 56 | data[i] ^= chunks[i % 8]; 57 | } 58 | } 59 | 60 | // Obfuscates a string at compile time 61 | template 62 | class obfuscator 63 | { 64 | public: 65 | // Obfuscates the string 'data' on construction 66 | constexpr obfuscator(const char* data) 67 | { 68 | // Copy data 69 | for (size_type i = 0; i < N; i++) 70 | { 71 | m_data[i] = data[i]; 72 | } 73 | 74 | // On construction each of the characters in the string is 75 | // obfuscated with an XOR cipher based on key 76 | cipher(m_data, N, KEY); 77 | } 78 | 79 | constexpr const char* data() const 80 | { 81 | return &m_data[0]; 82 | } 83 | 84 | constexpr size_type size() const 85 | { 86 | return N; 87 | } 88 | 89 | constexpr key_type key() const 90 | { 91 | return KEY; 92 | } 93 | 94 | private: 95 | 96 | char m_data[N]{}; 97 | }; 98 | 99 | // Handles decryption and re-encryption of an encrypted string at runtime 100 | template 101 | class obfuscated_data 102 | { 103 | public: 104 | obfuscated_data(const obfuscator& obfuscator) 105 | { 106 | // Copy obfuscated data 107 | for (size_type i = 0; i < N; i++) 108 | { 109 | m_data[i] = obfuscator.data()[i]; 110 | } 111 | } 112 | 113 | ~obfuscated_data() 114 | { 115 | // Zero m_data to remove it from memory 116 | for (size_type i = 0; i < N; i++) 117 | { 118 | m_data[i] = 0; 119 | } 120 | } 121 | 122 | // Returns a pointer to the plain text string, decrypting it if 123 | // necessary 124 | operator char*() 125 | { 126 | decrypt(); 127 | return m_data; 128 | } 129 | 130 | operator std::string() 131 | { 132 | decrypt(); 133 | return m_data; 134 | } 135 | 136 | // Manually decrypt the string 137 | void decrypt() 138 | { 139 | if (m_encrypted) 140 | { 141 | cipher(m_data, N, KEY); 142 | m_encrypted = false; 143 | } 144 | } 145 | 146 | // Manually re-encrypt the string 147 | void encrypt() 148 | { 149 | if (!m_encrypted) 150 | { 151 | cipher(m_data, N, KEY); 152 | m_encrypted = true; 153 | } 154 | } 155 | 156 | // Returns true if this string is currently encrypted, false otherwise. 157 | bool is_encrypted() const 158 | { 159 | return m_encrypted; 160 | } 161 | 162 | private: 163 | 164 | // Local storage for the string. Call is_encrypted() to check whether or 165 | // not the string is currently obfuscated. 166 | char m_data[N]; 167 | 168 | // Whether data is currently encrypted 169 | bool m_encrypted{ true }; 170 | }; 171 | 172 | // This function exists purely to extract the number of elements 'N' in the 173 | // array 'data' 174 | template 175 | constexpr auto make_obfuscator(const char(&data)[N]) 176 | { 177 | return obfuscator(data); 178 | } 179 | } 180 | 181 | // Obfuscates the string 'data' at compile-time and returns a reference to a 182 | // ay::obfuscated_data object with global lifetime that has functions for 183 | // decrypting the string and is also implicitly convertable to a char* 184 | #define OBFUSCATE(data) OBFUSCATE_KEY(data, AY_OBFUSCATE_DEFAULT_KEY) 185 | 186 | // Obfuscates the string 'data' with 'key' at compile-time and returns a 187 | // reference to a ay::obfuscated_data object with global lifetime that has 188 | // functions for decrypting the string and is also implicitly convertable to a 189 | // char* 190 | #define OBFUSCATE_KEY(data, key) \ 191 | []() -> ay::obfuscated_data& { \ 192 | static_assert(sizeof(decltype(key)) == sizeof(ay::key_type), "key must be a 64 bit unsigned integer"); \ 193 | constexpr auto n = sizeof(data)/sizeof(data[0]); \ 194 | constexpr auto obfuscator = ay::make_obfuscator(data); \ 195 | static auto obfuscated_data = ay::obfuscated_data(obfuscator); \ 196 | return obfuscated_data; \ 197 | }() 198 | 199 | /* -------------------------------- LICENSE ------------------------------------ 200 | Public Domain (http://www.unlicense.org) 201 | This is free and unencumbered software released into the public domain. 202 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 203 | software, either in source code form or as a compiled binary, for any purpose, 204 | commercial or non-commercial, and by any means. 205 | In jurisdictions that recognize copyright laws, the author or authors of this 206 | software dedicate any and all copyright interest in the software to the public 207 | domain. We make this dedication for the benefit of the public at large and to 208 | the detriment of our heirs and successors. We intend this dedication to be an 209 | overt act of relinquishment in perpetuity of all present and future rights to 210 | this software under copyright law. 211 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 212 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 213 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE 214 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 215 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 216 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 217 | ----------------------------------------------------------------------------- */ -------------------------------------------------------------------------------- /app/src/main/jni/KittyMemory/KittyMemory.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // KittyMemory.cpp 3 | // 4 | // Created by MJ (Ruit) on 1/1/19. 5 | // 6 | 7 | #include 8 | #include "KittyMemory.h" 9 | 10 | using KittyMemory::Memory_Status; 11 | using KittyMemory::ProcMap; 12 | 13 | 14 | struct mapsCache { 15 | std::string identifier; 16 | ProcMap map; 17 | }; 18 | 19 | static std::vector __mapsCache; 20 | static ProcMap findMapInCache(std::string id){ 21 | ProcMap ret; 22 | for(int i = 0; i < __mapsCache.size(); i++){ 23 | if(__mapsCache[i].identifier.compare(id) == 0){ 24 | ret = __mapsCache[i].map; 25 | break; 26 | } 27 | } 28 | return ret; 29 | } 30 | 31 | 32 | bool KittyMemory::ProtectAddr(void *addr, size_t length, int protection) { 33 | uintptr_t pageStart = _PAGE_START_OF_(addr); 34 | uintptr_t pageLen = _PAGE_LEN_OF_(addr, length); 35 | return ( 36 | mprotect(reinterpret_cast(pageStart), pageLen, protection) != -1 37 | ); 38 | } 39 | 40 | 41 | Memory_Status KittyMemory::memWrite(void *addr, const void *buffer, size_t len) { 42 | if (addr == NULL) 43 | return INV_ADDR; 44 | 45 | if (buffer == NULL) 46 | return INV_BUF; 47 | 48 | if (len < 1 || len > INT_MAX) 49 | return INV_LEN; 50 | 51 | if (!ProtectAddr(addr, len, _PROT_RWX_)) 52 | return INV_PROT; 53 | 54 | if (memcpy(addr, buffer, len) != NULL && ProtectAddr(addr, len, _PROT_RX_)) 55 | return SUCCESS; 56 | 57 | return FAILED; 58 | } 59 | 60 | 61 | Memory_Status KittyMemory::memRead(void *buffer, const void *addr, size_t len) { 62 | if (addr == NULL) 63 | return INV_ADDR; 64 | 65 | if (buffer == NULL) 66 | return INV_BUF; 67 | 68 | if (len < 1 || len > INT_MAX) 69 | return INV_LEN; 70 | 71 | if (memcpy(buffer, addr, len) != NULL) 72 | return SUCCESS; 73 | 74 | return FAILED; 75 | } 76 | 77 | 78 | std::string KittyMemory::read2HexStr(const void *addr, size_t len) { 79 | char temp[len]; 80 | memset(temp, 0, len); 81 | 82 | const size_t bufferLen = len * 2 + 1; 83 | char buffer[bufferLen]; 84 | memset(buffer, 0, bufferLen); 85 | 86 | std::string ret; 87 | 88 | if (memRead(temp, addr, len) != SUCCESS) 89 | return ret; 90 | 91 | for (int i = 0; i < len; i++) { 92 | sprintf(&buffer[i * 2], "%02X", (unsigned char) temp[i]); 93 | } 94 | 95 | ret += buffer; 96 | return ret; 97 | } 98 | 99 | ProcMap KittyMemory::getLibraryMap(const char *libraryName) { 100 | ProcMap retMap; 101 | char line[512] = {0}; 102 | 103 | FILE *fp = fopen(OBFUSCATE("/proc/self/maps"), OBFUSCATE("rt")); 104 | if (fp != NULL) { 105 | while (fgets(line, sizeof(line), fp)) { 106 | if (strstr(line, libraryName)) { 107 | char tmpPerms[5] = {0}, tmpDev[12] = {0}, tmpPathname[444] = {0}; 108 | // parse a line in maps file 109 | // (format) startAddress-endAddress perms offset dev inode pathname 110 | sscanf(line, "%llx-%llx %s %ld %s %d %s", 111 | (long long unsigned *) &retMap.startAddr, 112 | (long long unsigned *) &retMap.endAddr, 113 | tmpPerms, &retMap.offset, tmpDev, &retMap.inode, tmpPathname); 114 | 115 | retMap.length = (uintptr_t) retMap.endAddr - (uintptr_t) retMap.startAddr; 116 | retMap.perms = tmpPerms; 117 | retMap.dev = tmpDev; 118 | retMap.pathname = tmpPathname; 119 | 120 | break; 121 | } 122 | } 123 | fclose(fp); 124 | } 125 | return retMap; 126 | } 127 | 128 | uintptr_t KittyMemory::getAbsoluteAddress(const char *libraryName, uintptr_t relativeAddr, bool useCache) { 129 | ProcMap libMap; 130 | 131 | if(useCache){ 132 | libMap = findMapInCache(libraryName); 133 | if(libMap.isValid()) 134 | return (reinterpret_cast(libMap.startAddr) + relativeAddr); 135 | } 136 | 137 | libMap = getLibraryMap(libraryName); 138 | if (!libMap.isValid()) 139 | return 0; 140 | 141 | if(useCache){ 142 | mapsCache cachedMap; 143 | cachedMap.identifier = libraryName; 144 | cachedMap.map = libMap; 145 | __mapsCache.push_back(cachedMap); 146 | } 147 | 148 | return (reinterpret_cast(libMap.startAddr) + relativeAddr); 149 | } 150 | -------------------------------------------------------------------------------- /app/src/main/jni/KittyMemory/KittyMemory.h: -------------------------------------------------------------------------------- 1 | // 2 | // KittyMemory.hpp 3 | // 4 | // Created by MJ (Ruit) on 1/1/19. 5 | // 6 | 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | #define _SYS_PAGE_SIZE_ (sysconf(_SC_PAGE_SIZE)) 17 | 18 | #define _PAGE_START_OF_(x) ((uintptr_t)x & ~(uintptr_t)(_SYS_PAGE_SIZE_ - 1)) 19 | #define _PAGE_END_OF_(x, len) (_PAGE_START_OF_((uintptr_t)x + len - 1)) 20 | #define _PAGE_LEN_OF_(x, len) (_PAGE_END_OF_(x, len) - _PAGE_START_OF_(x) + _SYS_PAGE_SIZE_) 21 | #define _PAGE_OFFSET_OF_(x) ((uintptr_t)x - _PAGE_START_OF_(x)) 22 | 23 | #define _PROT_RWX_ (PROT_READ | PROT_WRITE | PROT_EXEC) 24 | #define _PROT_RX_ (PROT_READ | PROT_EXEC) 25 | 26 | 27 | #define EMPTY_VEC_OFFSET std::vector() 28 | 29 | namespace KittyMemory { 30 | 31 | typedef enum { 32 | FAILED = 0, 33 | SUCCESS = 1, 34 | INV_ADDR = 2, 35 | INV_LEN = 3, 36 | INV_BUF = 4, 37 | INV_PROT = 5 38 | } Memory_Status; 39 | 40 | 41 | struct ProcMap { 42 | void *startAddr; 43 | void *endAddr; 44 | size_t length; 45 | std::string perms; 46 | long offset; 47 | std::string dev; 48 | int inode; 49 | std::string pathname; 50 | 51 | bool isValid() { return (startAddr != NULL && endAddr != NULL && !pathname.empty()); } 52 | }; 53 | 54 | /* 55 | * Changes protection of an address with given length 56 | */ 57 | bool ProtectAddr(void *addr, size_t length, int protection); 58 | 59 | /* 60 | * Writes buffer content to an address 61 | */ 62 | Memory_Status memWrite(void *addr, const void *buffer, size_t len); 63 | 64 | /* 65 | * Reads an address content into a buffer 66 | */ 67 | Memory_Status memRead(void *buffer, const void *addr, size_t len); 68 | 69 | /* 70 | * Reads an address content and returns hex string 71 | */ 72 | std::string read2HexStr(const void *addr, size_t len); 73 | 74 | 75 | /* 76 | * Wrapper to dereference & get value of a multi level pointer 77 | * Make sure to use the correct data type! 78 | */ 79 | template 80 | Type readMultiPtr(void *ptr, std::vector offsets) { 81 | Type defaultVal = {}; 82 | if (ptr == NULL) 83 | return defaultVal; 84 | 85 | uintptr_t finalPtr = reinterpret_cast(ptr); 86 | int offsetsSize = offsets.size(); 87 | if (offsetsSize > 0) { 88 | for (int i = 0; finalPtr != 0 && i < offsetsSize; i++) { 89 | if (i == (offsetsSize - 1)) 90 | return *reinterpret_cast(finalPtr + offsets[i]); 91 | 92 | finalPtr = *reinterpret_cast(finalPtr + offsets[i]); 93 | } 94 | } 95 | 96 | if (finalPtr == 0) 97 | return defaultVal; 98 | 99 | return *reinterpret_cast(finalPtr); 100 | } 101 | 102 | 103 | /* 104 | * Wrapper to dereference & set value of a multi level pointer 105 | * Make sure to use the correct data type!, const objects won't work 106 | */ 107 | template 108 | bool writeMultiPtr(void *ptr, std::vector offsets, Type val) { 109 | if (ptr == NULL) 110 | return false; 111 | 112 | uintptr_t finalPtr = reinterpret_cast(ptr); 113 | int offsetsSize = offsets.size(); 114 | if (offsetsSize > 0) { 115 | for (int i = 0; finalPtr != 0 && i < offsetsSize; i++) { 116 | if (i == (offsetsSize - 1)) { 117 | *reinterpret_cast(finalPtr + offsets[i]) = val; 118 | return true; 119 | } 120 | 121 | finalPtr = *reinterpret_cast(finalPtr + offsets[i]); 122 | } 123 | } 124 | 125 | if (finalPtr == 0) 126 | return false; 127 | 128 | *reinterpret_cast(finalPtr) = val; 129 | return true; 130 | } 131 | 132 | /* 133 | * Wrapper to dereference & get value of a pointer 134 | * Make sure to use the correct data type! 135 | */ 136 | template 137 | Type readPtr(void *ptr) { 138 | Type defaultVal = {}; 139 | if (ptr == NULL) 140 | return defaultVal; 141 | 142 | return *reinterpret_cast(ptr); 143 | } 144 | 145 | /* 146 | * Wrapper to dereference & set value of a pointer 147 | * Make sure to use the correct data type!, const objects won't work 148 | */ 149 | template 150 | bool writePtr(void *ptr, Type val) { 151 | if (ptr == NULL) 152 | return false; 153 | 154 | *reinterpret_cast(ptr) = val; 155 | return true; 156 | } 157 | 158 | /* 159 | * Gets info of a mapped library in self process 160 | */ 161 | ProcMap getLibraryMap(const char *libraryName); 162 | 163 | /* 164 | * Expects a relative address in a library 165 | * Returns final absolute address 166 | */ 167 | uintptr_t 168 | getAbsoluteAddress(const char *libraryName, uintptr_t relativeAddr, bool useCache = false); 169 | }; 170 | -------------------------------------------------------------------------------- /app/src/main/jni/KittyMemory/KittyUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "KittyUtils.h" 2 | 3 | static void xtrim(std::string &hex){ 4 | if(hex.compare(0, 2, "0x") == 0){ 5 | hex.erase(0, 2); 6 | } 7 | 8 | // https://www.techiedelight.com/remove-whitespaces-string-cpp/ 9 | hex.erase(std::remove_if(hex.begin(), hex.end(), [](char c){ 10 | return (c == ' ' || c == '\n' || c == '\r' || 11 | c == '\t' || c == '\v' || c == '\f'); 12 | }), 13 | hex.end()); 14 | } 15 | 16 | 17 | bool KittyUtils::validateHexString(std::string &xstr){ 18 | if(xstr.length() < 2) return false; 19 | xtrim(xstr); // first remove spaces 20 | if(xstr.length() % 2 != 0) return false; 21 | for(size_t i = 0; i < xstr.length(); i++){ 22 | if(!std::isxdigit((unsigned char)xstr[i])){ 23 | return false; 24 | } 25 | } 26 | return true; 27 | } 28 | 29 | 30 | // https://tweex.net/post/c-anything-tofrom-a-hex-string/ 31 | #include 32 | #include 33 | 34 | 35 | // ------------------------------------------------------------------ 36 | /*! 37 | Convert a block of data to a hex string 38 | */ 39 | void KittyUtils::toHex( 40 | void *const data, //!< Data to convert 41 | const size_t dataLength, //!< Length of the data to convert 42 | std::string &dest //!< Destination string 43 | ) 44 | { 45 | unsigned char *byteData = reinterpret_cast(data); 46 | std::stringstream hexStringStream; 47 | 48 | hexStringStream << std::hex << std::setfill('0'); 49 | for(size_t index = 0; index < dataLength; ++index) 50 | hexStringStream << std::setw(2) << static_cast(byteData[index]); 51 | dest = hexStringStream.str(); 52 | } 53 | 54 | 55 | // ------------------------------------------------------------------ 56 | /*! 57 | Convert a hex string to a block of data 58 | */ 59 | void KittyUtils::fromHex( 60 | const std::string &in, //!< Input hex string 61 | void *const data //!< Data store 62 | ) 63 | { 64 | size_t length = in.length(); 65 | unsigned char *byteData = reinterpret_cast(data); 66 | 67 | std::stringstream hexStringStream; hexStringStream >> std::hex; 68 | for(size_t strIndex = 0, dataIndex = 0; strIndex < length; ++dataIndex) 69 | { 70 | // Read out and convert the string two characters at a time 71 | const char tmpStr[3] = { in[strIndex++], in[strIndex++], 0 }; 72 | 73 | // Reset and fill the string stream 74 | hexStringStream.clear(); 75 | hexStringStream.str(tmpStr); 76 | 77 | // Do the conversion 78 | int tmpValue = 0; 79 | hexStringStream >> tmpValue; 80 | byteData[dataIndex] = static_cast(tmpValue); 81 | } 82 | } -------------------------------------------------------------------------------- /app/src/main/jni/KittyMemory/KittyUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace KittyUtils { 7 | 8 | bool validateHexString(std::string &xstr); 9 | void toHex(void *const data, const size_t dataLength, std::string &dest); 10 | void fromHex(const std::string &in, void *const data); 11 | 12 | } -------------------------------------------------------------------------------- /app/src/main/jni/KittyMemory/MemoryBackup.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryBackup.cpp 3 | // 4 | // Created by MJ (Ruit) on 4/19/20. 5 | // 6 | 7 | #include 8 | #include "MemoryBackup.h" 9 | 10 | 11 | MemoryBackup::MemoryBackup() { 12 | _address = 0; 13 | _size = 0; 14 | _orig_code.clear(); 15 | } 16 | 17 | MemoryBackup::MemoryBackup(const char *libraryName, uintptr_t address, size_t backup_size, bool useMapCache) { 18 | MemoryBackup(); 19 | 20 | if (libraryName == NULL || address == 0 || backup_size < 1) 21 | return; 22 | 23 | _address = KittyMemory::getAbsoluteAddress(libraryName, address, useMapCache); 24 | if(_address == 0) return; 25 | 26 | _size = backup_size; 27 | 28 | _orig_code.resize(backup_size); 29 | 30 | // backup current content 31 | KittyMemory::memRead(&_orig_code[0], reinterpret_cast(_address), backup_size); 32 | } 33 | 34 | 35 | MemoryBackup::MemoryBackup(uintptr_t absolute_address, size_t backup_size) { 36 | MemoryBackup(); 37 | 38 | if (absolute_address == 0 || backup_size < 1) 39 | return; 40 | 41 | _address = absolute_address; 42 | 43 | _size = backup_size; 44 | 45 | _orig_code.resize(backup_size); 46 | 47 | // backup current content 48 | KittyMemory::memRead(&_orig_code[0], reinterpret_cast(_address), backup_size); 49 | } 50 | 51 | MemoryBackup::~MemoryBackup() { 52 | // clean up 53 | _orig_code.clear(); 54 | } 55 | 56 | 57 | bool MemoryBackup::isValid() const { 58 | return (_address != 0 && _size > 0 59 | && _orig_code.size() == _size); 60 | } 61 | 62 | size_t MemoryBackup::get_BackupSize() const{ 63 | return _size; 64 | } 65 | 66 | uintptr_t MemoryBackup::get_TargetAddress() const{ 67 | return _address; 68 | } 69 | 70 | bool MemoryBackup::Restore() { 71 | if (!isValid()) return false; 72 | return KittyMemory::memWrite(reinterpret_cast(_address), &_orig_code[0], _size) == Memory_Status::SUCCESS; 73 | } 74 | 75 | std::string MemoryBackup::get_CurrBytes() { 76 | if (!isValid()) 77 | _hexString = std::string(OBFUSCATE("0xInvalid")); 78 | else 79 | _hexString = KittyMemory::read2HexStr(reinterpret_cast(_address), _size); 80 | 81 | return _hexString; 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/jni/KittyMemory/MemoryBackup.h: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryBackup.h 3 | // 4 | // Created by MJ (Ruit) on 4/19/20. 5 | // 6 | 7 | #pragma once 8 | 9 | #include 10 | 11 | #include "KittyMemory.h" 12 | using KittyMemory::Memory_Status; 13 | using KittyMemory::ProcMap; 14 | 15 | 16 | class MemoryBackup { 17 | private: 18 | uintptr_t _address; 19 | size_t _size; 20 | 21 | std::vector _orig_code; 22 | 23 | std::string _hexString; 24 | 25 | public: 26 | MemoryBackup(); 27 | 28 | /* 29 | * expects library name and relative address 30 | */ 31 | MemoryBackup(const char *libraryName, uintptr_t address, size_t backup_size, bool useMapCache=true); 32 | 33 | /* 34 | * expects absolute address 35 | */ 36 | MemoryBackup(uintptr_t absolute_address, size_t backup_size); 37 | 38 | 39 | ~MemoryBackup(); 40 | 41 | 42 | /* 43 | * Validate patch 44 | */ 45 | bool isValid() const; 46 | 47 | 48 | size_t get_BackupSize() const; 49 | 50 | /* 51 | * Returns pointer to the target address 52 | */ 53 | uintptr_t get_TargetAddress() const; 54 | 55 | 56 | /* 57 | * Restores backup code 58 | */ 59 | bool Restore(); 60 | 61 | 62 | /* 63 | * Returns current target address bytes as hex string 64 | */ 65 | std::string get_CurrBytes(); 66 | }; 67 | -------------------------------------------------------------------------------- /app/src/main/jni/KittyMemory/MemoryPatch.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryPatch.cpp 3 | // 4 | // Created by MJ (Ruit) on 1/1/19. 5 | // 6 | 7 | #include 8 | #include "MemoryPatch.h" 9 | #include "Includes/Logger.h" 10 | 11 | MemoryPatch::MemoryPatch() { 12 | _address = 0; 13 | _size = 0; 14 | _orig_code.clear(); 15 | _patch_code.clear(); 16 | } 17 | 18 | MemoryPatch::MemoryPatch(const char *libraryName, uintptr_t address, 19 | const void *patch_code, size_t patch_size, bool useMapCache) { 20 | MemoryPatch(); 21 | 22 | if (libraryName == NULL || address == 0 || patch_code == NULL || patch_size < 1) 23 | return; 24 | 25 | _address = KittyMemory::getAbsoluteAddress(libraryName, address, useMapCache); 26 | if (_address == 0) return; 27 | 28 | _size = patch_size; 29 | 30 | _orig_code.resize(patch_size); 31 | _patch_code.resize(patch_size); 32 | 33 | // initialize patch & backup current content 34 | KittyMemory::memRead(&_patch_code[0], patch_code, patch_size); 35 | KittyMemory::memRead(&_orig_code[0], reinterpret_cast(_address), patch_size); 36 | } 37 | 38 | MemoryPatch::MemoryPatch(uintptr_t absolute_address, 39 | const void *patch_code, size_t patch_size) { 40 | MemoryPatch(); 41 | 42 | if (absolute_address == 0 || patch_code == NULL || patch_size < 1) 43 | return; 44 | 45 | _address = absolute_address; 46 | _size = patch_size; 47 | 48 | _orig_code.resize(patch_size); 49 | _patch_code.resize(patch_size); 50 | 51 | // initialize patch & backup current content 52 | KittyMemory::memRead(&_patch_code[0], patch_code, patch_size); 53 | KittyMemory::memRead(&_orig_code[0], reinterpret_cast(_address), patch_size); 54 | } 55 | 56 | MemoryPatch::~MemoryPatch() { 57 | // clean up 58 | _orig_code.clear(); 59 | _patch_code.clear(); 60 | } 61 | 62 | MemoryPatch MemoryPatch::createWithHex(const char *libraryName, uintptr_t address, 63 | std::string hex, bool useMapCache) { 64 | MemoryPatch patch; 65 | 66 | if (libraryName == NULL || address == 0 || !KittyUtils::validateHexString(hex)) 67 | return patch; 68 | 69 | patch._address = KittyMemory::getAbsoluteAddress(libraryName, address, useMapCache); 70 | if (patch._address == 0) return patch; 71 | 72 | patch._size = hex.length() / 2; 73 | 74 | patch._orig_code.resize(patch._size); 75 | patch._patch_code.resize(patch._size); 76 | 77 | // initialize patch 78 | KittyUtils::fromHex(hex, &patch._patch_code[0]); 79 | 80 | // backup current content 81 | KittyMemory::memRead(&patch._orig_code[0], reinterpret_cast(patch._address), 82 | patch._size); 83 | return patch; 84 | } 85 | 86 | MemoryPatch MemoryPatch::createWithHex(uintptr_t absolute_address, std::string hex) { 87 | MemoryPatch patch; 88 | 89 | if (absolute_address == 0 || !KittyUtils::validateHexString(hex)) 90 | return patch; 91 | 92 | patch._address = absolute_address; 93 | patch._size = hex.length() / 2; 94 | 95 | patch._orig_code.resize(patch._size); 96 | patch._patch_code.resize(patch._size); 97 | 98 | // initialize patch 99 | KittyUtils::fromHex(hex, &patch._patch_code[0]); 100 | 101 | // backup current content 102 | KittyMemory::memRead(&patch._orig_code[0], reinterpret_cast(patch._address), 103 | patch._size); 104 | return patch; 105 | } 106 | 107 | bool MemoryPatch::isValid() const { 108 | return (_address != 0 && _size > 0 109 | && _orig_code.size() == _size && _patch_code.size() == _size); 110 | } 111 | 112 | size_t MemoryPatch::get_PatchSize() const { 113 | return _size; 114 | } 115 | 116 | uintptr_t MemoryPatch::get_TargetAddress() const { 117 | return _address; 118 | } 119 | 120 | bool MemoryPatch::Restore() { 121 | if (!isValid()) return false; 122 | //LOGI("Restore %i", isLeeched); 123 | return KittyMemory::memWrite(reinterpret_cast(_address), &_orig_code[0], _size) == 124 | Memory_Status::SUCCESS; 125 | } 126 | 127 | bool MemoryPatch::Modify() { 128 | if (!isValid()) return false; 129 | //LOGI("Modify"); 130 | return (KittyMemory::memWrite(reinterpret_cast(_address), &_patch_code[0], _size) == 131 | Memory_Status::SUCCESS); 132 | } 133 | 134 | std::string MemoryPatch::get_CurrBytes() { 135 | if (!isValid()) 136 | _hexString = std::string(OBFUSCATE("0xInvalid")); 137 | else 138 | _hexString = KittyMemory::read2HexStr(reinterpret_cast(_address), _size); 139 | 140 | return _hexString; 141 | } 142 | -------------------------------------------------------------------------------- /app/src/main/jni/KittyMemory/MemoryPatch.h: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryPatch.h 3 | // 4 | // Created by MJ (Ruit) on 1/1/19. 5 | // 6 | 7 | #pragma once 8 | 9 | #include 10 | #include "KittyUtils.h" 11 | #include "KittyMemory.h" 12 | using KittyMemory::Memory_Status; 13 | using KittyMemory::ProcMap; 14 | 15 | class MemoryPatch { 16 | private: 17 | uintptr_t _address; 18 | size_t _size; 19 | 20 | std::vector _orig_code; 21 | std::vector _patch_code; 22 | 23 | std::string _hexString; 24 | 25 | public: 26 | MemoryPatch(); 27 | 28 | /* 29 | * expects library name and relative address 30 | */ 31 | MemoryPatch(const char *libraryName, uintptr_t address, 32 | const void *patch_code, size_t patch_size, bool useMapCache=true); 33 | 34 | 35 | /* 36 | * expects absolute address 37 | */ 38 | MemoryPatch(uintptr_t absolute_address, 39 | const void *patch_code, size_t patch_size); 40 | 41 | 42 | ~MemoryPatch(); 43 | 44 | /* 45 | * compatible hex format (0xffff & ffff & ff ff) 46 | */ 47 | static MemoryPatch createWithHex(const char *libraryName, uintptr_t address, std::string hex, bool useMapCache=true); 48 | static MemoryPatch createWithHex(uintptr_t absolute_address, std::string hex); 49 | 50 | /* 51 | * Validate patch 52 | */ 53 | bool isValid() const; 54 | 55 | 56 | size_t get_PatchSize() const; 57 | 58 | /* 59 | * Returns pointer to the target address 60 | */ 61 | uintptr_t get_TargetAddress() const; 62 | 63 | 64 | /* 65 | * Restores patch to original value 66 | */ 67 | bool Restore(); 68 | 69 | 70 | /* 71 | * Applies patch modifications to target address 72 | */ 73 | bool Modify(); 74 | 75 | 76 | /* 77 | * Returns current patch target address bytes as hex string 78 | */ 79 | std::string get_CurrBytes(); 80 | }; 81 | -------------------------------------------------------------------------------- /app/src/main/jni/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "Includes/Logger.h" 11 | #include "Includes/obfuscate.h" 12 | #include "Includes/Utils.h" 13 | #include "KittyMemory/MemoryPatch.h" 14 | #include "Menu.h" 15 | 16 | //Target lib here 17 | #define targetLibName OBFUSCATE("libil2cpp.so") 18 | 19 | #if defined(__aarch64__) //Compile for arm64 lib only 20 | #include 21 | 22 | #define HOOK(offset, ptr, orig) A64HookFunction((void *)getAbsoluteAddress(targetLibName, string2Offset(OBFUSCATE_KEY(offset, 23479432523588))), (void *)ptr, (void **)&orig) 23 | 24 | #else //Compile for armv7 lib only. Do not worry about greyed out highlighting code, it still works 25 | 26 | #include 27 | #include 28 | 29 | #define HOOK(offset, ptr, orig) MSHookFunction((void *)getAbsoluteAddress(targetLibName, string2Offset(OBFUSCATE_KEY(offset, 23479432523588))), (void *)ptr, (void **)&orig) 30 | 31 | #endif 32 | 33 | 34 | // fancy struct for patches for kittyMemory 35 | struct My_Patches { 36 | // let's assume we have patches for these functions for whatever game 37 | // like show in miniMap boolean function 38 | MemoryPatch GodMode, GodMode2, SliderExample; 39 | // etc... 40 | } hexPatches; 41 | 42 | bool feature1, feature2, featureHookToggle, Health; 43 | int sliderValue = 1, level = 0; 44 | void *instanceBtn; 45 | 46 | // Function pointer splitted because we want to avoid crash when the il2cpp lib isn't loaded. 47 | // If you putted getAbsoluteAddress here, the lib tries to read the address without il2cpp loaded, 48 | // will result in a null pointer which will cause crash 49 | // See https://guidedhacking.com/threads/android-function-pointers-hooking-template-tutorial.14771/ 50 | void (*AddMoneyExample)(void *instance, int amount); 51 | 52 | //KittyMemory Android Example: https://github.com/MJx0/KittyMemory/blob/master/Android/test/src/main.cpp 53 | //Use ARM Converter to convert ARM to HEX: https://armconverter.com/ 54 | 55 | // Hooking examples. Please refer to online tutorials how to write C++ and hooking. Here's a few below 56 | // https://platinmods.com/threads/basic-hooking-tutorial.115704/ 57 | // https://platinmods.com/threads/how-to-unlink-functions-in-il2cpp-and-other-native-games.130436/ 58 | bool (*old_get_BoolExample)(void *instance); 59 | bool get_BoolExample(void *instance) { 60 | if (instance != NULL && featureHookToggle) { 61 | return true; 62 | } 63 | return old_get_BoolExample(instance); 64 | } 65 | 66 | float (*old_get_FloatExample)(void *instance); 67 | float get_FloatExample(void *instance) { 68 | if (instance != NULL && sliderValue > 1) { 69 | return (float) sliderValue; 70 | } 71 | return old_get_FloatExample(instance); 72 | } 73 | 74 | int (*old_Level)(void *instance); 75 | int Level(void *instance) { 76 | if (instance != NULL && level) { 77 | return (int) level; 78 | } 79 | return old_Level(instance); 80 | } 81 | 82 | void (*old_Update)(void *instance); 83 | void Update(void *instance) { 84 | instanceBtn = instance; 85 | return old_Update(instance); 86 | } 87 | 88 | //Field offset hooking 89 | void (*old_HealthUpdate)(void *instance); 90 | void HealthUpdate(void *instance) { 91 | if (instance != NULL) { 92 | if (Health) { 93 | *(int *) ((uint64_t) instance + 0x48) = 999; 94 | } 95 | } 96 | return old_HealthUpdate(instance); 97 | } 98 | // we will run our hacks in a new thread so our while loop doesn't block process main thread 99 | void *hack_thread(void *) { 100 | LOGI(OBFUSCATE("pthread created")); 101 | 102 | //Check if target lib is loaded 103 | do { 104 | sleep(1); 105 | } while (!isLibraryLoaded(targetLibName)); 106 | 107 | //Anti-lib rename 108 | /* 109 | do { 110 | sleep(1); 111 | } while (!isLibraryLoaded("libYOURNAME.so"));*/ 112 | 113 | LOGI(OBFUSCATE("%s has been loaded"), (const char *) targetLibName); 114 | 115 | #if defined(__aarch64__) //To compile this code for arm64 lib only. Do not worry about greyed out highlighting code, it still works 116 | // New way to patch hex via KittyMemory without need to specify len. Spaces or without spaces are fine 117 | // ARM64 assembly example 118 | // MOV X0, #0x0 = 00 00 80 D2 119 | // RET = C0 03 5F D6 120 | hexPatches.GodMode = MemoryPatch::createWithHex(targetLibName, 121 | string2Offset(OBFUSCATE("0x123456")), 122 | OBFUSCATE("00 00 80 D2 C0 03 5F D6")); 123 | //You can also specify target lib like this 124 | hexPatches.GodMode2 = MemoryPatch::createWithHex("libtargetLibHere.so", 125 | string2Offset(OBFUSCATE("0x222222")), 126 | OBFUSCATE("20 00 80 D2 C0 03 5F D6")); 127 | 128 | // Offset Hook example 129 | // HOOK macro armv7/arm64 support 130 | // HOOK("0x123456", get_BoolExample, old_get_BoolExample); 131 | // HOOK("0x123456", Level, old_Level); 132 | 133 | //A64HookFunction((void *) getAbsoluteAddress(targetLibName, string2Offset(OBFUSCATE_KEY("0x123456", 23479432523588))), (void *) get_BoolExample, 134 | // (void **) &old_get_BoolExample); 135 | 136 | // Function pointer splitted because we want to avoid crash when the il2cpp lib isn't loaded. 137 | // See https://guidedhacking.com/threads/android-function-pointers-hooking-template-tutorial.14771/ 138 | AddMoneyExample = (void(*)(void *,int))getAbsoluteAddress(targetLibName, 0x123456); 139 | 140 | #else //To compile this code for armv7 lib only. 141 | 142 | // New way to patch hex via KittyMemory without need to specify len. Spaces or without spaces are fine 143 | // ARMv7 assembly example 144 | // MOV R0, #0x0 = 00 00 A0 E3 145 | // BX LR = 1E FF 2F E1 146 | hexPatches.GodMode = MemoryPatch::createWithHex(targetLibName, //Normal obfuscate 147 | string2Offset(OBFUSCATE("0x123456")), 148 | OBFUSCATE("00 00 A0 E3 1E FF 2F E1")); 149 | //You can also specify target lib like this 150 | hexPatches.GodMode2 = MemoryPatch::createWithHex("libtargetLibHere.so", 151 | string2Offset(OBFUSCATE_KEY("0x222222", 23479432523588)), //64-bit key in decimal 152 | OBFUSCATE_KEY("01 00 A0 E3 1E FF 2F E1", 0x3FE63DF21A3B)); //64-bit key in hex works too 153 | //Can apply patches directly here without need to use switch 154 | //hexPatches.GodMode.Modify(); 155 | //hexPatches.GodMode2.Modify(); 156 | 157 | // Offset Hook example 158 | // HOOK macro armv7/arm64 support 159 | // HOOK("0x123456", get_BoolExample, old_get_BoolExample); 160 | // HOOK("0x123456", Level, old_Level); 161 | 162 | // MSHookFunction((void *) getAbsoluteAddress(targetLibName, 163 | // string2Offset(OBFUSCATE_KEY("0x123456", '?'))), 164 | // (void *) get_BoolExample, (void **) &old_get_BoolExample); 165 | // MSHookFunction((void *) getAbsoluteAddress(targetLibName, 166 | // string2Offset(OBFUSCATE_KEY("0x123456", '?'))), 167 | // (void *) Level, (void **) &old_Level); 168 | 169 | // Symbol hook example (untested). Symbol/function names can be found in IDA if the lib are not stripped. This is not for il2cpp games 170 | // HOOK(("__SymbolNameExample"), get_BoolExample, old_get_BoolExample); 171 | // MSHookFunction((void *) ("__SymbolNameExample"), (void *) get_BoolExample, (void **) &old_get_BoolExample); 172 | 173 | // Function pointer splitted because we want to avoid crash when the il2cpp lib isn't loaded. 174 | // See https://guidedhacking.com/threads/android-function-pointers-hooking-template-tutorial.14771/ 175 | AddMoneyExample = (void (*)(void *, int)) getAbsoluteAddress(targetLibName, 0x123456); 176 | 177 | LOGI(OBFUSCATE("Done")); 178 | #endif 179 | 180 | return NULL; 181 | } 182 | 183 | //JNI calls 184 | extern "C" { 185 | 186 | JNIEXPORT jobjectArray 187 | JNICALL 188 | Java_uk_lgl_modmenu_FloatingModMenuService_getFeatureList(JNIEnv *env, jobject context) { 189 | jobjectArray ret; 190 | 191 | //Toasts added here so it's harder to remove it 192 | MakeToast(env, context, OBFUSCATE("Modded by LGL || MuskMods"), Toast::LENGTH_LONG); 193 | 194 | const char *features[] = { 195 | OBFUSCATE("Category_The Category"), //Not counted 196 | OBFUSCATE("Toggle_The toggle"), 197 | OBFUSCATE( 198 | "100_Toggle_True_The toggle 2"), //This one have feature number assigned, and switched on by default 199 | OBFUSCATE("110_Toggle_The toggle 3"), //This one too 200 | OBFUSCATE("SeekBar_The slider_1_100"), 201 | OBFUSCATE("SeekBar_Kittymemory slider example_1_5"), 202 | OBFUSCATE("Spinner_The spinner_Items 1,Items 2,Items 3"), 203 | OBFUSCATE("Button_The button"), 204 | OBFUSCATE("ButtonLink_The button with link_https://www.youtube.com/"), //Not counted 205 | OBFUSCATE("ButtonOnOff_The On/Off button"), 206 | OBFUSCATE("CheckBox_The Check Box"), 207 | OBFUSCATE("InputValue_Input number"), 208 | OBFUSCATE("InputValue_1000_Input number 2"), //Max value 209 | OBFUSCATE("InputText_Input text"), 210 | OBFUSCATE("RadioButton_Radio buttons_OFF,Mod 1,Mod 2,Mod 3"), 211 | 212 | //Create new collapse 213 | OBFUSCATE("Collapse_Collapse 1"), 214 | OBFUSCATE("CollapseAdd_Toggle_The toggle"), 215 | OBFUSCATE("CollapseAdd_Toggle_The toggle"), 216 | OBFUSCATE("123_CollapseAdd_Toggle_The toggle"), 217 | OBFUSCATE("CollapseAdd_Button_The button"), 218 | 219 | //Create new collapse again 220 | OBFUSCATE("Collapse_Collapse 2"), 221 | OBFUSCATE("CollapseAdd_SeekBar_The slider_1_100"), 222 | OBFUSCATE("CollapseAdd_InputValue_Input number"), 223 | 224 | OBFUSCATE("RichTextView_This is text view, not fully HTML." 225 | "Bold italic underline" 226 | "
New line Support colors" 227 | "
bigger Text"), 228 | OBFUSCATE("RichWebView_" 229 | "This is WebView, with REAL HTML support!" 230 | "
Support CSS
" 231 | "This is scrollable text" 232 | "") 233 | }; 234 | 235 | //Now you dont have to manually update the number everytime; 236 | int Total_Feature = (sizeof features / sizeof features[0]); 237 | ret = (jobjectArray) 238 | env->NewObjectArray(Total_Feature, env->FindClass(OBFUSCATE("java/lang/String")), 239 | env->NewStringUTF("")); 240 | 241 | for (int i = 0; i < Total_Feature; i++) 242 | env->SetObjectArrayElement(ret, i, env->NewStringUTF(features[i])); 243 | 244 | return (ret); 245 | } 246 | 247 | JNIEXPORT void JNICALL 248 | Java_uk_lgl_modmenu_FloatingModMenuService_Changes(JNIEnv *env, jclass clazz, jobject obj, 249 | jint featNum, jstring featName, jint value, 250 | jboolean boolean, jstring str) { 251 | 252 | LOGD(OBFUSCATE("Feature name: %d - %s | Value: = %d | Bool: = %d | Text: = %s"), featNum, 253 | env->GetStringUTFChars(featName, 0), value, 254 | boolean, str != NULL ? env->GetStringUTFChars(str, 0) : ""); 255 | 256 | //BE CAREFUL NOT TO ACCIDENTLY REMOVE break; 257 | 258 | switch (featNum) { 259 | case 0: 260 | MakeToast(env, obj, OBFUSCATE("Modded by LGL || MuskMods"), Toast::LENGTH_LONG); 261 | 262 | break; 263 | case 100: 264 | 265 | break; 266 | case 110: 267 | break; 268 | case 1: 269 | if (value >= 1) { 270 | sliderValue = value; 271 | } 272 | break; 273 | case 2: 274 | switch (value) { 275 | //For noobies 276 | case 0: 277 | hexPatches.SliderExample = MemoryPatch::createWithHex( 278 | targetLibName, string2Offset( 279 | OBFUSCATE("0x100000")), 280 | OBFUSCATE( 281 | "00 00 A0 E3 1E FF 2F E1")); 282 | hexPatches.SliderExample.Modify(); 283 | break; 284 | case 1: 285 | hexPatches.SliderExample = MemoryPatch::createWithHex( 286 | targetLibName, string2Offset( 287 | OBFUSCATE("0x100000")), 288 | OBFUSCATE( 289 | "01 00 A0 E3 1E FF 2F E1")); 290 | hexPatches.SliderExample.Modify(); 291 | break; 292 | case 2: 293 | hexPatches.SliderExample = MemoryPatch::createWithHex( 294 | targetLibName, 295 | string2Offset( 296 | OBFUSCATE("0x100000")), 297 | OBFUSCATE( 298 | "02 00 A0 E3 1E FF 2F E1")); 299 | hexPatches.SliderExample.Modify(); 300 | break; 301 | } 302 | break; 303 | case 3: 304 | switch (value) { 305 | case 0: 306 | LOGD(OBFUSCATE("Selected item 1")); 307 | break; 308 | case 1: 309 | LOGD(OBFUSCATE("Selected item 2")); 310 | break; 311 | case 2: 312 | LOGD(OBFUSCATE("Selected item 3")); 313 | break; 314 | } 315 | break; 316 | case 4: 317 | // Since we have instanceBtn as a field, we can call it out of Update hook function 318 | if (instanceBtn != NULL) 319 | AddMoneyExample(instanceBtn, 999999); 320 | // MakeToast(env, obj, OBFUSCATE("Button pressed"), Toast::LENGTH_SHORT); 321 | break; 322 | case 5: 323 | break; 324 | case 6: 325 | featureHookToggle = boolean; 326 | break; 327 | case 7: 328 | level = value; 329 | break; 330 | case 8: 331 | //MakeToast(env, obj, TextInput, Toast::LENGTH_SHORT); 332 | break; 333 | case 9: 334 | break; 335 | } 336 | } 337 | } 338 | 339 | //No need to use JNI_OnLoad, since we don't use JNIEnv 340 | //We do this to hide OnLoad from disassembler 341 | __attribute__((constructor)) 342 | void lib_main() { 343 | // Create a new thread so it does not block the main thread, means the game would not freeze 344 | pthread_t ptid; 345 | pthread_create(&ptid, NULL, hack_thread, NULL); 346 | } 347 | 348 | /* 349 | JNIEXPORT jint JNICALL 350 | JNI_OnLoad(JavaVM *vm, void *reserved) { 351 | JNIEnv *globalEnv; 352 | vm->GetEnv((void **) &globalEnv, JNI_VERSION_1_6); 353 | return JNI_VERSION_1_6; 354 | } 355 | */ 356 | -------------------------------------------------------------------------------- /app/src/main/jni/Menu.h: -------------------------------------------------------------------------------- 1 | bool titleValid, headingValid, iconValid, settingsValid, isLeeched; 2 | 3 | void setText(JNIEnv *env, jobject obj, const char* text){ 4 | jclass html = (*env).FindClass(OBFUSCATE("android/text/Html")); 5 | jmethodID fromHtml = (*env).GetStaticMethodID(html, OBFUSCATE("fromHtml"), OBFUSCATE("(Ljava/lang/String;)Landroid/text/Spanned;")); 6 | 7 | jclass textView = (*env).FindClass(OBFUSCATE("android/widget/TextView")); 8 | jmethodID setText = (*env).GetMethodID(textView, OBFUSCATE("setText"), OBFUSCATE("(Ljava/lang/CharSequence;)V")); 9 | 10 | jstring jstr = (*env).NewStringUTF(text); 11 | (*env).CallVoidMethod(obj, setText, (*env).CallStaticObjectMethod(html, fromHtml, jstr)); 12 | } 13 | 14 | extern "C" { 15 | JNIEXPORT void JNICALL 16 | Java_uk_lgl_modmenu_FloatingModMenuService_setTitleText(JNIEnv *env, jobject thiz, jobject obj) { 17 | setText(env, obj, OBFUSCATE("Modded by (yourname)")); 18 | 19 | titleValid = true; 20 | } 21 | 22 | JNIEXPORT void JNICALL 23 | Java_uk_lgl_modmenu_FloatingModMenuService_setHeadingText(JNIEnv *env, jobject thiz, jobject obj) { 24 | setText(env, obj, OBFUSCATE("

" 25 | "

Modded by LGL

| " 26 | "https://github.com/LGLTeam | Lorem Ipsum is simply dummy text of the printing and typesetting

" 27 | "
")); 28 | 29 | headingValid = true; 30 | } 31 | 32 | JNIEXPORT jstring JNICALL 33 | Java_uk_lgl_modmenu_FloatingModMenuService_Icon(JNIEnv *env, jobject thiz) { 34 | iconValid = true; 35 | 36 | //Use https://www.base64encode.org/ to encode your image to base64 37 | return env->NewStringUTF( 38 | OBFUSCATE("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")); 39 | } 40 | 41 | JNIEXPORT jstring JNICALL 42 | Java_uk_lgl_modmenu_FloatingModMenuService_IconWebViewData(JNIEnv *env, jobject thiz) { 43 | iconValid = true; 44 | return NULL; 45 | } 46 | 47 | JNIEXPORT jobjectArray JNICALL 48 | Java_uk_lgl_modmenu_FloatingModMenuService_settingsList(JNIEnv *env, jobject activityObject) { 49 | jobjectArray ret; 50 | 51 | const char *features[] = { 52 | OBFUSCATE("Category_Settings"), 53 | OBFUSCATE("1000_Toggle_Save feature preferences"), //-1 is checked on Preferences.java 54 | OBFUSCATE("1001_Toggle_Auto size vertically"), 55 | OBFUSCATE("Category_Logcat"), 56 | OBFUSCATE("RichTextView_Save logcat if a bug occured and sent it to the modder. Clear logcat and reproduce bug again if the log file is too large"), 57 | OBFUSCATE("RichTextView_Saving logcat does not need file permission. Logcat location:" 58 | "
Android 11: /storage/emulated/0/Documents/" 59 | "
Android 10 and below: /storage/emulated/0/Android/data/(package name)/files/Mod Menu
"), 60 | OBFUSCATE("1002_Button_Save logcat to file"), 61 | OBFUSCATE("1003_Button_Clear logcat"), 62 | OBFUSCATE("Category_Menu"), 63 | OBFUSCATE("1004_Button_Close settings"), 64 | }; 65 | 66 | int Total_Feature = (sizeof features / 67 | sizeof features[0]); //Now you dont have to manually update the number everytime; 68 | ret = (jobjectArray) 69 | env->NewObjectArray(Total_Feature, env->FindClass(OBFUSCATE("java/lang/String")), 70 | env->NewStringUTF("")); 71 | int i; 72 | for (i = 0; i < Total_Feature; i++) 73 | env->SetObjectArrayElement(ret, i, env->NewStringUTF(features[i])); 74 | 75 | settingsValid = true; 76 | 77 | return (ret); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/Buffer.hpp: -------------------------------------------------------------------------------- 1 | /* Cydia Substrate - Powerful Code Insertion Platform 2 | * Copyright (C) 2008-2011 Jay Freeman (saurik) 3 | */ 4 | 5 | /* GNU Lesser General Public License, Version 3 {{{ */ 6 | /* 7 | * Substrate is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * Substrate is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with Substrate. If not, see . 19 | **/ 20 | /* }}} */ 21 | 22 | #ifndef SUBSTRATE_BUFFER_HPP 23 | #define SUBSTRATE_BUFFER_HPP 24 | 25 | #include 26 | 27 | template 28 | _disused static _finline void MSWrite(uint8_t *&buffer, Type_ value) { 29 | *reinterpret_cast(buffer) = value; 30 | buffer += sizeof(Type_); 31 | } 32 | 33 | _disused static _finline void MSWrite(uint8_t *&buffer, uint8_t *data, size_t size) { 34 | memcpy(buffer, data, size); 35 | buffer += size; 36 | } 37 | 38 | #endif//SUBSTRATE_BUFFER_HPP 39 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/CydiaSubstrate.h: -------------------------------------------------------------------------------- 1 | /* Cydia Substrate - Powerful Code Insertion Platform 2 | * Copyright (C) 2008-2011 Jay Freeman (saurik) 3 | */ 4 | 5 | /* GNU Lesser General Public License, Version 3 {{{ */ 6 | /* 7 | * Substrate is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * Substrate is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with Substrate. If not, see . 19 | **/ 20 | /* }}} */ 21 | 22 | #ifndef SUBSTRATE_H_ 23 | #define SUBSTRATE_H_ 24 | 25 | #ifdef __APPLE__ 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | #include 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | 34 | #include 35 | #include 36 | #endif 37 | 38 | #include 39 | #include 40 | 41 | #define _finline \ 42 | inline __attribute__((__always_inline__)) 43 | #define _disused \ 44 | __attribute__((__unused__)) 45 | 46 | #define _extern \ 47 | extern "C" __attribute__((__visibility__("default"))) 48 | 49 | #ifdef __cplusplus 50 | #define _default(value) = value 51 | #else 52 | #define _default(value) 53 | #endif 54 | 55 | #ifdef __cplusplus 56 | extern "C" { 57 | #endif 58 | 59 | bool MSHookProcess(pid_t pid, const char *library); 60 | 61 | typedef const void *MSImageRef; 62 | 63 | MSImageRef MSGetImageByName(const char *file); 64 | void *MSFindSymbol(MSImageRef image, const char *name); 65 | 66 | void MSHookFunction(void *symbol, void *replace, void **result); 67 | 68 | #ifdef __APPLE__ 69 | #ifdef __arm__ 70 | __attribute__((__deprecated__)) 71 | IMP MSHookMessage(Class _class, SEL sel, IMP imp, const char *prefix _default(NULL)); 72 | #endif 73 | void MSHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result); 74 | #endif 75 | 76 | #ifdef SubstrateInternal 77 | typedef void *SubstrateAllocatorRef; 78 | typedef struct __SubstrateProcess *SubstrateProcessRef; 79 | typedef struct __SubstrateMemory *SubstrateMemoryRef; 80 | 81 | SubstrateProcessRef SubstrateProcessCreate(SubstrateAllocatorRef allocator, pid_t pid); 82 | void SubstrateProcessRelease(SubstrateProcessRef process); 83 | 84 | SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size); 85 | void SubstrateMemoryRelease(SubstrateMemoryRef memory); 86 | #endif 87 | 88 | #ifdef __cplusplus 89 | } 90 | #endif 91 | 92 | #ifdef __cplusplus 93 | 94 | #ifdef SubstrateInternal 95 | struct SubstrateHookMemory { 96 | SubstrateMemoryRef handle_; 97 | 98 | SubstrateHookMemory(SubstrateProcessRef process, void *data, size_t size) : 99 | handle_(SubstrateMemoryCreate(NULL, NULL, data, size)) 100 | { 101 | } 102 | 103 | ~SubstrateHookMemory() { 104 | if (handle_ != NULL) 105 | SubstrateMemoryRelease(handle_); 106 | } 107 | }; 108 | #endif 109 | 110 | 111 | template 112 | static inline void MSHookFunction(Type_ *symbol, Type_ *replace, Type_ **result) { 113 | MSHookFunction( 114 | reinterpret_cast(symbol), 115 | reinterpret_cast(replace), 116 | reinterpret_cast(result) 117 | ); 118 | } 119 | 120 | template 121 | static inline void MSHookFunction(Type_ *symbol, Type_ *replace) { 122 | return MSHookFunction(symbol, replace, reinterpret_cast(NULL)); 123 | } 124 | 125 | template 126 | static inline void MSHookSymbol(Type_ *&value, const char *name, MSImageRef image = NULL) { 127 | value = reinterpret_cast(MSFindSymbol(image, name)); 128 | } 129 | 130 | template 131 | static inline void MSHookFunction(const char *name, Type_ *replace, Type_ **result = NULL) { 132 | Type_ *symbol; 133 | MSHookSymbol(symbol, name); 134 | return MSHookFunction(symbol, replace, result); 135 | } 136 | 137 | #endif 138 | 139 | #define MSHook(type, name, args...) \ 140 | _disused static type (*_ ## name)(args); \ 141 | static type $ ## name(args) 142 | 143 | #ifdef __cplusplus 144 | #define MSHake(name) \ 145 | &$ ## name, &_ ## name 146 | #else 147 | #define MSHake(name) \ 148 | &$ ## name, (void **) &_ ## name 149 | #endif 150 | 151 | 152 | #endif//SUBSTRATE_H_ 153 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/SubstrateARM.hpp: -------------------------------------------------------------------------------- 1 | /* Cydia Substrate - Powerful Code Insertion Platform 2 | * Copyright (C) 2008-2011 Jay Freeman (saurik) 3 | */ 4 | 5 | /* GNU Lesser General Public License, Version 3 {{{ */ 6 | /* 7 | * Substrate is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * Substrate is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with Substrate. If not, see . 19 | **/ 20 | /* }}} */ 21 | 22 | #ifndef SUBSTRATE_ARM_HPP 23 | #define SUBSTRATE_ARM_HPP 24 | 25 | enum A$r { 26 | A$r0, A$r1, A$r2, A$r3, 27 | A$r4, A$r5, A$r6, A$r7, 28 | A$r8, A$r9, A$r10, A$r11, 29 | A$r12, A$r13, A$r14, A$r15, 30 | A$sp = A$r13, 31 | A$lr = A$r14, 32 | A$pc = A$r15 33 | }; 34 | 35 | enum A$c { 36 | A$eq, A$ne, A$cs, A$cc, 37 | A$mi, A$pl, A$vs, A$vc, 38 | A$hi, A$ls, A$ge, A$lt, 39 | A$gt, A$le, A$al, 40 | A$hs = A$cs, 41 | A$lo = A$cc 42 | }; 43 | 44 | #define A$mrs_rm_cpsr(rd) /* mrs rd, cpsr */ \ 45 | (0xe10f0000 | ((rd) << 12)) 46 | #define A$msr_cpsr_f_rm(rm) /* msr cpsr_f, rm */ \ 47 | (0xe128f000 | (rm)) 48 | #define A$ldr_rd_$rn_im$(rd, rn, im) /* ldr rd, [rn, #im] */ \ 49 | (0xe5100000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs((int)(im))) 50 | #define A$str_rd_$rn_im$(rd, rn, im) /* sr rd, [rn, #im] */ \ 51 | (0xe5000000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs(im)) 52 | #define A$sub_rd_rn_$im(rd, rn, im) /* sub, rd, rn, #im */ \ 53 | (0xe2400000 | ((rn) << 16) | ((rd) << 12) | (im & 0xff)) 54 | #define A$blx_rm(rm) /* blx rm */ \ 55 | (0xe12fff30 | (rm)) 56 | #define A$mov_rd_rm(rd, rm) /* mov rd, rm */ \ 57 | (0xe1a00000 | ((rd) << 12) | (rm)) 58 | #define A$ldmia_sp$_$rs$(rs) /* ldmia sp!, {rs} */ \ 59 | (0xe8b00000 | (A$sp << 16) | (rs)) 60 | #define A$stmdb_sp$_$rs$(rs) /* stmdb sp!, {rs} */ \ 61 | (0xe9200000 | (A$sp << 16) | (rs)) 62 | #define A$stmia_sp$_$r0$ 0xe8ad0001 /* stmia sp!, {r0} */ 63 | #define A$bx_r0 0xe12fff10 /* bx r0 */ 64 | 65 | #endif//SUBSTRATE_ARM_HPP 66 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/SubstrateDebug.cpp: -------------------------------------------------------------------------------- 1 | /* Cydia Substrate - Powerful Code Insertion Platform 2 | * Copyright (C) 2008-2011 Jay Freeman (saurik) 3 | */ 4 | 5 | /* GNU Lesser General Public License, Version 3 {{{ */ 6 | /* 7 | * Substrate is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * Substrate is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with Substrate. If not, see . 19 | **/ 20 | /* }}} */ 21 | 22 | #include "SubstrateHook.h" 23 | #include "SubstrateDebug.hpp" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | _extern bool MSDebug; 31 | bool MSDebug = false; 32 | 33 | static char _MSHexChar(uint8_t value) { 34 | return value < 0x20 || value >= 0x80 ? '.' : value; 35 | } 36 | 37 | #define HexWidth_ 16 38 | #define HexDepth_ 4 39 | 40 | void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark) { 41 | const uint8_t *data((const uint8_t *) vdata); 42 | 43 | size_t i(0), j; 44 | 45 | char d[256]; 46 | size_t b(0); 47 | d[0] = '\0'; 48 | 49 | while (i != size) { 50 | if (i % HexWidth_ == 0) { 51 | if (mark != NULL) 52 | b += sprintf(d + b, OBFUSCATE("\n[%s] "), mark); 53 | b += sprintf(d + b, OBFUSCATE("0x%.3zx:"), i); 54 | } 55 | 56 | b += sprintf(d + b, " "); 57 | 58 | for (size_t q(0); q != stride; ++q) 59 | b += sprintf(d + b, OBFUSCATE("%.2x"), data[i + stride - q - 1]); 60 | 61 | i += stride; 62 | 63 | for (size_t q(1); q != stride; ++q) 64 | b += sprintf(d + b, " "); 65 | 66 | if (i % HexDepth_ == 0) 67 | b += sprintf(d + b, " "); 68 | 69 | if (i % HexWidth_ == 0) { 70 | b += sprintf(d + b, " "); 71 | for (j = i - HexWidth_; j != i; ++j) 72 | b += sprintf(d + b, "%c", _MSHexChar(data[j])); 73 | 74 | lprintf("%s", d); 75 | b = 0; 76 | d[0] = '\0'; 77 | } 78 | } 79 | 80 | if (i % HexWidth_ != 0) { 81 | for (j = i % HexWidth_; j != HexWidth_; ++j) 82 | b += sprintf(d + b, " "); 83 | for (j = 0; j != (HexWidth_ - i % HexWidth_ + HexDepth_ - 1) / HexDepth_; ++j) 84 | b += sprintf(d + b, " "); 85 | b += sprintf(d + b, " "); 86 | for (j = i / HexWidth_ * HexWidth_; j != i; ++j) 87 | b += sprintf(d + b, OBFUSCATE("%c"), _MSHexChar(data[j])); 88 | 89 | // lprintf("%s", d); 90 | b = 0; 91 | d[0] = '\0'; 92 | } 93 | } 94 | 95 | void MSLogHex(const void *vdata, size_t size, const char *mark) { 96 | return MSLogHexEx(vdata, size, 1, mark); 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/SubstrateDebug.hpp: -------------------------------------------------------------------------------- 1 | /* Cydia Substrate - Powerful Code Insertion Platform 2 | * Copyright (C) 2008-2011 Jay Freeman (saurik) 3 | */ 4 | 5 | /* GNU Lesser General Public License, Version 3 {{{ */ 6 | /* 7 | * Substrate is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * Substrate is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with Substrate. If not, see . 19 | **/ 20 | /* }}} */ 21 | 22 | #ifndef SUBSTRATE_DEBUG_HPP 23 | #define SUBSTRATE_DEBUG_HPP 24 | 25 | #include "SubstrateLog.hpp" 26 | #define lprintf(format, ...) \ 27 | MSLog(MSLogLevelNotice, format, ## __VA_ARGS__) 28 | 29 | extern "C" bool MSDebug; 30 | void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark = 0); 31 | void MSLogHex(const void *vdata, size_t size, const char *mark = 0); 32 | 33 | #endif//SUBSTRATE_DEBUG_HPP 34 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/SubstrateHook.h: -------------------------------------------------------------------------------- 1 | #ifndef __SUBSTRATEHOOK_H__ 2 | #define __SUBSTRATEHOOK_H__ 3 | 4 | 5 | #include 6 | 7 | #define _extern extern "C" __attribute__((__visibility__("hidden"))) 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | void MSHookFunction(void *symbol, void *replace, void **result); 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/SubstrateLog.hpp: -------------------------------------------------------------------------------- 1 | /* Cydia Substrate - Powerful Code Insertion Platform 2 | * Copyright (C) 2008-2011 Jay Freeman (saurik) 3 | */ 4 | 5 | /* GNU Lesser General Public License, Version 3 {{{ */ 6 | /* 7 | * Substrate is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * Substrate is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with Substrate. If not, see . 19 | **/ 20 | /* }}} */ 21 | 22 | #ifndef SUBSTRATE_LOG_HPP 23 | #define SUBSTRATE_LOG_HPP 24 | 25 | #if 0 26 | #include 27 | 28 | #define MSLog(level, format, ...) ((void)__android_log_print(level, "NNNN", format, __VA_ARGS__)) 29 | 30 | #define MSLogLevelNotice ANDROID_LOG_INFO 31 | #define MSLogLevelWarning ANDROID_LOG_WARN 32 | #define MSLogLevelError ANDROID_LOG_ERROR 33 | 34 | #else 35 | 36 | #define MSLog(level, format, ...) printf(format, __VA_ARGS__) 37 | 38 | #endif 39 | 40 | #endif//SUBSTRATE_LOG_HPP 41 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/SubstratePosixMemory.cpp: -------------------------------------------------------------------------------- 1 | /* Cydia Substrate - Powerful Code Insertion Platform 2 | * Copyright (C) 2008-2011 Jay Freeman (saurik) 3 | */ 4 | 5 | /* GNU Lesser General Public License, Version 3 {{{ */ 6 | /* 7 | * Substrate is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * Substrate is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with Substrate. If not, see . 19 | **/ 20 | /* }}} */ 21 | 22 | #define SubstrateInternal 23 | #include "CydiaSubstrate.h" 24 | #include "SubstrateLog.hpp" 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | extern "C" void __clear_cache (void *beg, void *end); 34 | 35 | struct __SubstrateMemory { 36 | void *address_; 37 | size_t width_; 38 | 39 | __SubstrateMemory(void *address, size_t width) : 40 | address_(address), 41 | width_(width) 42 | { 43 | } 44 | }; 45 | 46 | extern "C" SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size) { 47 | if (allocator != NULL) { 48 | MSLog(MSLogLevelError, OBFUSCATE("MS:Error:allocator != %d"), 0); 49 | return NULL; 50 | } 51 | 52 | if (size == 0) 53 | return NULL; 54 | 55 | int page(getpagesize()); 56 | 57 | uintptr_t base(reinterpret_cast(data) / page * page); 58 | size_t width(((reinterpret_cast(data) + size - 1) / page + 1) * page - base); 59 | void *address(reinterpret_cast(base)); 60 | 61 | if (mprotect(address, width, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) { 62 | MSLog(MSLogLevelError, OBFUSCATE("MS:Error:mprotect() = %d"), errno); 63 | return NULL; 64 | } 65 | 66 | return new __SubstrateMemory(address, width); 67 | } 68 | 69 | extern "C" void SubstrateMemoryRelease(SubstrateMemoryRef memory) { 70 | if (mprotect(memory->address_, memory->width_, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) 71 | MSLog(MSLogLevelError, OBFUSCATE("MS:Error:mprotect() = %d"), errno); 72 | 73 | __clear_cache(reinterpret_cast(memory->address_), reinterpret_cast(memory->address_) + memory->width_); 74 | 75 | delete memory; 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/SubstrateX86.hpp: -------------------------------------------------------------------------------- 1 | /* Cydia Substrate - Powerful Code Insertion Platform 2 | * Copyright (C) 2008-2011 Jay Freeman (saurik) 3 | */ 4 | 5 | /* GNU Lesser General Public License, Version 3 {{{ */ 6 | /* 7 | * Substrate is free software: you can redistribute it and/or modify it under 8 | * the terms of the GNU Lesser General Public License as published by the 9 | * Free Software Foundation, either version 3 of the License, or (at your 10 | * option) any later version. 11 | * 12 | * Substrate is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15 | * License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with Substrate. If not, see . 19 | **/ 20 | /* }}} */ 21 | 22 | #ifndef SUBSTRATE_X86_HPP 23 | #define SUBSTRATE_X86_HPP 24 | 25 | #include "Buffer.hpp" 26 | 27 | #ifdef __LP64__ 28 | static const bool ia32 = false; 29 | #else 30 | static const bool ia32 = true; 31 | #endif 32 | 33 | enum I$r { 34 | I$rax, I$rcx, I$rdx, I$rbx, 35 | I$rsp, I$rbp, I$rsi, I$rdi, 36 | I$r8, I$r9, I$r10, I$r11, 37 | I$r12, I$r13, I$r14, I$r15, 38 | }; 39 | 40 | _disused static bool MSIs32BitOffset(uintptr_t target, uintptr_t source) { 41 | intptr_t offset(target - source); 42 | return int32_t(offset) == offset; 43 | } 44 | 45 | _disused static size_t MSSizeOfSkip() { 46 | return 5; 47 | } 48 | 49 | _disused static size_t MSSizeOfPushPointer(uintptr_t target) { 50 | return uint64_t(target) >> 32 == 0 ? 5 : 13; 51 | } 52 | 53 | _disused static size_t MSSizeOfPushPointer(void *target) { 54 | return MSSizeOfPushPointer(reinterpret_cast(target)); 55 | } 56 | 57 | _disused static size_t MSSizeOfJump(bool blind, uintptr_t target, uintptr_t source = 0) { 58 | if (ia32 || !blind && MSIs32BitOffset(target, source + 5)) 59 | return MSSizeOfSkip(); 60 | else 61 | return MSSizeOfPushPointer(target) + 1; 62 | } 63 | 64 | _disused static size_t MSSizeOfJump(uintptr_t target, uintptr_t source) { 65 | return MSSizeOfJump(false, target, source); 66 | } 67 | 68 | _disused static size_t MSSizeOfJump(uintptr_t target) { 69 | return MSSizeOfJump(true, target); 70 | } 71 | 72 | _disused static size_t MSSizeOfJump(void *target, void *source) { 73 | return MSSizeOfJump(reinterpret_cast(target), reinterpret_cast(source)); 74 | } 75 | 76 | _disused static size_t MSSizeOfJump(void *target) { 77 | return MSSizeOfJump(reinterpret_cast(target)); 78 | } 79 | 80 | _disused static void MSWriteSkip(uint8_t *¤t, ssize_t size) { 81 | MSWrite(current, 0xe9); 82 | MSWrite(current, size); 83 | } 84 | 85 | _disused static void MSPushPointer(uint8_t *¤t, uintptr_t target) { 86 | MSWrite(current, 0x68); 87 | MSWrite(current, target); 88 | 89 | if (uint32_t high = uint64_t(target) >> 32) { 90 | MSWrite(current, 0xc7); 91 | MSWrite(current, 0x44); 92 | MSWrite(current, 0x24); 93 | MSWrite(current, 0x04); 94 | MSWrite(current, high); 95 | } 96 | } 97 | 98 | _disused static void MSPushPointer(uint8_t *¤t, void *target) { 99 | return MSPushPointer(current, reinterpret_cast(target)); 100 | } 101 | 102 | _disused static void MSWriteCall(uint8_t *¤t, I$r target) { 103 | if (target >> 3 != 0) 104 | MSWrite(current, 0x40 | (target & 0x08) >> 3); 105 | MSWrite(current, 0xff); 106 | MSWrite(current, 0xd0 | target & 0x07); 107 | } 108 | 109 | _disused static void MSWriteCall(uint8_t *¤t, uintptr_t target) { 110 | uintptr_t source(reinterpret_cast(current)); 111 | 112 | if (ia32 || MSIs32BitOffset(target, source + 5)) { 113 | MSWrite(current, 0xe8); 114 | MSWrite(current, target - (source + 5)); 115 | } else { 116 | MSPushPointer(current, target); 117 | 118 | MSWrite(current, 0x83); 119 | MSWrite(current, 0xc4); 120 | MSWrite(current, 0x08); 121 | 122 | MSWrite(current, 0x67); 123 | MSWrite(current, 0xff); 124 | MSWrite(current, 0x54); 125 | MSWrite(current, 0x24); 126 | MSWrite(current, 0xf8); 127 | } 128 | } 129 | 130 | template 131 | _disused static void MSWriteCall(uint8_t *¤t, Type_ *target) { 132 | return MSWriteCall(current, reinterpret_cast(target)); 133 | } 134 | 135 | _disused static void MSWriteJump(uint8_t *¤t, uintptr_t target) { 136 | uintptr_t source(reinterpret_cast(current)); 137 | 138 | if (ia32 || MSIs32BitOffset(target, source + 5)) 139 | MSWriteSkip(current, target - (source + 5)); 140 | else { 141 | MSPushPointer(current, target); 142 | MSWrite(current, 0xc3); 143 | } 144 | } 145 | 146 | _disused static void MSWriteJump(uint8_t *¤t, void *target) { 147 | return MSWriteJump(current, reinterpret_cast(target)); 148 | } 149 | 150 | _disused static void MSWriteJump(uint8_t *¤t, I$r target) { 151 | if (target >> 3 != 0) 152 | MSWrite(current, 0x40 | (target & 0x08) >> 3); 153 | MSWrite(current, 0xff); 154 | MSWrite(current, 0xe0 | target & 0x07); 155 | } 156 | 157 | _disused static void MSWritePop(uint8_t *¤t, uint8_t target) { 158 | if (target >> 3 != 0) 159 | MSWrite(current, 0x40 | (target & 0x08) >> 3); 160 | MSWrite(current, 0x58 | target & 0x07); 161 | } 162 | 163 | _disused static size_t MSSizeOfPop(uint8_t target) { 164 | return target >> 3 != 0 ? 2 : 1; 165 | } 166 | 167 | _disused static void MSWritePush(uint8_t *¤t, I$r target) { 168 | if (target >> 3 != 0) 169 | MSWrite(current, 0x40 | (target & 0x08) >> 3); 170 | MSWrite(current, 0x50 | target & 0x07); 171 | } 172 | 173 | _disused static void MSWriteAdd(uint8_t *¤t, I$r target, uint8_t source) { 174 | MSWrite(current, 0x83); 175 | MSWrite(current, 0xc4 | target & 0x07); 176 | MSWrite(current, source); 177 | } 178 | 179 | _disused static void MSWriteSet64(uint8_t *¤t, I$r target, uintptr_t source) { 180 | MSWrite(current, 0x48 | (target & 0x08) >> 3 << 2); 181 | MSWrite(current, 0xb8 | target & 0x7); 182 | MSWrite(current, source); 183 | } 184 | 185 | template 186 | _disused static void MSWriteSet64(uint8_t *¤t, I$r target, Type_ *source) { 187 | return MSWriteSet64(current, target, reinterpret_cast(source)); 188 | } 189 | 190 | _disused static void MSWriteMove64(uint8_t *¤t, uint8_t source, uint8_t target) { 191 | MSWrite(current, 0x48 | (target & 0x08) >> 3 << 2 | (source & 0x08) >> 3); 192 | MSWrite(current, 0x8b); 193 | MSWrite(current, (target & 0x07) << 3 | source & 0x07); 194 | } 195 | 196 | _disused static size_t MSSizeOfMove64() { 197 | return 3; 198 | } 199 | 200 | #endif//SUBSTRATE_X86_HPP 201 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/SymbolFinder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "SymbolFinder.h" 11 | 12 | #define TAG "MSHook" 13 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) 14 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) 15 | /* memory map for libraries */ 16 | #define MAX_NAME_LEN 256 17 | #define MEMORY_ONLY "[memory]" 18 | struct mm { 19 | char name[MAX_NAME_LEN]; 20 | unsigned long start, end; 21 | }; 22 | 23 | typedef struct symtab *symtab_t; 24 | struct symlist { 25 | Elf32_Sym *sym; /* symbols */ 26 | char *str; /* symbol strings */ 27 | unsigned num; /* number of symbols */ 28 | }; 29 | struct symtab { 30 | struct symlist *st; /* "static" symbols */ 31 | struct symlist *dyn; /* dynamic symbols */ 32 | }; 33 | 34 | static void *xmalloc(size_t size) { 35 | void *p; 36 | p = malloc(size); 37 | if (!p) { 38 | printf(OBFUSCATE("Out of memory\n")); 39 | exit(1); 40 | } 41 | return p; 42 | } 43 | 44 | static int my_pread(int fd, void *buf, size_t count, off_t offset) { 45 | lseek(fd, offset, SEEK_SET); 46 | return read(fd, buf, count); 47 | } 48 | 49 | static struct symlist *get_syms(int fd, Elf32_Shdr *symh, Elf32_Shdr *strh) { 50 | struct symlist *sl, *ret; 51 | int rv; 52 | 53 | ret = NULL; 54 | sl = (struct symlist *) xmalloc(sizeof(struct symlist)); 55 | sl->str = NULL; 56 | sl->sym = NULL; 57 | 58 | /* sanity */ 59 | if (symh->sh_size % sizeof(Elf32_Sym)) { 60 | //printf("elf_error\n"); 61 | goto out; 62 | } 63 | 64 | /* symbol table */ 65 | sl->num = symh->sh_size / sizeof(Elf32_Sym); 66 | sl->sym = (Elf32_Sym *) xmalloc(symh->sh_size); 67 | rv = my_pread(fd, sl->sym, symh->sh_size, symh->sh_offset); 68 | if (0 > rv) { 69 | //perror("read"); 70 | goto out; 71 | } 72 | if (rv != symh->sh_size) { 73 | //printf("elf error\n"); 74 | goto out; 75 | } 76 | 77 | /* string table */ 78 | sl->str = (char *) xmalloc(strh->sh_size); 79 | rv = my_pread(fd, sl->str, strh->sh_size, strh->sh_offset); 80 | if (0 > rv) { 81 | //perror("read"); 82 | goto out; 83 | } 84 | if (rv != strh->sh_size) { 85 | //printf("elf error"); 86 | goto out; 87 | } 88 | 89 | ret = sl; 90 | out: 91 | return ret; 92 | } 93 | 94 | static int do_load(int fd, symtab_t symtab) { 95 | int rv; 96 | size_t size; 97 | Elf32_Ehdr ehdr; 98 | Elf32_Shdr *shdr = NULL, *p; 99 | Elf32_Shdr *dynsymh, *dynstrh; 100 | Elf32_Shdr *symh, *strh; 101 | char *shstrtab = NULL; 102 | int i; 103 | int ret = -1; 104 | 105 | /* elf header */ 106 | rv = read(fd, &ehdr, sizeof(ehdr)); 107 | if (0 > rv) { 108 | LOGD(OBFUSCATE("read\n")); 109 | goto out; 110 | } 111 | if (rv != sizeof(ehdr)) { 112 | LOGD(OBFUSCATE("elf error 1\n")); 113 | goto out; 114 | } 115 | if (strncmp((const char *) ELFMAG, (const char *) ehdr.e_ident, SELFMAG)) { /* sanity */ 116 | LOGD(OBFUSCATE("not an elf\n")); 117 | goto out; 118 | } 119 | if (sizeof(Elf32_Shdr) != ehdr.e_shentsize) { /* sanity */ 120 | LOGD(OBFUSCATE("elf error 2\n")); 121 | goto out; 122 | } 123 | 124 | /* section header table */ 125 | size = ehdr.e_shentsize * ehdr.e_shnum; 126 | shdr = (Elf32_Shdr *) xmalloc(size); 127 | rv = my_pread(fd, shdr, size, ehdr.e_shoff); 128 | if (0 > rv) { 129 | LOGD(OBFUSCATE("read\n")); 130 | goto out; 131 | } 132 | if (rv != size) { 133 | LOGD(OBFUSCATE("elf error 3 %d %d\n"), rv, size); 134 | goto out; 135 | } 136 | 137 | /* section header string table */ 138 | size = shdr[ehdr.e_shstrndx].sh_size; 139 | shstrtab = (char *) xmalloc(size); 140 | rv = my_pread(fd, shstrtab, size, shdr[ehdr.e_shstrndx].sh_offset); 141 | if (0 > rv) { 142 | LOGD(OBFUSCATE("read\n")); 143 | goto out; 144 | } 145 | if (rv != size) { 146 | LOGD(OBFUSCATE("elf error 4 %d %d\n"), rv, size); 147 | goto out; 148 | } 149 | 150 | /* symbol table headers */ 151 | symh = dynsymh = NULL; 152 | strh = dynstrh = NULL; 153 | for (i = 0, p = shdr; i < ehdr.e_shnum; i++, p++) 154 | if (SHT_SYMTAB == p->sh_type) { 155 | if (symh) { 156 | LOGD(OBFUSCATE("too many symbol tables\n")); 157 | goto out; 158 | } 159 | symh = p; 160 | } else if (SHT_DYNSYM == p->sh_type) { 161 | if (dynsymh) { 162 | LOGD(OBFUSCATE("too many symbol tables\n")); 163 | goto out; 164 | } 165 | dynsymh = p; 166 | } else if (SHT_STRTAB == p->sh_type 167 | && !strncmp(shstrtab + p->sh_name, OBFUSCATE(".strtab"), 7)) { 168 | if (strh) { 169 | LOGD(OBFUSCATE("too many string tables\n")); 170 | goto out; 171 | } 172 | strh = p; 173 | } else if (SHT_STRTAB == p->sh_type 174 | && !strncmp(shstrtab + p->sh_name, OBFUSCATE(".dynstr"), 7)) { 175 | if (dynstrh) { 176 | LOGD(OBFUSCATE("too many string tables\n")); 177 | goto out; 178 | } 179 | dynstrh = p; 180 | } 181 | /* sanity checks */ 182 | if ((!dynsymh && dynstrh) || (dynsymh && !dynstrh)) { 183 | LOGD(OBFUSCATE("bad dynamic symbol table\n")); 184 | goto out; 185 | } 186 | if ((!symh && strh) || (symh && !strh)) { 187 | LOGD(OBFUSCATE("bad symbol table\n")); 188 | goto out; 189 | } 190 | if (!dynsymh && !symh) { 191 | LOGD(OBFUSCATE("no symbol table\n")); 192 | goto out; 193 | } 194 | 195 | /* symbol tables */ 196 | if (dynsymh) 197 | symtab->dyn = get_syms(fd, dynsymh, dynstrh); 198 | if (symh) 199 | symtab->st = get_syms(fd, symh, strh); 200 | ret = 0; 201 | out: 202 | free(shstrtab); 203 | free(shdr); 204 | return ret; 205 | } 206 | 207 | static symtab_t load_symtab(char *filename) { 208 | int fd; 209 | symtab_t symtab; 210 | 211 | symtab = (symtab_t) xmalloc(sizeof(*symtab)); 212 | memset(symtab, 0, sizeof(*symtab)); 213 | 214 | fd = open(filename, O_RDONLY); 215 | if (0 > fd) { 216 | LOGE(OBFUSCATE("%s open\n"), __func__); 217 | return NULL; 218 | } 219 | if (0 > do_load(fd, symtab)) { 220 | LOGE(OBFUSCATE("Error ELF parsing %s\n"), filename); 221 | free(symtab); 222 | symtab = NULL; 223 | } 224 | close(fd); 225 | return symtab; 226 | } 227 | 228 | static int load_memmap(pid_t pid, struct mm *mm, int *nmmp) { 229 | size_t buf_size = 0x40000; 230 | char *p_buf = (char *) malloc(buf_size); // increase this if needed for larger "maps" 231 | char name[MAX_NAME_LEN] = {0}; 232 | char *p; 233 | unsigned long start, end; 234 | struct mm *m; 235 | int nmm = 0; 236 | int fd, rv; 237 | int i; 238 | 239 | sprintf(p_buf, OBFUSCATE("/proc/%d/maps"), pid); 240 | fd = open(p_buf, O_RDONLY); 241 | if (0 > fd) { 242 | LOGE(OBFUSCATE("Can't open %s for reading\n"), p_buf); 243 | free(p_buf); 244 | return -1; 245 | } 246 | 247 | /* Zero to ensure data is null terminated */ 248 | memset(p_buf, 0, buf_size); 249 | 250 | p = p_buf; 251 | while (1) { 252 | rv = read(fd, p, buf_size - (p - p_buf)); 253 | if (0 > rv) { 254 | LOGE(OBFUSCATE("%s read"), __FUNCTION__); 255 | free(p_buf); 256 | return -1; 257 | } 258 | if (0 == rv) 259 | break; 260 | p += rv; 261 | if (p - p_buf >= buf_size) { 262 | LOGE(OBFUSCATE("Too many memory mapping\n")); 263 | free(p_buf); 264 | return -1; 265 | } 266 | } 267 | close(fd); 268 | 269 | p = strtok(p_buf, "\n"); 270 | m = mm; 271 | while (p) { 272 | /* parse current map line */ 273 | rv = sscanf(p, OBFUSCATE("%08lx-%08lx %*s %*s %*s %*s %s\n"), &start, &end, name); 274 | 275 | p = strtok(NULL, "\n"); 276 | 277 | if (rv == 2) { 278 | m = &mm[nmm++]; 279 | m->start = start; 280 | m->end = end; 281 | memcpy(m->name, MEMORY_ONLY, sizeof(MEMORY_ONLY)); 282 | continue; 283 | } 284 | 285 | /* search backward for other mapping with same name */ 286 | for (i = nmm - 1; i >= 0; i--) { 287 | m = &mm[i]; 288 | if (!strcmp(m->name, name)) 289 | break; 290 | } 291 | 292 | if (i >= 0) { 293 | if (start < m->start) 294 | m->start = start; 295 | if (end > m->end) 296 | m->end = end; 297 | } else { 298 | /* new entry */ 299 | m = &mm[nmm++]; 300 | m->start = start; 301 | m->end = end; 302 | memcpy(m->name, name, strlen(name)); 303 | } 304 | } 305 | 306 | *nmmp = nmm; 307 | free(p_buf); 308 | return 0; 309 | } 310 | 311 | /* Find libc in MM, storing no more than LEN-1 chars of 312 | its name in NAME and set START to its starting 313 | address. If libc cannot be found return -1 and 314 | leave NAME and START untouched. Otherwise return 0 315 | and null-terminated NAME. */ 316 | static int find_libname(const char *libn, char *name, int len, unsigned long *start, 317 | struct mm *mm, int nmm) { 318 | int i; 319 | struct mm *m; 320 | char *p; 321 | for (i = 0, m = mm; i < nmm; i++, m++) { 322 | if (!strcmp(m->name, MEMORY_ONLY)) 323 | continue; 324 | p = strrchr(m->name, '/'); 325 | if (!p) 326 | continue; 327 | p++; 328 | if (strncmp(libn, p, strlen(libn))) 329 | continue; 330 | p += strlen(libn); 331 | 332 | /* here comes our crude test -> 'libc.so' or 'libc-[0-9]' */ 333 | if (!strncmp(OBFUSCATE("so"), p, 2) || 1) // || (p[0] == '-' && isdigit(p[1]))) 334 | break; 335 | } 336 | if (i >= nmm) 337 | /* not found */ 338 | return -1; 339 | 340 | *start = m->start; 341 | strncpy(name, m->name, len); 342 | if (strlen(m->name) >= len) 343 | name[len - 1] = '\0'; 344 | 345 | mprotect((void *) m->start, m->end - m->start, 346 | PROT_READ | PROT_WRITE | PROT_EXEC); 347 | return 0; 348 | } 349 | 350 | static int lookup2(struct symlist *sl, unsigned char type, char *name, 351 | unsigned long *val) { 352 | Elf32_Sym *p; 353 | int len; 354 | int i; 355 | 356 | len = strlen(name); 357 | for (i = 0, p = sl->sym; i < sl->num; i++, p++) { 358 | //LOGD("name: %s %x\n", sl->str+p->st_name, p->st_value) 359 | if (!strncmp(sl->str + p->st_name, name, len) 360 | && *(sl->str + p->st_name + len) == 0 361 | && ELF32_ST_TYPE(p->st_info) == type) { 362 | //if (p->st_value != 0) { 363 | *val = p->st_value; 364 | return 0; 365 | //} 366 | } 367 | } 368 | return -1; 369 | } 370 | 371 | static int lookup_sym(symtab_t s, unsigned char type, char *name, 372 | unsigned long *val) { 373 | if (s->dyn && !lookup2(s->dyn, type, name, val)) 374 | return 0; 375 | if (s->st && !lookup2(s->st, type, name, val)) 376 | return 0; 377 | return -1; 378 | } 379 | 380 | static int lookup_func_sym(symtab_t s, char *name, unsigned long *val) { 381 | return lookup_sym(s, STT_FUNC, name, val); 382 | } 383 | 384 | int find_name(pid_t pid, const char *name, const char *libn, 385 | unsigned long *addr) { 386 | struct mm mm[1000] = {0}; 387 | unsigned long libcaddr; 388 | int nmm; 389 | char libc[1024] = {0}; 390 | symtab_t s; 391 | 392 | if (0 > load_memmap(pid, mm, &nmm)) { 393 | LOGD(OBFUSCATE("cannot read memory map\n")); 394 | return -1; 395 | } 396 | if (0 397 | > find_libname((char *) libn, (char *) libc, sizeof(libc), 398 | &libcaddr, mm, nmm)) { 399 | LOGD(OBFUSCATE("cannot find lib: %s\n"), libn); 400 | return -1; 401 | } 402 | //LOGD("lib: >%s<\n", libc) 403 | s = load_symtab(libc); 404 | if (!s) { 405 | LOGD(OBFUSCATE("cannot read symbol table\n")); 406 | return -1; 407 | } 408 | if (0 > lookup_func_sym(s, (char *) name, addr)) { 409 | LOGD(OBFUSCATE("cannot find function: %s\n"), name); 410 | return -1; 411 | } 412 | *addr += libcaddr; 413 | return 0; 414 | } 415 | 416 | int find_libbase(pid_t pid, const char *libn, unsigned long *addr) { 417 | struct mm mm[1000] = {0}; 418 | unsigned long libcaddr; 419 | int nmm; 420 | char libc[1024] = {0}; 421 | symtab_t s; 422 | 423 | if (0 > load_memmap(pid, mm, &nmm)) { 424 | LOGD(OBFUSCATE("cannot read memory map\n")); 425 | return -1; 426 | } 427 | if (0 > find_libname(libn, libc, sizeof(libc), &libcaddr, mm, nmm)) { 428 | LOGD(OBFUSCATE("cannot find lib\n")); 429 | return -1; 430 | } 431 | *addr = libcaddr; 432 | return 0; 433 | } 434 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/SymbolFinder.h: -------------------------------------------------------------------------------- 1 | #ifndef SYMBOL_FINDER 2 | #define SYMBOL_FINDER 3 | 4 | #include 5 | 6 | extern int find_name(pid_t pid, const char *name,const char *libn, unsigned long *addr); 7 | extern int find_libbase(pid_t pid, const char *libn, unsigned long *addr); 8 | #endif -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/hde64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Hacker Disassembler Engine 64 C 3 | * Copyright (c) 2008-2009, Vyacheslav Patkov. 4 | * All rights reserved. 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include "hde64.h" 12 | #include "table64.h" 13 | 14 | unsigned int hde64_disasm(const void *code, hde64s *hs) 15 | { 16 | uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; 17 | uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; 18 | uint8_t op64 = 0; 19 | 20 | memset(hs,0,sizeof(hde64s)); 21 | char *tmp=(char*)hs; 22 | 23 | for (x = 16; x; x--) 24 | switch (c = *p++) { 25 | case 0xf3: 26 | hs->p_rep = c; 27 | pref |= PRE_F3; 28 | break; 29 | case 0xf2: 30 | hs->p_rep = c; 31 | pref |= PRE_F2; 32 | break; 33 | case 0xf0: 34 | hs->p_lock = c; 35 | pref |= PRE_LOCK; 36 | break; 37 | case 0x26: case 0x2e: case 0x36: 38 | case 0x3e: case 0x64: case 0x65: 39 | hs->p_seg = c; 40 | pref |= PRE_SEG; 41 | break; 42 | case 0x66: 43 | hs->p_66 = c; 44 | pref |= PRE_66; 45 | break; 46 | case 0x67: 47 | hs->p_67 = c; 48 | pref |= PRE_67; 49 | break; 50 | default: 51 | goto pref_done; 52 | } 53 | pref_done: 54 | 55 | hs->flags = (uint32_t)pref << 23; 56 | 57 | if (!pref) 58 | pref |= PRE_NONE; 59 | 60 | if ((c & 0xf0) == 0x40) { 61 | hs->flags |= F_PREFIX_REX; 62 | if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) 63 | op64++; 64 | hs->rex_r = (c & 7) >> 2; 65 | hs->rex_x = (c & 3) >> 1; 66 | hs->rex_b = c & 1; 67 | if (((c = *p++) & 0xf0) == 0x40) { 68 | opcode = c; 69 | goto error_opcode; 70 | } 71 | } 72 | 73 | if ((hs->opcode = c) == 0x0f) { 74 | hs->opcode2 = c = *p++; 75 | ht += DELTA_OPCODES; 76 | } else if (c >= 0xa0 && c <= 0xa3) { 77 | op64++; 78 | if (pref & PRE_67) 79 | pref |= PRE_66; 80 | else 81 | pref &= ~PRE_66; 82 | } 83 | 84 | opcode = c; 85 | cflags = ht[ht[opcode / 4] + (opcode % 4)]; 86 | 87 | if (cflags == C_ERROR) { 88 | error_opcode: 89 | hs->flags |= F_ERROR | F_ERROR_OPCODE; 90 | cflags = 0; 91 | if ((opcode & -3) == 0x24) 92 | cflags++; 93 | } 94 | 95 | x = 0; 96 | if (cflags & C_GROUP) { 97 | uint16_t t; 98 | t = *(uint16_t *)(ht + (cflags & 0x7f)); 99 | cflags = (uint8_t)t; 100 | x = (uint8_t)(t >> 8); 101 | } 102 | 103 | if (hs->opcode2) { 104 | ht = hde64_table + DELTA_PREFIXES; 105 | if (ht[ht[opcode / 4] + (opcode % 4)] & pref) 106 | hs->flags |= F_ERROR | F_ERROR_OPCODE; 107 | } 108 | 109 | if (cflags & C_MODRM) { 110 | hs->flags |= F_MODRM; 111 | hs->modrm = c = *p++; 112 | hs->modrm_mod = m_mod = c >> 6; 113 | hs->modrm_rm = m_rm = c & 7; 114 | hs->modrm_reg = m_reg = (c & 0x3f) >> 3; 115 | 116 | if (x && ((x << m_reg) & 0x80)) 117 | hs->flags |= F_ERROR | F_ERROR_OPCODE; 118 | 119 | if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { 120 | uint8_t t = opcode - 0xd9; 121 | if (m_mod == 3) { 122 | ht = hde64_table + DELTA_FPU_MODRM + t*8; 123 | t = ht[m_reg] << m_rm; 124 | } else { 125 | ht = hde64_table + DELTA_FPU_REG; 126 | t = ht[t] << m_reg; 127 | } 128 | if (t & 0x80) 129 | hs->flags |= F_ERROR | F_ERROR_OPCODE; 130 | } 131 | 132 | if (pref & PRE_LOCK) { 133 | if (m_mod == 3) { 134 | hs->flags |= F_ERROR | F_ERROR_LOCK; 135 | } else { 136 | uint8_t *table_end, op = opcode; 137 | if (hs->opcode2) { 138 | ht = hde64_table + DELTA_OP2_LOCK_OK; 139 | table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; 140 | } else { 141 | ht = hde64_table + DELTA_OP_LOCK_OK; 142 | table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; 143 | op &= -2; 144 | } 145 | for (; ht != table_end; ht++) 146 | if (*ht++ == op) { 147 | if (!((*ht << m_reg) & 0x80)) 148 | goto no_lock_error; 149 | else 150 | break; 151 | } 152 | hs->flags |= F_ERROR | F_ERROR_LOCK; 153 | no_lock_error: 154 | ; 155 | } 156 | } 157 | 158 | if (hs->opcode2) { 159 | switch (opcode) { 160 | case 0x20: case 0x22: 161 | m_mod = 3; 162 | if (m_reg > 4 || m_reg == 1) 163 | goto error_operand; 164 | else 165 | goto no_error_operand; 166 | case 0x21: case 0x23: 167 | m_mod = 3; 168 | if (m_reg == 4 || m_reg == 5) 169 | goto error_operand; 170 | else 171 | goto no_error_operand; 172 | } 173 | } else { 174 | switch (opcode) { 175 | case 0x8c: 176 | if (m_reg > 5) 177 | goto error_operand; 178 | else 179 | goto no_error_operand; 180 | case 0x8e: 181 | if (m_reg == 1 || m_reg > 5) 182 | goto error_operand; 183 | else 184 | goto no_error_operand; 185 | } 186 | } 187 | 188 | if (m_mod == 3) { 189 | uint8_t *table_end; 190 | if (hs->opcode2) { 191 | ht = hde64_table + DELTA_OP2_ONLY_MEM; 192 | table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; 193 | } else { 194 | ht = hde64_table + DELTA_OP_ONLY_MEM; 195 | table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; 196 | } 197 | for (; ht != table_end; ht += 2) 198 | if (*ht++ == opcode) { 199 | if (*ht++ & pref && !((*ht << m_reg) & 0x80)) 200 | goto error_operand; 201 | else 202 | break; 203 | } 204 | goto no_error_operand; 205 | } else if (hs->opcode2) { 206 | switch (opcode) { 207 | case 0x50: case 0xd7: case 0xf7: 208 | if (pref & (PRE_NONE | PRE_66)) 209 | goto error_operand; 210 | break; 211 | case 0xd6: 212 | if (pref & (PRE_F2 | PRE_F3)) 213 | goto error_operand; 214 | break; 215 | case 0xc5: 216 | goto error_operand; 217 | } 218 | goto no_error_operand; 219 | } else 220 | goto no_error_operand; 221 | 222 | error_operand: 223 | hs->flags |= F_ERROR | F_ERROR_OPERAND; 224 | no_error_operand: 225 | 226 | c = *p++; 227 | if (m_reg <= 1) { 228 | if (opcode == 0xf6) 229 | cflags |= C_IMM8; 230 | else if (opcode == 0xf7) 231 | cflags |= C_IMM_P66; 232 | } 233 | 234 | switch (m_mod) { 235 | case 0: 236 | if (pref & PRE_67) { 237 | if (m_rm == 6) 238 | disp_size = 2; 239 | } else 240 | if (m_rm == 5) 241 | disp_size = 4; 242 | break; 243 | case 1: 244 | disp_size = 1; 245 | break; 246 | case 2: 247 | disp_size = 2; 248 | if (!(pref & PRE_67)) 249 | disp_size <<= 1; 250 | } 251 | 252 | if (m_mod != 3 && m_rm == 4) { 253 | hs->flags |= F_SIB; 254 | p++; 255 | hs->sib = c; 256 | hs->sib_scale = c >> 6; 257 | hs->sib_index = (c & 0x3f) >> 3; 258 | if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) 259 | disp_size = 4; 260 | } 261 | 262 | p--; 263 | switch (disp_size) { 264 | case 1: 265 | hs->flags |= F_DISP8; 266 | hs->disp.disp8 = *p; 267 | break; 268 | case 2: 269 | hs->flags |= F_DISP16; 270 | hs->disp.disp16 = *(uint16_t *)p; 271 | break; 272 | case 4: 273 | hs->flags |= F_DISP32; 274 | hs->disp.disp32 = *(uint32_t *)p; 275 | } 276 | p += disp_size; 277 | } else if (pref & PRE_LOCK) 278 | hs->flags |= F_ERROR | F_ERROR_LOCK; 279 | 280 | if (cflags & C_IMM_P66) { 281 | if (cflags & C_REL32) { 282 | if (pref & PRE_66) { 283 | hs->flags |= F_IMM16 | F_RELATIVE; 284 | hs->imm.imm16 = *(uint16_t *)p; 285 | p += 2; 286 | goto disasm_done; 287 | } 288 | goto rel32_ok; 289 | } 290 | if (op64) { 291 | hs->flags |= F_IMM64; 292 | hs->imm.imm64 = *(uint64_t *)p; 293 | p += 8; 294 | } else if (!(pref & PRE_66)) { 295 | hs->flags |= F_IMM32; 296 | hs->imm.imm32 = *(uint32_t *)p; 297 | p += 4; 298 | } else 299 | goto imm16_ok; 300 | } 301 | 302 | 303 | if (cflags & C_IMM16) { 304 | imm16_ok: 305 | hs->flags |= F_IMM16; 306 | hs->imm.imm16 = *(uint16_t *)p; 307 | p += 2; 308 | } 309 | if (cflags & C_IMM8) { 310 | hs->flags |= F_IMM8; 311 | hs->imm.imm8 = *p++; 312 | } 313 | 314 | if (cflags & C_REL32) { 315 | rel32_ok: 316 | hs->flags |= F_IMM32 | F_RELATIVE; 317 | hs->imm.imm32 = *(uint32_t *)p; 318 | p += 4; 319 | } else if (cflags & C_REL8) { 320 | hs->flags |= F_IMM8 | F_RELATIVE; 321 | hs->imm.imm8 = *p++; 322 | } 323 | 324 | disasm_done: 325 | 326 | if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { 327 | hs->flags |= F_ERROR | F_ERROR_LENGTH; 328 | hs->len = 15; 329 | } 330 | 331 | return (unsigned int)hs->len; 332 | } 333 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/hde64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Hacker Disassembler Engine 64 3 | * Copyright (c) 2008-2009, Vyacheslav Patkov. 4 | * All rights reserved. 5 | * 6 | * hde64.h: C/C++ header file 7 | * 8 | */ 9 | 10 | #ifndef _HDE64_H_ 11 | #define _HDE64_H_ 12 | 13 | /* stdint.h - C99 standard header 14 | * http://en.wikipedia.org/wiki/stdint.h 15 | * 16 | * if your compiler doesn't contain "stdint.h" header (for 17 | * example, Microsoft Visual C++), you can download file: 18 | * http://www.azillionmonkeys.com/qed/pstdint.h 19 | * and change next line to: 20 | * #include "pstdint.h" 21 | */ 22 | #include 23 | 24 | #define F_MODRM 0x00000001 25 | #define F_SIB 0x00000002 26 | #define F_IMM8 0x00000004 27 | #define F_IMM16 0x00000008 28 | #define F_IMM32 0x00000010 29 | #define F_IMM64 0x00000020 30 | #define F_DISP8 0x00000040 31 | #define F_DISP16 0x00000080 32 | #define F_DISP32 0x00000100 33 | #define F_RELATIVE 0x00000200 34 | #define F_ERROR 0x00001000 35 | #define F_ERROR_OPCODE 0x00002000 36 | #define F_ERROR_LENGTH 0x00004000 37 | #define F_ERROR_LOCK 0x00008000 38 | #define F_ERROR_OPERAND 0x00010000 39 | #define F_PREFIX_REPNZ 0x01000000 40 | #define F_PREFIX_REPX 0x02000000 41 | #define F_PREFIX_REP 0x03000000 42 | #define F_PREFIX_66 0x04000000 43 | #define F_PREFIX_67 0x08000000 44 | #define F_PREFIX_LOCK 0x10000000 45 | #define F_PREFIX_SEG 0x20000000 46 | #define F_PREFIX_REX 0x40000000 47 | #define F_PREFIX_ANY 0x7f000000 48 | 49 | #define PREFIX_SEGMENT_CS 0x2e 50 | #define PREFIX_SEGMENT_SS 0x36 51 | #define PREFIX_SEGMENT_DS 0x3e 52 | #define PREFIX_SEGMENT_ES 0x26 53 | #define PREFIX_SEGMENT_FS 0x64 54 | #define PREFIX_SEGMENT_GS 0x65 55 | #define PREFIX_LOCK 0xf0 56 | #define PREFIX_REPNZ 0xf2 57 | #define PREFIX_REPX 0xf3 58 | #define PREFIX_OPERAND_SIZE 0x66 59 | #define PREFIX_ADDRESS_SIZE 0x67 60 | 61 | #pragma pack(push,1) 62 | 63 | typedef struct { 64 | uint8_t len; 65 | uint8_t p_rep; 66 | uint8_t p_lock; 67 | uint8_t p_seg; 68 | uint8_t p_66; 69 | uint8_t p_67; 70 | uint8_t rex; 71 | uint8_t rex_w; 72 | uint8_t rex_r; 73 | uint8_t rex_x; 74 | uint8_t rex_b; 75 | uint8_t opcode; 76 | uint8_t opcode2; 77 | uint8_t modrm; 78 | uint8_t modrm_mod; 79 | uint8_t modrm_reg; 80 | uint8_t modrm_rm; 81 | uint8_t sib; 82 | uint8_t sib_scale; 83 | uint8_t sib_index; 84 | uint8_t sib_base; 85 | union { 86 | uint8_t imm8; 87 | uint16_t imm16; 88 | uint32_t imm32; 89 | uint64_t imm64; 90 | } imm; 91 | union { 92 | uint8_t disp8; 93 | uint16_t disp16; 94 | uint32_t disp32; 95 | } disp; 96 | uint32_t flags; 97 | } hde64s; 98 | 99 | #pragma pack(pop) 100 | 101 | #ifdef __cplusplus 102 | extern "C" { 103 | #endif 104 | 105 | /* __cdecl */ 106 | unsigned int hde64_disasm(const void *code, hde64s *hs); 107 | 108 | #ifdef __cplusplus 109 | } 110 | #endif 111 | 112 | #endif /* _HDE64_H_ */ 113 | -------------------------------------------------------------------------------- /app/src/main/jni/Substrate/table64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Hacker Disassembler Engine 64 C 3 | * Copyright (c) 2008-2009, Vyacheslav Patkov. 4 | * All rights reserved. 5 | * 6 | */ 7 | 8 | #define C_NONE 0x00 9 | #define C_MODRM 0x01 10 | #define C_IMM8 0x02 11 | #define C_IMM16 0x04 12 | #define C_IMM_P66 0x10 13 | #define C_REL8 0x20 14 | #define C_REL32 0x40 15 | #define C_GROUP 0x80 16 | #define C_ERROR 0xff 17 | 18 | #define PRE_ANY 0x00 19 | #define PRE_NONE 0x01 20 | #define PRE_F2 0x02 21 | #define PRE_F3 0x04 22 | #define PRE_66 0x08 23 | #define PRE_67 0x10 24 | #define PRE_LOCK 0x20 25 | #define PRE_SEG 0x40 26 | #define PRE_ALL 0xff 27 | 28 | #define DELTA_OPCODES 0x4a 29 | #define DELTA_FPU_REG 0xfd 30 | #define DELTA_FPU_MODRM 0x104 31 | #define DELTA_PREFIXES 0x13c 32 | #define DELTA_OP_LOCK_OK 0x1ae 33 | #define DELTA_OP2_LOCK_OK 0x1c6 34 | #define DELTA_OP_ONLY_MEM 0x1d8 35 | #define DELTA_OP2_ONLY_MEM 0x1e7 36 | 37 | unsigned char hde64_table[] = { 38 | 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, 39 | 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, 40 | 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, 41 | 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, 42 | 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, 43 | 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, 44 | 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, 45 | 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, 46 | 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, 47 | 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, 48 | 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, 49 | 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, 50 | 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, 51 | 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, 52 | 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, 53 | 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, 54 | 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, 55 | 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, 56 | 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, 57 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, 58 | 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, 59 | 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, 60 | 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, 61 | 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, 62 | 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, 63 | 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, 64 | 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, 65 | 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, 66 | 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, 67 | 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, 68 | 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, 69 | 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, 70 | 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, 71 | 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, 72 | 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, 73 | 0x00,0xf0,0x02,0x00 74 | }; 75 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmusk026/Android-Mod-Menu-Kotlin/8b8da656baa383d343846ef707b51e62da697d83/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmusk026/Android-Mod-Menu-Kotlin/8b8da656baa383d343846ef707b51e62da697d83/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmusk026/Android-Mod-Menu-Kotlin/8b8da656baa383d343846ef707b51e62da697d83/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmusk026/Android-Mod-Menu-Kotlin/8b8da656baa383d343846ef707b51e62da697d83/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmusk026/Android-Mod-Menu-Kotlin/8b8da656baa383d343846ef707b51e62da697d83/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmusk026/Android-Mod-Menu-Kotlin/8b8da656baa383d343846ef707b51e62da697d83/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmusk026/Android-Mod-Menu-Kotlin/8b8da656baa383d343846ef707b51e62da697d83/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmusk026/Android-Mod-Menu-Kotlin/8b8da656baa383d343846ef707b51e62da697d83/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmusk026/Android-Mod-Menu-Kotlin/8b8da656baa383d343846ef707b51e62da697d83/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmusk026/Android-Mod-Menu-Kotlin/8b8da656baa383d343846ef707b51e62da697d83/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #202020 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | LGL Mod Menu 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.6.0-RC2' 5 | repositories { 6 | google() 7 | jcenter() 8 | 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:4.2.1' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | jcenter() 23 | 24 | } 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Oct 24 18:32:28 CEST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------