├── .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 | 
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 | 
56 |
57 | Go to **Build & Run**, and click on **Manage native code support**.
58 |
59 | 
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 | 
66 |
67 | Paste it in the prompt box.
68 |
69 | 
70 |
71 | Click install and wait
72 |
73 | 
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 | 
84 |
85 | Now that the file explorer will look like this, means the project has been opened
86 |
87 | 
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 | 
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 | 
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 | 
120 |
121 | Enter your keystore password
122 |
123 | 
124 |
125 | After that, it will tell you the APK has been experted
126 |
127 | 
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 | 
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 | 
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 | 
160 |
161 | You will now see the content structure inside the APK
162 |
163 | 
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 | 
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 | 
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 | 
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 | 
195 |
196 | This dialog will show if it have multidex. **SELECT ALL** and click OK
197 |
198 | 
199 |
200 | The editor opens.
201 |
202 | Make some changes. After you're done, save it
203 |
204 | 
205 |
206 | Go back and **save and exit**
207 |
208 | 
209 |
210 | Click OK to update the changes to the game's APK file with auto sign on
211 |
212 | 
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 | 
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 | 
225 |
226 | Open `AndroidManifest.xml`
227 |
228 | 
229 |
230 | Make some changes. After you're done, save it
231 |
232 | 
233 |
234 | Go back, save the APK file
235 |
236 | 
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 | 
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 | 
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 | 
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 | 
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 | 
312 |
313 | Be sure to enable Word wrap so it is easier to read
314 |
315 | 
316 |
317 | 2. APK Easy Tool since it can read out location of main activity without decompiling APK
318 |
319 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | ""
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("
"
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 |
--------------------------------------------------------------------------------