├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── reveny │ │ └── imgui │ │ └── mod │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── reveny │ │ │ └── imgui │ │ │ └── mod │ │ │ ├── MainActivity.java │ │ │ └── test │ │ │ ├── myGlSurfaceView.java │ │ │ └── myRenderer.java │ ├── jni │ │ ├── Build │ │ │ ├── Android.mk │ │ │ └── Application.mk │ │ ├── Include │ │ │ ├── Dobby │ │ │ │ ├── arm64-v8a │ │ │ │ │ └── libdobby.a │ │ │ │ ├── armeabi-v7a │ │ │ │ │ └── libdobby.a │ │ │ │ ├── dobby.h │ │ │ │ ├── x86 │ │ │ │ │ └── libdobby.a │ │ │ │ └── x86_64 │ │ │ │ │ └── libdobby.a │ │ │ ├── Drawing.h │ │ │ ├── ImGui.h │ │ │ ├── ImGui │ │ │ │ ├── backends │ │ │ │ │ ├── android_native_app_glue.h │ │ │ │ │ ├── imgui_impl_android.cpp │ │ │ │ │ ├── imgui_impl_android.h │ │ │ │ │ ├── imgui_impl_opengl3.cpp │ │ │ │ │ ├── imgui_impl_opengl3.h │ │ │ │ │ └── imgui_impl_opengl3_loader.h │ │ │ │ ├── imconfig.h │ │ │ │ ├── imgui.cpp │ │ │ │ ├── imgui.h │ │ │ │ ├── imgui_demo.cpp │ │ │ │ ├── imgui_draw.cpp │ │ │ │ ├── imgui_internal.h │ │ │ │ ├── imgui_tables.cpp │ │ │ │ ├── imgui_widgets.cpp │ │ │ │ ├── imstb_rectpack.h │ │ │ │ ├── imstb_textedit.h │ │ │ │ └── imstb_truetype.h │ │ │ ├── KittyMemory │ │ │ │ ├── KittyMemory.cpp │ │ │ │ ├── KittyMemory.h │ │ │ │ ├── KittyUtils.cpp │ │ │ │ ├── KittyUtils.h │ │ │ │ ├── MemoryBackup.cpp │ │ │ │ ├── MemoryBackup.h │ │ │ │ ├── MemoryPatch.cpp │ │ │ │ └── MemoryPatch.h │ │ │ ├── Logger.h │ │ │ ├── Math │ │ │ │ ├── Quaternion.hpp │ │ │ │ ├── Vector2.hpp │ │ │ │ └── Vector3.hpp │ │ │ ├── Obfuscate.h │ │ │ ├── RemapTools.h │ │ │ ├── Roboto-Regular.h │ │ │ ├── Unity.h │ │ │ ├── Utils.h │ │ │ ├── json │ │ │ │ └── json.hpp │ │ │ └── xdl │ │ │ │ ├── include │ │ │ │ └── xdl.h │ │ │ │ ├── xdl.c │ │ │ │ ├── xdl_iterate.c │ │ │ │ ├── xdl_iterate.h │ │ │ │ ├── xdl_linker.c │ │ │ │ ├── xdl_linker.h │ │ │ │ ├── xdl_lzma.c │ │ │ │ ├── xdl_lzma.h │ │ │ │ ├── xdl_util.c │ │ │ │ └── xdl_util.h │ │ ├── Loader │ │ │ └── Loader.cpp │ │ └── Main │ │ │ └── Main.cpp │ └── res │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ └── ic_launcher_foreground.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── reveny │ └── imgui │ └── mod │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img ├── 264991616-75554869-54d2-4c18-b89b-70958246d300.png ├── 264992884-d8e57355-6f62-427f-97b0-db6aa04f30fb.png └── ezgif.com-video-cutter (1).gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | 5 | # Local configuration file (sdk path, etc) 6 | local.properties 7 | 8 | # Log/OS Files 9 | *.log 10 | 11 | # Android Studio generated files and folders 12 | captures/ 13 | .externalNativeBuild/ 14 | .cxx/ 15 | *.apk 16 | output.json 17 | 18 | # IntelliJ 19 | *.iml 20 | .idea/ 21 | misc.xml 22 | deploymentTargetDropDown.xml 23 | render.experimental.xml 24 | 25 | # Keystore files 26 | *.jks 27 | *.keystore 28 | 29 | # Google Services (e.g. APIs or Firebase) 30 | google-services.json 31 | 32 | # Android Profiling 33 | *.hprof 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android-ImGui-Mod-Menu 2 | Android Internal ImGui Mod Menu Template with remap hide 3 | 4 | # Features 5 | - [x] Any Android Version 6 | - [x] Any Game Supported 7 | - [x] Arm64 Support 8 | - [x] x86 Support 9 | - [x] Test UI Without having to implement into game 10 | 11 | # Implementation 12 | 1. Build the APK in Android Studio 13 | 2. Open the APK in the zip program of your choice 14 | 3. Copy all the files from /lib/arm64-v8a/ to /lib/arm64-v8a of the game 15 | ![image](https://github.com/reveny/Android-ImGui-Mod-Menu/blob/main/img/264991616-75554869-54d2-4c18-b89b-70958246d300.png) 16 | 4. Find the games launcher activity and place the following code in the onCreate method 17 | ``` 18 | const-string v0, "Loader" 19 | invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V 20 | ``` 21 | ![image](https://github.com/reveny/Android-ImGui-Mod-Menu/blob/main/img/264992884-d8e57355-6f62-427f-97b0-db6aa04f30fb.png) 22 | 5. Recompile the apk 23 | 24 | 25 | # Credits 26 | ImGui by ocornut: https://github.com/ocornut/imgui
27 | DobbyHook by jmpwes: https://github.com/jmpews/Dobby
28 | KittyMemory by MJx0: https://github.com/MJx0/KittyMemory
29 | OpenGL Test by JimSeker: https://github.com/JimSeker/opengl/tree/master
30 | ImGui Touch by fedes1to: https://github.com/fedes1to/Zygisk-ImGui-Menu
31 | 32 | # Contact 33 | Telegram Group: https://t.me/reveny1
34 | Telegram Contact: https://t.me/revenyy 35 | 36 | # Preview 37 | ![image](https://github.com/reveny/Android-ImGui-Mod-Menu/blob/main/img/ezgif.com-video-cutter%20(1).gif) 38 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | namespace 'com.reveny.imgui.mod' 7 | compileSdk 33 8 | 9 | defaultConfig { 10 | applicationId "com.reveny.imgui.mod" 11 | minSdk 24 12 | targetSdk 33 13 | versionCode 1 14 | versionName "1.0" 15 | ndk { 16 | abiFilters "arm64-v8a", "armeabi-v7a" 17 | } 18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | compileOptions { 28 | sourceCompatibility JavaVersion.VERSION_1_8 29 | targetCompatibility JavaVersion.VERSION_1_8 30 | } 31 | 32 | externalNativeBuild { 33 | ndkBuild { 34 | path file('src/main/jni/Build/Android.mk') 35 | } 36 | } 37 | ndkVersion = '24.0.7856742' 38 | } 39 | 40 | dependencies { 41 | implementation 'androidx.appcompat:appcompat:1.6.1' 42 | implementation 'com.google.android.material:material:1.9.0' 43 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 44 | testImplementation 'junit:junit:4.13.2' 45 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 47 | } -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /app/src/androidTest/java/com/reveny/imgui/mod/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.reveny.imgui.mod; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.reveny.imgui.mod", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/reveny/imgui/mod/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.reveny.imgui.mod; 2 | 3 | import android.app.Activity; 4 | import android.app.ActivityManager; 5 | import android.content.Context; 6 | import android.content.pm.ConfigurationInfo; 7 | import android.os.Bundle; 8 | import android.util.Log; 9 | import android.view.View; 10 | 11 | import com.reveny.imgui.mod.test.myGlSurfaceView; 12 | 13 | public class MainActivity extends Activity { 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.activity_main); 19 | 20 | //Taken from https://github.com/JimSeker/opengl/blob/master/OpenGL30Cube/app/src/main/java/edu/cs4730/opengl30cube/MainActivity.java 21 | if (detectOpenGLES30()) { 22 | //so we know it a opengl 3.0 and use our extended GLsurfaceview. 23 | setContentView(new myGlSurfaceView(this)); 24 | } else { 25 | // This is where you could create an OpenGL ES 2.0 and/or 1.x compatible 26 | // renderer if you wanted to support both ES 1 and ES 2. 27 | Log.e("OpenGL", "OpenGL ES 3.0 not supported on device."); 28 | finish(); 29 | } 30 | 31 | System.loadLibrary("Loader"); 32 | } 33 | 34 | private boolean detectOpenGLES30() { 35 | ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); 36 | ConfigurationInfo info = am.getDeviceConfigurationInfo(); 37 | return (info.reqGlEsVersion >= 0x30000); 38 | } 39 | 40 | @Override 41 | public void onWindowFocusChanged(boolean hasFocus) { 42 | super.onWindowFocusChanged(hasFocus); 43 | if (hasFocus) { 44 | getWindow().getDecorView().setSystemUiVisibility( 45 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE 46 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 47 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 48 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 49 | | View.SYSTEM_UI_FLAG_FULLSCREEN); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/reveny/imgui/mod/test/myGlSurfaceView.java: -------------------------------------------------------------------------------- 1 | package com.reveny.imgui.mod.test; 2 | 3 | import android.content.Context; 4 | import android.opengl.GLSurfaceView; 5 | import android.view.MotionEvent; 6 | 7 | /* 8 | * simple extention of the GLsurfaceview. basically setup to use opengl 3.0 9 | * and set some configs. This would be where the touch listener is setup to do something. 10 | * 11 | * It also declares and sets the render. 12 | */ 13 | 14 | public class myGlSurfaceView extends GLSurfaceView { 15 | 16 | myRenderer myRender; 17 | 18 | public myGlSurfaceView(Context context) { 19 | super(context); 20 | // Create an OpenGL ES 3.0 context. 21 | setEGLContextClientVersion(3); 22 | 23 | super.setEGLConfigChooser(8, 8, 8, 8, 16, 0); 24 | 25 | // Set the Renderer for drawing on the GLSurfaceView 26 | myRender = new myRenderer(context); 27 | setRenderer(myRender); 28 | 29 | // Render the view only when there is a change in the drawing data 30 | setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); 31 | } 32 | 33 | 34 | //private final float TOUCH_SCALE_FACTOR = 180.0f / 320; 35 | private static final float TOUCH_SCALE_FACTOR = 0.015f; 36 | private float mPreviousX; 37 | private float mPreviousY; 38 | 39 | @Override 40 | public boolean onTouchEvent(MotionEvent e) { 41 | // MotionEvent reports input details from the touch screen 42 | // and other input controls. In this case, you are only 43 | // interested in events where the touch position changed. 44 | 45 | float x = e.getX(); 46 | float y = e.getY(); 47 | 48 | switch (e.getAction()) { 49 | case MotionEvent.ACTION_MOVE: 50 | 51 | float dx = x - mPreviousX; 52 | //subtract, so the cube moves the same direction as your finger. 53 | //with plus it moves the opposite direction. 54 | myRender.setX(myRender.getX() - (dx * TOUCH_SCALE_FACTOR)); 55 | 56 | float dy = y - mPreviousY; 57 | myRender.setY(myRender.getY() - (dy * TOUCH_SCALE_FACTOR)); 58 | } 59 | 60 | mPreviousX = x; 61 | mPreviousY = y; 62 | return true; 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /app/src/main/java/com/reveny/imgui/mod/test/myRenderer.java: -------------------------------------------------------------------------------- 1 | package com.reveny.imgui.mod.test; 2 | 3 | import android.content.Context; 4 | import android.opengl.GLES30; 5 | import android.opengl.Matrix; 6 | import android.opengl.GLSurfaceView; 7 | import android.util.Log; 8 | 9 | import javax.microedition.khronos.egl.EGLConfig; 10 | import javax.microedition.khronos.opengles.GL10; 11 | 12 | /** 13 | * Created by Seker on 7/2/2015. 14 | * 15 | * 16 | * Some code is uses from the OpenGL ES 3.0 programming guide second edition book. used under the MIT license. 17 | * 18 | */ 19 | public class myRenderer implements GLSurfaceView.Renderer { 20 | 21 | private int mWidth; 22 | private int mHeight; 23 | private static String TAG = "myRenderer"; 24 | private float mAngle =0; 25 | private float mTransY=0; 26 | private float mTransX=0; 27 | private static final float Z_NEAR = 1f; 28 | private static final float Z_FAR = 40f; 29 | 30 | // mMVPMatrix is an abbreviation for "Model View Projection Matrix" 31 | private final float[] mMVPMatrix = new float[16]; 32 | private final float[] mProjectionMatrix = new float[16]; 33 | private final float[] mViewMatrix = new float[16]; 34 | private final float[] mRotationMatrix = new float[16]; 35 | 36 | // 37 | public myRenderer(Context context) { 38 | //cube can not be instianated here, because of "no egl context" no clue. 39 | //do it in onSurfaceCreate and it is fine. odd, but workable solution. 40 | } 41 | /// 42 | // Create a shader object, load the shader source, and 43 | // compile the shader. 44 | // 45 | public static int LoadShader(int type, String shaderSrc) { 46 | int shader; 47 | int[] compiled = new int[1]; 48 | 49 | // Create the shader object 50 | shader = GLES30.glCreateShader(type); 51 | 52 | if (shader == 0) { 53 | return 0; 54 | } 55 | 56 | // Load the shader source 57 | GLES30.glShaderSource(shader, shaderSrc); 58 | 59 | // Compile the shader 60 | GLES30.glCompileShader(shader); 61 | 62 | // Check the compile status 63 | GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0); 64 | 65 | if (compiled[0] == 0) { 66 | Log.e(TAG, "Erorr!!!!"); 67 | Log.e(TAG, GLES30.glGetShaderInfoLog(shader)); 68 | GLES30.glDeleteShader(shader); 69 | return 0; 70 | } 71 | 72 | return shader; 73 | } 74 | 75 | /** 76 | * Utility method for debugging OpenGL calls. Provide the name of the call 77 | * just after making it: 78 | * 79 | *
 80 |      * mColorHandle = GLES30.glGetUniformLocation(mProgram, "vColor");
 81 |      * MyGLRenderer.checkGlError("glGetUniformLocation");
82 | * 83 | * If the operation is not successful, the check throws an error. 84 | * 85 | * @param glOperation - Name of the OpenGL call to check. 86 | */ 87 | public static void checkGlError(String glOperation) { 88 | int error; 89 | while ((error = GLES30.glGetError()) != GLES30.GL_NO_ERROR) { 90 | Log.e(TAG, glOperation + ": glError " + error); 91 | throw new RuntimeException(glOperation + ": glError " + error); 92 | } 93 | } 94 | 95 | /// 96 | // Initialize the shader and program object 97 | // 98 | public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { 99 | 100 | 101 | //set the clear buffer color to light gray. 102 | //GLES30.glClearColor(0.9f, .9f, 0.9f, 0.9f); 103 | //set the clear buffer color to a dark grey. 104 | GLES30.glClearColor(0.1f, .1f, 0.1f, 0.9f); 105 | //initialize the cube code for drawing. 106 | //if we had other objects setup them up here as well. 107 | } 108 | 109 | // / 110 | // Draw a triangle using the shader pair created in onSurfaceCreated() 111 | // 112 | public void onDrawFrame(GL10 glUnused) { 113 | 114 | // Clear the color buffer set above by glClearColor. 115 | GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT); 116 | 117 | //need this otherwise, it will over right stuff and the cube will look wrong! 118 | GLES30.glEnable(GLES30.GL_DEPTH_TEST); 119 | 120 | // Set the camera position (View matrix) note Matrix is an include, not a declared method. 121 | Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 122 | 123 | // Create a rotation and translation for the cube 124 | Matrix.setIdentityM(mRotationMatrix, 0); 125 | 126 | //move the cube up/down and left/right 127 | Matrix.translateM(mRotationMatrix, 0, mTransX, mTransY, 0); 128 | 129 | //mangle is how fast, x,y,z which directions it rotates. 130 | Matrix.rotateM(mRotationMatrix, 0, mAngle, 1.0f, 1.0f, 1.0f); 131 | 132 | // combine the model with the view matrix 133 | Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mRotationMatrix, 0); 134 | 135 | // combine the model-view with the projection matrix 136 | Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0); 137 | 138 | //change the angle, so the cube will spin. 139 | mAngle+=.4; 140 | 141 | } 142 | 143 | // / 144 | // Handle surface changes 145 | // 146 | public void onSurfaceChanged(GL10 glUnused, int width, int height) { 147 | mWidth = width; 148 | mHeight = height; 149 | // Set the viewport 150 | GLES30.glViewport(0, 0, mWidth, mHeight); 151 | float aspect = (float) width / height; 152 | 153 | // this projection matrix is applied to object coordinates 154 | //no idea why 53.13f, it was used in another example and it worked. 155 | Matrix.perspectiveM(mProjectionMatrix, 0, 53.13f, aspect, Z_NEAR, Z_FAR); 156 | } 157 | 158 | //used the touch listener to move the cube up/down (y) and left/right (x) 159 | public float getY() { 160 | return mTransY; 161 | } 162 | 163 | public void setY(float mY) { 164 | mTransY = mY; 165 | } 166 | 167 | public float getX() { 168 | return mTransX; 169 | } 170 | 171 | public void setX(float mX) { 172 | mTransX = mX; 173 | } 174 | 175 | } -------------------------------------------------------------------------------- /app/src/main/jni/Build/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := libdobby 5 | LOCAL_SRC_FILES := $(LOCAL_PATH)/../Include/Dobby/$(TARGET_ARCH_ABI)/libdobby.a 6 | include $(PREBUILT_STATIC_LIBRARY) 7 | 8 | MAIN_LOCAL_PATH := $(call my-dir) 9 | LOCAL_C_INCLUDES += $(MAIN_LOCAL_PATH)/../Include 10 | 11 | include $(CLEAR_VARS) 12 | 13 | LOCAL_MODULE := ModMenu 14 | 15 | # Code optimization 16 | LOCAL_CFLAGS := -Wno-error=format-security -fvisibility=hidden -ffunction-sections -fdata-sections -w 17 | LOCAL_CFLAGS += -fno-rtti -fexceptions -fpermissive 18 | LOCAL_CPPFLAGS := -Wno-error=format-security -fvisibility=hidden -ffunction-sections -fdata-sections -w -Werror -s -std=c++17 19 | LOCAL_CPPFLAGS += -Wno-error=c++11-narrowing -fms-extensions -fno-rtti -fexceptions -fpermissive 20 | LOCAL_LDFLAGS += -Wl,--gc-sections, -llog 21 | LOCAL_ARM_MODE := arm 22 | 23 | LOCAL_STATIC_LIBRARIES := libdobby 24 | LOCAL_LDLIBS := -lz 25 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/xdl/include 26 | 27 | LOCAL_SRC_FILES := ../Main/Main.cpp \ 28 | ../Include/ImGui/backends/imgui_impl_opengl3.cpp \ 29 | ../Include/ImGui/backends/imgui_impl_android.cpp \ 30 | ../Include/ImGui/imgui.cpp \ 31 | ../Include/ImGui/imgui_draw.cpp \ 32 | ../Include/ImGui/imgui_demo.cpp \ 33 | ../Include/ImGui/imgui_tables.cpp \ 34 | ../Include/ImGui/imgui_widgets.cpp \ 35 | ../Include/xdl/xdl.c \ 36 | ../Include/xdl/xdl_iterate.c \ 37 | ../Include/xdl/xdl_linker.c \ 38 | ../Include/xdl/xdl_lzma.c \ 39 | ../Include/xdl/xdl_util.c \ 40 | ../Include/KittyMemory/KittyMemory.cpp \ 41 | ../Include/KittyMemory/MemoryPatch.cpp \ 42 | ../Include/KittyMemory/MemoryBackup.cpp \ 43 | ../Include/KittyMemory/KittyUtils.cpp \ 44 | 45 | LOCAL_LDLIBS := -llog -landroid -lz -lEGL -lGLESv3 -lGLESv2 46 | 47 | include $(BUILD_SHARED_LIBRARY) 48 | 49 | # Loader 50 | include $(CLEAR_VARS) 51 | 52 | LOCAL_MODULE := Loader 53 | LOCAL_CFLAGS := -Wno-error=format-security -fvisibility=hidden -ffunction-sections -fdata-sections -w 54 | LOCAL_CFLAGS += -fno-rtti -fexceptions -fpermissive 55 | LOCAL_CPPFLAGS := -Wno-error=format-security -fvisibility=hidden -ffunction-sections -fdata-sections -w -Werror -s -std=c++17 56 | LOCAL_CPPFLAGS += -Wno-error=c++11-narrowing -fms-extensions -fno-rtti -fexceptions -fpermissive 57 | LOCAL_LDFLAGS += -Wl,--gc-sections,--strip-all, -llog 58 | LOCAL_ARM_MODE := arm 59 | 60 | LOCAL_SRC_FILES := ../Loader/Loader.cpp \ 61 | LOCAL_LDLIBS := -llog -landroid 62 | 63 | include $(BUILD_SHARED_LIBRARY) -------------------------------------------------------------------------------- /app/src/main/jni/Build/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := arm64-v8a armeabi-v7a 2 | APP_STL := c++_static 3 | APP_OPTIM := release 4 | APP_THIN_ARCHIVE := true 5 | APP_PIE := true -------------------------------------------------------------------------------- /app/src/main/jni/Include/Dobby/arm64-v8a/libdobby.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/jni/Include/Dobby/arm64-v8a/libdobby.a -------------------------------------------------------------------------------- /app/src/main/jni/Include/Dobby/armeabi-v7a/libdobby.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/jni/Include/Dobby/armeabi-v7a/libdobby.a -------------------------------------------------------------------------------- /app/src/main/jni/Include/Dobby/dobby.h: -------------------------------------------------------------------------------- 1 | #ifndef dobby_h 2 | #define dobby_h 3 | 4 | // obfuscated interface 5 | #if 0 6 | #define DobbyBuildVersion c343f74888dffad84d9ad08d9c433456 7 | #define DobbyHook c8dc3ffa44f22dbd10ccae213dd8b1f8 8 | #define DobbyInstrument b71e27bca2c362de90c1034f19d839f9 9 | #endif 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | #include 17 | 18 | void log_set_level(int level); 19 | void log_switch_to_syslog(); 20 | void log_switch_to_file(const char *path); 21 | 22 | typedef enum { 23 | kMemoryOperationSuccess, 24 | kMemoryOperationError, 25 | kNotSupportAllocateExecutableMemory, 26 | kNotEnough, 27 | kNone 28 | } MemoryOperationError; 29 | 30 | #define PLATFORM_INTERFACE_CODE_PATCH_TOOL_H 31 | MemoryOperationError CodePatch(void *address, uint8_t *buffer, uint32_t buffer_size); 32 | 33 | typedef uintptr_t addr_t; 34 | typedef uint32_t addr32_t; 35 | typedef uint64_t addr64_t; 36 | 37 | #if defined(__arm64__) || defined(__aarch64__) 38 | 39 | #define ARM64_TMP_REG_NDX_0 17 40 | 41 | // float register 42 | typedef union _FPReg { 43 | __int128_t q; 44 | struct { 45 | double d1; 46 | double d2; 47 | } d; 48 | struct { 49 | float f1; 50 | float f2; 51 | float f3; 52 | float f4; 53 | } f; 54 | } FPReg; 55 | 56 | // register context 57 | typedef struct _RegisterContext { 58 | uint64_t dmmpy_0; // dummy placeholder 59 | uint64_t sp; 60 | 61 | uint64_t dmmpy_1; // dummy placeholder 62 | union { 63 | uint64_t x[29]; 64 | struct { 65 | uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, 66 | x23, x24, x25, x26, x27, x28; 67 | } regs; 68 | } general; 69 | 70 | uint64_t fp; 71 | uint64_t lr; 72 | 73 | union { 74 | FPReg q[32]; 75 | struct { 76 | FPReg q0, q1, q2, q3, q4, q5, q6, q7; 77 | // [!!! READ ME !!!] 78 | // for Arm64, can't access q8 - q31, unless you enable full floating-point register pack 79 | FPReg q8, q9, q10, q11, q12, q13, q14, q15, q16, q17, q18, q19, q20, q21, q22, q23, q24, q25, q26, q27, q28, q29, 80 | q30, q31; 81 | } regs; 82 | } floating; 83 | } RegisterContext; 84 | #elif defined(__arm__) 85 | typedef struct _RegisterContext { 86 | uint32_t dummy_0; 87 | uint32_t dummy_1; 88 | 89 | uint32_t dummy_2; 90 | uint32_t sp; 91 | 92 | union { 93 | uint32_t r[13]; 94 | struct { 95 | uint32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12; 96 | } regs; 97 | } general; 98 | 99 | uint32_t lr; 100 | } RegisterContext; 101 | #elif defined(_M_IX86) || defined(__i386__) 102 | typedef struct _RegisterContext { 103 | uint32_t dummy_0; 104 | uint32_t esp; 105 | 106 | uint32_t dummy_1; 107 | uint32_t flags; 108 | 109 | union { 110 | struct { 111 | uint32_t eax, ebx, ecx, edx, ebp, esp, edi, esi; 112 | } regs; 113 | } general; 114 | 115 | } RegisterContext; 116 | #elif defined(_M_X64) || defined(__x86_64__) 117 | typedef struct _RegisterContext { 118 | uint64_t dummy_0; 119 | uint64_t rsp; 120 | 121 | union { 122 | struct { 123 | uint64_t rax, rbx, rcx, rdx, rbp, rsp, rdi, rsi, r8, r9, r10, r11, r12, r13, r14, r15; 124 | } regs; 125 | } general; 126 | 127 | uint64_t dummy_1; 128 | uint64_t flags; 129 | } RegisterContext; 130 | #endif 131 | 132 | #define RT_FAILED -1 133 | #define RT_SUCCESS 0 134 | typedef enum _RetStatus { RS_FAILED = -1, RS_SUCCESS = 0 } RetStatus; 135 | 136 | typedef struct _HookEntryInfo { 137 | int hook_id; 138 | union { 139 | void *target_address; 140 | void *function_address; 141 | void *instruction_address; 142 | }; 143 | } HookEntryInfo; 144 | 145 | // DobbyWrap <==> DobbyInstrument, so use DobbyInstrument instead of DobbyWrap 146 | #if 0 147 | // wrap function with pre_call and post_call 148 | typedef void (*PreCallTy)(RegisterContext *ctx, const HookEntryInfo *info); 149 | typedef void (*PostCallTy)(RegisterContext *ctx, const HookEntryInfo *info); 150 | int DobbyWrap(void *function_address, PreCallTy pre_call, PostCallTy post_call); 151 | #endif 152 | 153 | // return dobby build date 154 | const char *DobbyBuildVersion(); 155 | 156 | // replace function 157 | int DobbyHook(void *address, void *replace_call, void **origin_call); 158 | 159 | // dynamic binary instrument for instruction 160 | // [!!! READ ME !!!] 161 | // for Arm64, can't access q8 - q31, unless you enable full floating-point register pack 162 | typedef void (*DBICallTy)(RegisterContext *ctx, const HookEntryInfo *info); 163 | int DobbyInstrument(void *address, DBICallTy dbi_call); 164 | 165 | // destory and restore hook 166 | int DobbyDestroy(void *address); 167 | 168 | // iterate symbol table and find symbol 169 | void *DobbySymbolResolver(const char *image_name, const char *symbol_name); 170 | 171 | // global offset table 172 | int DobbyGlobalOffsetTableReplace(char *image_name, char *symbol_name, void *fake_func, void **orig_func); 173 | 174 | // [!!! READ ME !!!] 175 | // for arm, Arm64, dobby will use b xxx instead of ldr absolute indirect branch 176 | // for x64, dobby always use absolute indirect jump 177 | #if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) 178 | void dobby_enable_near_branch_trampoline(); 179 | void dobby_disable_near_branch_trampoline(); 180 | #endif 181 | 182 | // register linker load image callback 183 | typedef void (*linker_load_callback_t)(const char *image_name, void *handle); 184 | void dobby_register_image_load_callback(linker_load_callback_t func); 185 | 186 | #ifdef __cplusplus 187 | } 188 | #endif 189 | 190 | #endif 191 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/Dobby/x86/libdobby.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/jni/Include/Dobby/x86/libdobby.a -------------------------------------------------------------------------------- /app/src/main/jni/Include/Dobby/x86_64/libdobby.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/jni/Include/Dobby/x86_64/libdobby.a -------------------------------------------------------------------------------- /app/src/main/jni/Include/Drawing.h: -------------------------------------------------------------------------------- 1 | // 2 | // Taken from https://github.com/Rev/Among-Us-Android-ESP/blob/main/Drawing.h 3 | // 4 | 5 | #ifndef ImGuiAndroid_ESP 6 | #define ImGuiAndroid_ESP 7 | 8 | #include "ImGui/imgui_internal.h" 9 | 10 | namespace ESP { 11 | void DrawLine(ImVec2 start, ImVec2 end, ImVec4 color) { 12 | auto background = ImGui::GetBackgroundDrawList(); 13 | 14 | if(background) { 15 | background->AddLine(start, end, ImColor(color.x, color.y, color.z, color.w)); 16 | } 17 | } 18 | 19 | void DrawBox(float x, float y, float z, float w, ImVec4 color) { 20 | ImVec2 v1(x, y); 21 | ImVec2 v2(x + z, y); 22 | ImVec2 v3(x + z, y + w); 23 | ImVec2 v4(x, y + w); 24 | 25 | DrawLine(v1, v2, color); 26 | DrawLine(v2, v3, color); 27 | DrawLine(v3, v4, color); 28 | DrawLine(v4, v1, color); 29 | } 30 | 31 | void DrawCircle(float X, float Y, float radius, bool filled, ImVec4 color) { 32 | auto background = ImGui::GetBackgroundDrawList(); 33 | 34 | if(background) { 35 | if(filled) { 36 | background->AddCircleFilled(ImVec2(X, Y), radius, ImColor(color.x, color.y, color.z, color.w)); 37 | } else { 38 | background->AddCircle(ImVec2(X, Y), radius, ImColor(color.x, color.y, color.z, color.w)); 39 | } 40 | } 41 | } 42 | 43 | void DrawText2(float fontSize, ImVec2 position, ImVec4 color, const char *text) { 44 | auto background = ImGui::GetBackgroundDrawList(); 45 | 46 | if(background) { 47 | background->AddText(NULL, fontSize, position, ImColor(color.x, color.y, color.z, color.w), text); 48 | } 49 | } 50 | } 51 | 52 | #endif //ImGuiAndroid_ESP -------------------------------------------------------------------------------- /app/src/main/jni/Include/ImGui.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Reveny on 2022/12/25. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ImGui/imgui.h" 11 | #include "Roboto-Regular.h" 12 | #include "ImGui/backends/imgui_impl_opengl3.h" 13 | #include "ImGui/backends/imgui_impl_android.h" 14 | #include "ImGui/backends/android_native_app_glue.h" 15 | 16 | #include "Utils.h" 17 | #include "Dobby/dobby.h" 18 | #include "Obfuscate.h" 19 | #include "Logger.h" 20 | 21 | void menuStyle(); 22 | void (*menuAddress)(); 23 | 24 | using swapbuffers_orig = EGLBoolean (*)(EGLDisplay dpy, EGLSurface surf); 25 | EGLBoolean swapbuffers_hook(EGLDisplay dpy, EGLSurface surf); 26 | swapbuffers_orig o_swapbuffers = nullptr; 27 | 28 | bool isInitialized = false; 29 | int glWidth = 0; 30 | int glHeight = 0; 31 | 32 | //Taken from https://github.com/fedes1to/Zygisk-ImGui-Menu/blob/main/module/src/main/cpp/hook.cpp 33 | #define HOOKINPUT(ret, func, ...) \ 34 | ret (*orig##func)(__VA_ARGS__); \ 35 | ret my##func(__VA_ARGS__) 36 | 37 | HOOKINPUT(void, Input, void *thiz, void *ex_ab, void *ex_ac) { 38 | origInput(thiz, ex_ab, ex_ac); 39 | ImGui_ImplAndroid_HandleInputEvent((AInputEvent *)thiz); 40 | return; 41 | } 42 | 43 | HOOKINPUT(int32_t, Consume, void *thiz, void *arg1, bool arg2, long arg3, uint32_t *arg4, AInputEvent **input_event) 44 | { 45 | auto result = origConsume(thiz, arg1, arg2, arg3, arg4, input_event); 46 | if(result != 0 || *input_event == nullptr) return result; 47 | ImGui_ImplAndroid_HandleInputEvent(*input_event); 48 | return result; 49 | } 50 | 51 | //This menu_addr is used to allow for multiple game support in the future 52 | void *initModMenu(void *menu_addr) { 53 | menuAddress = (void (*)())menu_addr; 54 | do { 55 | sleep(1); 56 | } while (!isLibraryLoaded(OBFUSCATE("libEGL.so"))); 57 | 58 | auto swapBuffers = ((uintptr_t) DobbySymbolResolver(OBFUSCATE("libEGL.so"), OBFUSCATE("eglSwapBuffers"))); 59 | KittyMemory::ProtectAddr((void *)swapBuffers, sizeof(swapBuffers), PROT_READ | PROT_WRITE | PROT_EXEC); 60 | DobbyHook((void *) swapBuffers, (void *) swapbuffers_hook, (void **) &o_swapbuffers); 61 | 62 | //Taken from https://github.com/fedes1to/Zygisk-ImGui-Menu/blob/main/module/src/main/cpp/hook.cpp 63 | void *sym_input = DobbySymbolResolver(OBFUSCATE("/system/lib/libinput.so"), OBFUSCATE("_ZN7android13InputConsumer21initializeMotionEventEPNS_11MotionEventEPKNS_12InputMessageE")); 64 | 65 | if (sym_input != nullptr) { 66 | DobbyHook((void *) sym_input, (void *) myInput, (void **) &origInput); 67 | } else { 68 | sym_input = DobbySymbolResolver(("/system/lib/libinput.so"), ("_ZN7android13InputConsumer7consumeEPNS_26InputEventFactoryInterfaceEblPjPPNS_10InputEventE")); 69 | if(NULL != sym_input) { 70 | DobbyHook(sym_input,(void *) myConsume,(void **) &origConsume); 71 | } 72 | } //c 73 | 74 | LOGI(OBFUSCATE("ImGUI Hooks initialized")); 75 | return nullptr; 76 | } 77 | 78 | void setupMenu() { 79 | if (isInitialized) return; 80 | 81 | auto ctx = ImGui::CreateContext(); 82 | if (!ctx) { 83 | LOGI(OBFUSCATE("Failed to create context")); 84 | return; 85 | } 86 | 87 | ImGuiIO& io = ImGui::GetIO(); 88 | io.DisplaySize = ImVec2((float)glWidth, (float)glHeight); 89 | io.ConfigWindowsMoveFromTitleBarOnly = true; 90 | io.IniFilename = nullptr; 91 | 92 | // Setup Platform/Renderer backends 93 | ImGui_ImplAndroid_Init(); 94 | ImGui_ImplOpenGL3_Init("#version 300 es"); 95 | 96 | int systemScale = (1.0 / glWidth) * glWidth; 97 | ImFontConfig font_cfg; 98 | font_cfg.SizePixels = systemScale * 22.0f; 99 | io.Fonts->AddFontFromMemoryTTF(Roboto_Regular, systemScale * 30.0, 40.0f); 100 | 101 | ImGui::GetStyle().ScaleAllSizes(2); 102 | 103 | isInitialized = true; 104 | LOGI("setup done."); 105 | } 106 | void internalDrawMenu(int width, int height) { 107 | if(!isInitialized) return; 108 | 109 | ImGuiIO &io = ImGui::GetIO(); 110 | 111 | ImGui_ImplOpenGL3_NewFrame(); 112 | ImGui_ImplAndroid_NewFrame(width, height); 113 | ImGui::NewFrame(); 114 | 115 | menuAddress(); 116 | 117 | ImGui::Render(); 118 | 119 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 120 | } 121 | 122 | EGLBoolean swapbuffers_hook(EGLDisplay dpy, EGLSurface surf) { 123 | EGLint w, h; 124 | eglQuerySurface(dpy, surf, EGL_WIDTH, &w); 125 | eglQuerySurface(dpy, surf, EGL_HEIGHT, &h); 126 | glWidth = w; 127 | glHeight = h; 128 | 129 | setupMenu(); 130 | internalDrawMenu(w, h); 131 | 132 | return o_swapbuffers(dpy, surf); 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/ImGui/backends/android_native_app_glue.h: -------------------------------------------------------------------------------- 1 | #ifndef _ANDROID_NATIVE_APP_GLUE_H 2 | #define _ANDROID_NATIVE_APP_GLUE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /** 17 | * The native activity interface provided by 18 | * is based on a set of application-provided callbacks that will be called 19 | * by the Activity's main thread when certain events occur. 20 | * 21 | * This means that each one of this callbacks _should_ _not_ block, or they 22 | * risk having the system force-close the application. This programming 23 | * model is direct, lightweight, but constraining. 24 | * 25 | * The 'threaded_native_app' static library is used to provide a different 26 | * execution model where the application can implement its own main event 27 | * loop in a different thread instead. Here's how it works: 28 | * 29 | * 1/ The application must provide a function named "android_main()" that 30 | * will be called when the activity is created, in a new thread that is 31 | * distinct from the activity's main thread. 32 | * 33 | * 2/ android_main() receives a pointer to a valid "android_app" structure 34 | * that contains references to other important objects, e.g. the 35 | * ANativeActivity obejct instance the application is running in. 36 | * 37 | * 3/ the "android_app" object holds an ALooper instance that already 38 | * listens to two important things: 39 | * 40 | * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX 41 | * declarations below. 42 | * 43 | * - input events coming from the AInputQueue attached to the activity. 44 | * 45 | * Each of these correspond to an ALooper identifier returned by 46 | * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, 47 | * respectively. 48 | * 49 | * Your application can use the same ALooper to listen to additional 50 | * file-descriptors. They can either be callback based, or with return 51 | * identifiers starting with LOOPER_ID_USER. 52 | * 53 | * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, 54 | * the returned data will point to an android_poll_source structure. You 55 | * can call the process() function on it, and fill in android_app->onAppCmd 56 | * and android_app->onInputEvent to be called for your own processing 57 | * of the event. 58 | * 59 | * Alternatively, you can call the low-level functions to read and process 60 | * the data directly... look at the process_cmd() and process_input() 61 | * implementations in the glue to see how to do this. 62 | * 63 | * See the sample named "native-activity" that comes with the NDK with a 64 | * full usage example. Also look at the JavaDoc of NativeActivity. 65 | */ 66 | 67 | struct android_app; 68 | 69 | /** 70 | * Data associated with an ALooper fd that will be returned as the "outData" 71 | * when that source has data ready. 72 | */ 73 | struct android_poll_source { 74 | // The identifier of this source. May be LOOPER_ID_MAIN or 75 | // LOOPER_ID_INPUT. 76 | int32_t id; 77 | 78 | // The android_app this ident is associated with. 79 | struct android_app* app; 80 | 81 | // Function to call to perform the standard processing of data from 82 | // this source. 83 | void (*process)(struct android_app* app, struct android_poll_source* source); 84 | }; 85 | 86 | /** 87 | * This is the interface for the standard glue code of a threaded 88 | * application. In this model, the application's code is running 89 | * in its own thread separate from the main thread of the process. 90 | * It is not required that this thread be associated with the Java 91 | * VM, although it will need to be in order to make JNI calls any 92 | * Java objects. 93 | */ 94 | struct android_app { 95 | // The application can place a pointer to its own state object 96 | // here if it likes. 97 | void* userData; 98 | 99 | // Fill this in with the function to process main app commands (APP_CMD_*) 100 | void (*onAppCmd)(struct android_app* app, int32_t cmd); 101 | 102 | // Fill this in with the function to process input events. At this point 103 | // the event has already been pre-dispatched, and it will be finished upon 104 | // return. Return 1 if you have handled the event, 0 for any default 105 | // dispatching. 106 | int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); 107 | 108 | // The ANativeActivity object instance that this app is running in. 109 | ANativeActivity* activity; 110 | 111 | // The current configuration the app is running in. 112 | AConfiguration* config; 113 | 114 | // This is the last instance's saved state, as provided at creation time. 115 | // It is NULL if there was no state. You can use this as you need; the 116 | // memory will remain around until you call android_app_exec_cmd() for 117 | // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. 118 | // These variables should only be changed when processing a APP_CMD_SAVE_STATE, 119 | // at which point they will be initialized to NULL and you can malloc your 120 | // state and place the information here. In that case the memory will be 121 | // freed for you later. 122 | void* savedState; 123 | size_t savedStateSize; 124 | 125 | // The ALooper associated with the app's thread. 126 | ALooper* looper; 127 | 128 | // When non-NULL, this is the input queue from which the app will 129 | // receive user input events. 130 | AInputQueue* inputQueue; 131 | 132 | // When non-NULL, this is the window surface that the app can draw in. 133 | ANativeWindow* window; 134 | 135 | // Current content rectangle of the window; this is the area where the 136 | // window's content should be placed to be seen by the user. 137 | ARect contentRect; 138 | 139 | // Current state of the app's activity. May be either APP_CMD_START, 140 | // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. 141 | int activityState; 142 | 143 | // This is non-zero when the application's NativeActivity is being 144 | // destroyed and waiting for the app thread to complete. 145 | int destroyRequested; 146 | 147 | // ------------------------------------------------- 148 | // Below are "private" implementation of the glue code. 149 | 150 | pthread_mutex_t mutex; 151 | pthread_cond_t cond; 152 | 153 | int msgread; 154 | int msgwrite; 155 | 156 | pthread_t thread; 157 | 158 | struct android_poll_source cmdPollSource; 159 | struct android_poll_source inputPollSource; 160 | 161 | int running; 162 | int stateSaved; 163 | int destroyed; 164 | int redrawNeeded; 165 | AInputQueue* pendingInputQueue; 166 | ANativeWindow* pendingWindow; 167 | ARect pendingContentRect; 168 | }; 169 | 170 | enum { 171 | /** 172 | * Looper data ID of commands coming from the app's main thread, which 173 | * is returned as an identifier from ALooper_pollOnce(). The data for this 174 | * identifier is a pointer to an android_poll_source structure. 175 | * These can be retrieved and processed with android_app_read_cmd() 176 | * and android_app_exec_cmd(). 177 | */ 178 | LOOPER_ID_MAIN = 1, 179 | 180 | /** 181 | * Looper data ID of events coming from the AInputQueue of the 182 | * application's window, which is returned as an identifier from 183 | * ALooper_pollOnce(). The data for this identifier is a pointer to an 184 | * android_poll_source structure. These can be read via the inputQueue 185 | * object of android_app. 186 | */ 187 | LOOPER_ID_INPUT = 2, 188 | 189 | /** 190 | * Start of user-defined ALooper identifiers. 191 | */ 192 | LOOPER_ID_USER = 3, 193 | }; 194 | 195 | enum { 196 | /** 197 | * Command from main thread: the AInputQueue has changed. Upon processing 198 | * this command, android_app->inputQueue will be updated to the new queue 199 | * (or NULL). 200 | */ 201 | APP_CMD_INPUT_CHANGED, 202 | 203 | /** 204 | * Command from main thread: a new ANativeWindow is ready for use. Upon 205 | * receiving this command, android_app->window will contain the new window 206 | * surface. 207 | */ 208 | APP_CMD_INIT_WINDOW, 209 | 210 | /** 211 | * Command from main thread: the existing ANativeWindow needs to be 212 | * terminated. Upon receiving this command, android_app->window still 213 | * contains the existing window; after calling android_app_exec_cmd 214 | * it will be set to NULL. 215 | */ 216 | APP_CMD_TERM_WINDOW, 217 | 218 | /** 219 | * Command from main thread: the current ANativeWindow has been resized. 220 | * Please redraw with its new size. 221 | */ 222 | APP_CMD_WINDOW_RESIZED, 223 | 224 | /** 225 | * Command from main thread: the system needs that the current ANativeWindow 226 | * be redrawn. You should redraw the window before handing this to 227 | * android_app_exec_cmd() in order to avoid transient drawing glitches. 228 | */ 229 | APP_CMD_WINDOW_REDRAW_NEEDED, 230 | 231 | /** 232 | * Command from main thread: the content area of the window has changed, 233 | * such as from the soft input window being shown or hidden. You can 234 | * find the new content rect in android_app::contentRect. 235 | */ 236 | APP_CMD_CONTENT_RECT_CHANGED, 237 | 238 | /** 239 | * Command from main thread: the app's activity window has gained 240 | * input focus. 241 | */ 242 | APP_CMD_GAINED_FOCUS, 243 | 244 | /** 245 | * Command from main thread: the app's activity window has lost 246 | * input focus. 247 | */ 248 | APP_CMD_LOST_FOCUS, 249 | 250 | /** 251 | * Command from main thread: the current device configuration has changed. 252 | */ 253 | APP_CMD_CONFIG_CHANGED, 254 | 255 | /** 256 | * Command from main thread: the system is running low on memory. 257 | * Try to reduce your memory use. 258 | */ 259 | APP_CMD_LOW_MEMORY, 260 | 261 | /** 262 | * Command from main thread: the app's activity has been started. 263 | */ 264 | APP_CMD_START, 265 | 266 | /** 267 | * Command from main thread: the app's activity has been resumed. 268 | */ 269 | APP_CMD_RESUME, 270 | 271 | /** 272 | * Command from main thread: the app should generate a new saved state 273 | * for itself, to restore from later if needed. If you have saved state, 274 | * allocate it with malloc and place it in android_app.savedState with 275 | * the size in android_app.savedStateSize. The will be freed for you 276 | * later. 277 | */ 278 | APP_CMD_SAVE_STATE, 279 | 280 | /** 281 | * Command from main thread: the app's activity has been paused. 282 | */ 283 | APP_CMD_PAUSE, 284 | 285 | /** 286 | * Command from main thread: the app's activity has been stopped. 287 | */ 288 | APP_CMD_STOP, 289 | 290 | /** 291 | * Command from main thread: the app's activity is being destroyed, 292 | * and waiting for the app thread to clean up and exit before proceeding. 293 | */ 294 | APP_CMD_DESTROY, 295 | }; 296 | 297 | /** 298 | * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next 299 | * app command message. 300 | */ 301 | int8_t android_app_read_cmd(struct android_app* android_app); 302 | 303 | /** 304 | * Call with the command returned by android_app_read_cmd() to do the 305 | * initial pre-processing of the given command. You can perform your own 306 | * actions for the command after calling this function. 307 | */ 308 | void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); 309 | 310 | /** 311 | * Call with the command returned by android_app_read_cmd() to do the 312 | * final post-processing of the given command. You must have done your own 313 | * actions for the command before calling this function. 314 | */ 315 | void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); 316 | 317 | /** 318 | * Dummy function you can call to ensure glue code isn't stripped. 319 | */ 320 | void app_dummy(); 321 | 322 | /** 323 | * This is the function that application code must implement, representing 324 | * the main entry to the app. 325 | */ 326 | extern void android_main(struct android_app* app); 327 | 328 | #ifdef __cplusplus 329 | } 330 | #endif 331 | 332 | #endif /* _ANDROID_NATIVE_APP_GLUE_H */ 333 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/ImGui/backends/imgui_impl_android.cpp: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Binding for Android native app 2 | // This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3) 3 | 4 | // Implemented features: 5 | // [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). 6 | // Missing features: 7 | // [ ] Platform: Clipboard support. 8 | // [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | // [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. 10 | // Important: 11 | // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) 12 | // - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446) 13 | 14 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 15 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 16 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 17 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 18 | 19 | // CHANGELOG 20 | // (minor and older changes stripped away, please see git history for details) 21 | // 2021-03-04: Initial version. 22 | 23 | #include "../imgui.h" 24 | #include "imgui_impl_android.h" 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | // Android data 34 | static double g_Time = 0.0; 35 | static ANativeWindow* g_Window; 36 | static char g_LogTag[] = "ImGuiExample"; 37 | static std::map> g_KeyEventQueues; // FIXME: Remove dependency on map and queue once we use upcoming input queue. 38 | 39 | int32_t ImGui_ImplAndroid_HandleInputEvent(int motion_event, int x, int y, int pointer) 40 | { 41 | ImGuiIO& io = ImGui::GetIO(); 42 | io.MousePos = ImVec2(x, y); 43 | 44 | switch(motion_event) { 45 | case 2: 46 | if (pointer > 1) { 47 | io.MouseDown[0] = false; 48 | } 49 | else { 50 | io.MouseWheel = 0; 51 | } 52 | break; 53 | case 1: 54 | io.MouseDown[0] = false; 55 | break; 56 | case 0: 57 | io.MouseDown[0] = true; 58 | break; 59 | 60 | } 61 | 62 | return 0; 63 | } 64 | int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event) 65 | { 66 | ImGuiIO& io = ImGui::GetIO(); 67 | int32_t event_type = AInputEvent_getType(input_event); 68 | switch (event_type) 69 | { 70 | case AINPUT_EVENT_TYPE_KEY: 71 | { 72 | int32_t event_key_code = AKeyEvent_getKeyCode(input_event); 73 | int32_t event_action = AKeyEvent_getAction(input_event); 74 | int32_t event_meta_state = AKeyEvent_getMetaState(input_event); 75 | 76 | io.KeyCtrl = ((event_meta_state & AMETA_CTRL_ON) != 0); 77 | io.KeyShift = ((event_meta_state & AMETA_SHIFT_ON) != 0); 78 | io.KeyAlt = ((event_meta_state & AMETA_ALT_ON) != 0); 79 | 80 | switch (event_action) 81 | { 82 | // FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once as soon as a touch pointer 83 | // goes up from a key. We use a simple key event queue/ and process one event per key per frame in 84 | // ImGui_ImplAndroid_NewFrame()...or consider using IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787 85 | case AKEY_EVENT_ACTION_DOWN: 86 | case AKEY_EVENT_ACTION_UP: 87 | g_KeyEventQueues[event_key_code].push(event_action); 88 | break; 89 | default: 90 | break; 91 | } 92 | break; 93 | } 94 | case AINPUT_EVENT_TYPE_MOTION: 95 | { 96 | int32_t event_action = AMotionEvent_getAction(input_event); 97 | int32_t event_pointer_index = (event_action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; 98 | event_action &= AMOTION_EVENT_ACTION_MASK; 99 | switch (event_action) 100 | { 101 | case AMOTION_EVENT_ACTION_DOWN: 102 | case AMOTION_EVENT_ACTION_UP: 103 | // Physical mouse buttons (and probably other physical devices) also invoke the actions AMOTION_EVENT_ACTION_DOWN/_UP, 104 | // but we have to process them separately to identify the actual button pressed. This is done below via 105 | // AMOTION_EVENT_ACTION_BUTTON_PRESS/_RELEASE. Here, we only process "FINGER" input (and "UNKNOWN", as a fallback). 106 | if((AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER) 107 | || (AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN)) 108 | { 109 | io.MouseDown[0] = (event_action == AMOTION_EVENT_ACTION_DOWN); 110 | io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); 111 | } 112 | break; 113 | case AMOTION_EVENT_ACTION_BUTTON_PRESS: 114 | case AMOTION_EVENT_ACTION_BUTTON_RELEASE: 115 | { 116 | int32_t button_state = AMotionEvent_getButtonState(input_event); 117 | io.MouseDown[0] = ((button_state & AMOTION_EVENT_BUTTON_PRIMARY) != 0); 118 | io.MouseDown[1] = ((button_state & AMOTION_EVENT_BUTTON_SECONDARY) != 0); 119 | io.MouseDown[2] = ((button_state & AMOTION_EVENT_BUTTON_TERTIARY) != 0); 120 | } 121 | break; 122 | case AMOTION_EVENT_ACTION_HOVER_MOVE: // Hovering: Tool moves while NOT pressed (such as a physical mouse) 123 | case AMOTION_EVENT_ACTION_MOVE: // Touch pointer moves while DOWN 124 | io.MousePos = ImVec2(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index)); 125 | break; 126 | case AMOTION_EVENT_ACTION_SCROLL: 127 | io.MouseWheel = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index); 128 | io.MouseWheelH = AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index); 129 | break; 130 | default: 131 | break; 132 | } 133 | } 134 | return 1; 135 | default: 136 | break; 137 | } 138 | 139 | return 0; 140 | } 141 | 142 | bool ImGui_ImplAndroid_Init(ANativeWindow* window) 143 | { 144 | g_Window = window; 145 | g_Time = 0.0; 146 | 147 | // Setup backend capabilities flags 148 | ImGuiIO& io = ImGui::GetIO(); 149 | io.BackendPlatformName = "ofxImGui"; //imgui_impl_android 150 | 151 | // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. 152 | io.KeyMap[ImGuiKey_Tab] = AKEYCODE_TAB; 153 | io.KeyMap[ImGuiKey_LeftArrow] = AKEYCODE_DPAD_LEFT; // also covers physical keyboard arrow key 154 | io.KeyMap[ImGuiKey_RightArrow] = AKEYCODE_DPAD_RIGHT; // also covers physical keyboard arrow key 155 | io.KeyMap[ImGuiKey_UpArrow] = AKEYCODE_DPAD_UP; // also covers physical keyboard arrow key 156 | io.KeyMap[ImGuiKey_DownArrow] = AKEYCODE_DPAD_DOWN; // also covers physical keyboard arrow key 157 | io.KeyMap[ImGuiKey_PageUp] = AKEYCODE_PAGE_UP; 158 | io.KeyMap[ImGuiKey_PageDown] = AKEYCODE_PAGE_DOWN; 159 | io.KeyMap[ImGuiKey_Home] = AKEYCODE_MOVE_HOME; 160 | io.KeyMap[ImGuiKey_End] = AKEYCODE_MOVE_END; 161 | io.KeyMap[ImGuiKey_Insert] = AKEYCODE_INSERT; 162 | io.KeyMap[ImGuiKey_Delete] = AKEYCODE_FORWARD_DEL; 163 | io.KeyMap[ImGuiKey_Backspace] = AKEYCODE_DEL; 164 | io.KeyMap[ImGuiKey_Space] = AKEYCODE_SPACE; 165 | io.KeyMap[ImGuiKey_Enter] = AKEYCODE_ENTER; 166 | io.KeyMap[ImGuiKey_Escape] = AKEYCODE_ESCAPE; 167 | io.KeyMap[ImGuiKey_KeyPadEnter] = AKEYCODE_NUMPAD_ENTER; 168 | io.KeyMap[ImGuiKey_A] = AKEYCODE_A; 169 | io.KeyMap[ImGuiKey_C] = AKEYCODE_C; 170 | io.KeyMap[ImGuiKey_V] = AKEYCODE_V; 171 | io.KeyMap[ImGuiKey_X] = AKEYCODE_X; 172 | io.KeyMap[ImGuiKey_Y] = AKEYCODE_Y; 173 | io.KeyMap[ImGuiKey_Z] = AKEYCODE_Z; 174 | 175 | return true; 176 | } 177 | 178 | void ImGui_ImplAndroid_Shutdown() 179 | { 180 | } 181 | 182 | void ImGui_ImplAndroid_NewFrame() 183 | { 184 | ImGuiIO& io = ImGui::GetIO(); 185 | 186 | // Process queued key events 187 | // FIXME: This is a workaround for multiple key event actions occurring at once (see above) and can be removed once we use upcoming input queue. 188 | for (auto& key_queue : g_KeyEventQueues) 189 | { 190 | if (key_queue.second.empty()) 191 | continue; 192 | io.KeysDown[key_queue.first] = (key_queue.second.front() == AKEY_EVENT_ACTION_DOWN); 193 | key_queue.second.pop(); 194 | } 195 | 196 | // Setup display size (every frame to accommodate for window resizing) 197 | int32_t window_width = ANativeWindow_getWidth(g_Window); 198 | int32_t window_height = ANativeWindow_getHeight(g_Window); 199 | int display_width = window_width; 200 | int display_height = window_height; 201 | 202 | io.DisplaySize = ImVec2((float)window_width, (float)window_height); 203 | if (window_width > 0 && window_height > 0) 204 | io.DisplayFramebufferScale = ImVec2((float)display_width / window_width, (float)display_height / window_height); 205 | 206 | // Setup time step 207 | struct timespec current_timespec; 208 | clock_gettime(CLOCK_MONOTONIC, ¤t_timespec); 209 | double current_time = (double)(current_timespec.tv_sec) + (current_timespec.tv_nsec / 1000000000.0); 210 | io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); 211 | g_Time = current_time; 212 | } 213 | 214 | void ImGui_ImplAndroid_NewFrame(int x, int y) 215 | { 216 | ImGuiIO& io = ImGui::GetIO(); 217 | 218 | // Process queued key events 219 | // FIXME: This is a workaround for multiple key event actions occurring at once (see above) and can be removed once we use upcoming input queue. 220 | for (auto& key_queue : g_KeyEventQueues) 221 | { 222 | if (key_queue.second.empty()) 223 | continue; 224 | io.KeysDown[key_queue.first] = (key_queue.second.front() == AKEY_EVENT_ACTION_DOWN); 225 | key_queue.second.pop(); 226 | } 227 | 228 | // Setup display size (every frame to accommodate for window resizing) 229 | int32_t window_width = x; 230 | int32_t window_height = y; 231 | int display_width = window_width; 232 | int display_height = window_height; 233 | 234 | io.DisplaySize = ImVec2((float)window_width, (float)window_height); 235 | if (window_width > 0 && window_height > 0) 236 | io.DisplayFramebufferScale = ImVec2((float)display_width / window_width, (float)display_height / window_height); 237 | 238 | // Setup time step 239 | struct timespec current_timespec; 240 | clock_gettime(CLOCK_MONOTONIC, ¤t_timespec); 241 | double current_time = (double)(current_timespec.tv_sec) + (current_timespec.tv_nsec / 1000000000.0); 242 | io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); 243 | g_Time = current_time; 244 | } -------------------------------------------------------------------------------- /app/src/main/jni/Include/ImGui/backends/imgui_impl_android.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Binding for Android native app 2 | // This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3) 3 | 4 | // Implemented features: 5 | // [X] Platform: Keyboard arrays indexed using AKEYCODE_* codes, e.g. ImGui::IsKeyPressed(AKEYCODE_SPACE). 6 | // Missing features: 7 | // [ ] Platform: Clipboard support. 8 | // [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | // [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. 10 | // Important: 11 | // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) 12 | // - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446) 13 | 14 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 15 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 16 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 17 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 18 | 19 | #pragma once 20 | 21 | struct ANativeWindow; 22 | struct AInputEvent; 23 | 24 | IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(ANativeWindow* window = 0); 25 | IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event); 26 | IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(int motion_event, int x, int y, int pointer); 27 | IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown(); 28 | IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame(); 29 | IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame(int x, int y); -------------------------------------------------------------------------------- /app/src/main/jni/Include/ImGui/backends/imgui_impl_opengl3.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline 2 | // - Desktop GL: 2.x 3.x 4.x 3 | // - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) 4 | // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) 5 | 6 | // Implemented features: 7 | // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! 8 | // [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices. 9 | 10 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 11 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 12 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 13 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 14 | 15 | // About GLSL version: 16 | // The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. 17 | // On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" 18 | // Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. 19 | 20 | #pragma once 21 | #include "../imgui.h" // IMGUI_IMPL_API 22 | 23 | // Backend API 24 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); 25 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); 26 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); 27 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); 28 | 29 | // (Optional) Called by Init/NewFrame/Shutdown 30 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); 31 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); 32 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); 33 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); 34 | 35 | // Specific OpenGL ES versions 36 | //#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten 37 | //#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android 38 | 39 | // You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. 40 | #if !defined(IMGUI_IMPL_OPENGL_ES2) \ 41 | && !defined(IMGUI_IMPL_OPENGL_ES3) 42 | 43 | // Try to detect GLES on matching platforms 44 | #if defined(__APPLE__) 45 | #include 46 | #endif 47 | #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) 48 | #define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" 49 | #elif defined(__EMSCRIPTEN__) 50 | #define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" 51 | #else 52 | // Otherwise imgui_impl_opengl3_loader.h will be used. 53 | #endif 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/ImGui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // COMPILE-TIME OPTIONS FOR DEAR IMGUI 3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. 4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. 5 | //----------------------------------------------------------------------------- 6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it) 7 | // B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template. 8 | //----------------------------------------------------------------------------- 9 | // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp 10 | // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. 11 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. 12 | // Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. 13 | //----------------------------------------------------------------------------- 14 | 15 | #pragma once 16 | 17 | //---- Define assertion handler. Defaults to calling assert(). 18 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. 19 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 20 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts 21 | 22 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows 23 | // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. 24 | // DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() 25 | // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. 26 | //#define IMGUI_API __declspec( dllexport ) 27 | //#define IMGUI_API __declspec( dllimport ) 28 | 29 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. 30 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 31 | //#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions. 32 | 33 | //---- Disable all of Dear ImGui or don't implement standard windows. 34 | // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. 35 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. 36 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. 37 | //#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() will be empty. 38 | 39 | //---- Don't implement some functions to reduce linkage requirements. 40 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) 41 | //#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) 42 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) 43 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). 44 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). 45 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) 46 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. 47 | //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) 48 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. 49 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). 50 | //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available 51 | 52 | //---- Include imgui_user.h at the end of imgui.h as a convenience 53 | //#define IMGUI_INCLUDE_IMGUI_USER_H 54 | 55 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) 56 | //#define IMGUI_USE_BGRA_PACKED_COLOR 57 | 58 | //---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) 59 | //#define IMGUI_USE_WCHAR32 60 | 61 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version 62 | // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. 63 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" 64 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" 65 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION 66 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 67 | 68 | //---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) 69 | // Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf. 70 | // #define IMGUI_USE_STB_SPRINTF 71 | 72 | //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) 73 | // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). 74 | // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. 75 | //#define IMGUI_ENABLE_FREETYPE 76 | 77 | //---- Use stb_truetype to build and rasterize the font atlas (default) 78 | // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. 79 | //#define IMGUI_ENABLE_STB_TRUETYPE 80 | 81 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. 82 | // This will be inlined as part of ImVec2 and ImVec4 class declarations. 83 | /* 84 | #define IM_VEC2_CLASS_EXTRA \ 85 | constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \ 86 | operator MyVec2() const { return MyVec2(x,y); } 87 | 88 | #define IM_VEC4_CLASS_EXTRA \ 89 | constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \ 90 | operator MyVec4() const { return MyVec4(x,y,z,w); } 91 | */ 92 | 93 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. 94 | // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). 95 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. 96 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. 97 | //#define ImDrawIdx unsigned int 98 | 99 | //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) 100 | //struct ImDrawList; 101 | //struct ImDrawCmd; 102 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); 103 | //#define ImDrawCallback MyImDrawCallback 104 | 105 | //---- Debug Tools: Macro to break in Debugger 106 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) 107 | //#define IM_DEBUG_BREAK IM_ASSERT(0) 108 | //#define IM_DEBUG_BREAK __debugbreak() 109 | 110 | //---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(), 111 | // (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.) 112 | // This adds a small runtime cost which is why it is not enabled by default. 113 | //#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX 114 | 115 | //---- Debug Tools: Enable slower asserts 116 | //#define IMGUI_DEBUG_PARANOID 117 | 118 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 119 | /* 120 | namespace ImGui 121 | { 122 | void MyFunction(const char* name, const MyMatrix44& v); 123 | } 124 | */ 125 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/KittyMemory/KittyMemory.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // KittyMemory.cpp 3 | // 4 | // Created by MJ (Ruit) on 1/1/19. 5 | // 6 | 7 | #include "../Obfuscate.h" 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 | return relativeAddr; 130 | 131 | ProcMap libMap; 132 | 133 | if(useCache){ 134 | libMap = findMapInCache(libraryName); 135 | if(libMap.isValid()) 136 | return (reinterpret_cast(libMap.startAddr) + relativeAddr); 137 | } 138 | 139 | libMap = getLibraryMap(libraryName); 140 | if (!libMap.isValid()) 141 | return 0; 142 | 143 | if(useCache){ 144 | mapsCache cachedMap; 145 | cachedMap.identifier = libraryName; 146 | cachedMap.map = libMap; 147 | __mapsCache.push_back(cachedMap); 148 | } 149 | 150 | return (reinterpret_cast(libMap.startAddr) + relativeAddr); 151 | } 152 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/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/Include/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/Include/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/Include/KittyMemory/MemoryBackup.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryBackup.cpp 3 | // 4 | // Created by MJ (Ruit) on 4/19/20. 5 | // 6 | 7 | #include "../Obfuscate.h" 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/Include/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/Include/KittyMemory/MemoryPatch.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryPatch.cpp 3 | // 4 | // Created by MJ (Ruit) on 1/1/19. 5 | // 6 | 7 | #include "../Obfuscate.h" 8 | #include "MemoryPatch.h" 9 | #include "../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/Include/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/Include/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 | #define TAG OBFUSCATE("ModMenu") 14 | 15 | #define LOGD(...) ((void)__android_log_print(daDEBUG, TAG, __VA_ARGS__)) 16 | #define LOGE(...) ((void)__android_log_print(daERROR, TAG, __VA_ARGS__)) 17 | #define LOGI(...) ((void)__android_log_print(daINFO, TAG, __VA_ARGS__)) 18 | #define LOGW(...) ((void)__android_log_print(daWARN, TAG, __VA_ARGS__)) 19 | 20 | #endif -------------------------------------------------------------------------------- /app/src/main/jni/Include/Math/Vector2.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define _USE_MATH_DEFINES 4 | #include 5 | 6 | 7 | struct Vector2 8 | { 9 | union 10 | { 11 | struct 12 | { 13 | float X; 14 | float Y; 15 | }; 16 | float data[2]; 17 | }; 18 | 19 | 20 | /** 21 | * Constructors. 22 | */ 23 | inline Vector2(); 24 | inline Vector2(float data[]); 25 | inline Vector2(float value); 26 | inline Vector2(float x, float y); 27 | 28 | 29 | /** 30 | * Constants for common vectors. 31 | */ 32 | static inline Vector2 Zero(); 33 | static inline Vector2 One(); 34 | static inline Vector2 Right(); 35 | static inline Vector2 Left(); 36 | static inline Vector2 Up(); 37 | static inline Vector2 Down(); 38 | 39 | 40 | /** 41 | * Returns the angle between two vectors in radians. 42 | * @param a: The first vector. 43 | * @param b: The second vector. 44 | * @return: A scalar value. 45 | */ 46 | static inline float Angle(Vector2 a, Vector2 b); 47 | 48 | /** 49 | * Returns a vector with its magnitude clamped to maxLength. 50 | * @param vector: The target vector. 51 | * @param maxLength: The maximum length of the return vector. 52 | * @return: A new vector. 53 | */ 54 | static inline Vector2 ClampMagnitude(Vector2 vector, float maxLength); 55 | 56 | /** 57 | * Returns the component of a in the direction of b (scalar projection). 58 | * @param a: The target vector. 59 | * @param b: The vector being compared against. 60 | * @return: A scalar value. 61 | */ 62 | static inline float Component(Vector2 a, Vector2 b); 63 | 64 | /** 65 | * Returns the distance between a and b. 66 | * @param a: The first point. 67 | * @param b: The second point. 68 | * @return: A scalar value. 69 | */ 70 | static inline float Distance(Vector2 a, Vector2 b); 71 | 72 | /** 73 | * Returns the dot product of two vectors. 74 | * @param lhs: The left side of the multiplication. 75 | * @param rhs: The right side of the multiplication. 76 | * @return: A scalar value. 77 | */ 78 | static inline float Dot(Vector2 lhs, Vector2 rhs); 79 | 80 | /** 81 | * Converts a polar representation of a vector into cartesian 82 | * coordinates. 83 | * @param rad: The magnitude of the vector. 84 | * @param theta: The angle from the X axis. 85 | * @return: A new vector. 86 | */ 87 | static inline Vector2 FromPolar(float rad, float theta); 88 | 89 | /** 90 | * Returns a vector linearly interpolated between a and b, moving along 91 | * a straight line. The vector is clamped to never go beyond the end points. 92 | * @param a: The starting point. 93 | * @param b: The ending point. 94 | * @param t: The interpolation value [0-1]. 95 | * @return: A new vector. 96 | */ 97 | static inline Vector2 Lerp(Vector2 a, Vector2 b, float t); 98 | 99 | /** 100 | * Returns a vector linearly interpolated between a and b, moving along 101 | * a straight line. 102 | * @param a: The starting point. 103 | * @param b: The ending point. 104 | * @param t: The interpolation value [0-1] (no actual bounds). 105 | * @return: A new vector. 106 | */ 107 | static inline Vector2 LerpUnclamped(Vector2 a, Vector2 b, float t); 108 | 109 | /** 110 | * Returns the magnitude of a vector. 111 | * @param v: The vector in question. 112 | * @return: A scalar value. 113 | */ 114 | static inline float Magnitude(Vector2 v); 115 | 116 | /** 117 | * Returns a vector made from the largest components of two other vectors. 118 | * @param a: The first vector. 119 | * @param b: The second vector. 120 | * @return: A new vector. 121 | */ 122 | static inline Vector2 Max(Vector2 a, Vector2 b); 123 | 124 | /** 125 | * Returns a vector made from the smallest components of two other vectors. 126 | * @param a: The first vector. 127 | * @param b: The second vector. 128 | * @return: A new vector. 129 | */ 130 | static inline Vector2 Min(Vector2 a, Vector2 b); 131 | 132 | /** 133 | * Returns a vector "maxDistanceDelta" units closer to the target. This 134 | * interpolation is in a straight line, and will not overshoot. 135 | * @param current: The current position. 136 | * @param target: The destination position. 137 | * @param maxDistanceDelta: The maximum distance to move. 138 | * @return: A new vector. 139 | */ 140 | static inline Vector2 MoveTowards(Vector2 current, Vector2 target, 141 | float maxDistanceDelta); 142 | 143 | /** 144 | * Returns a new vector with magnitude of one. 145 | * @param v: The vector in question. 146 | * @return: A new vector. 147 | */ 148 | static inline Vector2 Normalized(Vector2 v); 149 | 150 | /** 151 | * Creates a new coordinate system out of the two vectors. 152 | * Normalizes "normal" and normalizes "tangent" and makes it orthogonal to 153 | * "normal".. 154 | * @param normal: A reference to the first axis vector. 155 | * @param tangent: A reference to the second axis vector. 156 | */ 157 | static inline void OrthoNormalize(Vector2 &normal, Vector2 &tangent); 158 | 159 | /** 160 | * Returns the vector projection of a onto b. 161 | * @param a: The target vector. 162 | * @param b: The vector being projected onto. 163 | * @return: A new vector. 164 | */ 165 | static inline Vector2 Project(Vector2 a, Vector2 b); 166 | 167 | /** 168 | * Returns a vector reflected about the provided line. 169 | * This behaves as if there is a plane with the line as its normal, and the 170 | * vector comes in and bounces off this plane. 171 | * @param vector: The vector traveling inward at the imaginary plane. 172 | * @param line: The line about which to reflect. 173 | * @return: A new vector pointing outward from the imaginary plane. 174 | */ 175 | static inline Vector2 Reflect(Vector2 vector, Vector2 line); 176 | 177 | /** 178 | * Returns the vector rejection of a on b. 179 | * @param a: The target vector. 180 | * @param b: The vector being projected onto. 181 | * @return: A new vector. 182 | */ 183 | static inline Vector2 Reject(Vector2 a, Vector2 b); 184 | 185 | /** 186 | * Rotates vector "current" towards vector "target" by "maxRadiansDelta". 187 | * This treats the vectors as directions and will linearly interpolate 188 | * between their magnitudes by "maxMagnitudeDelta". This function does not 189 | * overshoot. If a negative delta is supplied, it will rotate away from 190 | * "target" until it is pointing the opposite direction, but will not 191 | * overshoot that either. 192 | * @param current: The starting direction. 193 | * @param target: The destination direction. 194 | * @param maxRadiansDelta: The maximum number of radians to rotate. 195 | * @param maxMagnitudeDelta: The maximum delta for magnitude interpolation. 196 | * @return: A new vector. 197 | */ 198 | static inline Vector2 RotateTowards(Vector2 current, Vector2 target, 199 | float maxRadiansDelta, 200 | float maxMagnitudeDelta); 201 | 202 | /** 203 | * Multiplies two vectors component-wise. 204 | * @param a: The lhs of the multiplication. 205 | * @param b: The rhs of the multiplication. 206 | * @return: A new vector. 207 | */ 208 | static inline Vector2 Scale(Vector2 a, Vector2 b); 209 | 210 | /** 211 | * Returns a vector rotated towards b from a by the percent t. 212 | * Since interpolation is done spherically, the vector moves at a constant 213 | * angular velocity. This rotation is clamped to 0 <= t <= 1. 214 | * @param a: The starting direction. 215 | * @param b: The ending direction. 216 | * @param t: The interpolation value [0-1]. 217 | */ 218 | static inline Vector2 Slerp(Vector2 a, Vector2 b, float t); 219 | 220 | /** 221 | * Returns a vector rotated towards b from a by the percent t. 222 | * Since interpolation is done spherically, the vector moves at a constant 223 | * angular velocity. This rotation is unclamped. 224 | * @param a: The starting direction. 225 | * @param b: The ending direction. 226 | * @param t: The interpolation value [0-1]. 227 | */ 228 | static inline Vector2 SlerpUnclamped(Vector2 a, Vector2 b, float t); 229 | 230 | /** 231 | * Returns the squared magnitude of a vector. 232 | * This is useful when comparing relative lengths, where the exact length 233 | * is not important, and much time can be saved by not calculating the 234 | * square root. 235 | * @param v: The vector in question. 236 | * @return: A scalar value. 237 | */ 238 | static inline float SqrMagnitude(Vector2 v); 239 | 240 | /** 241 | * Calculates the polar coordinate space representation of a vector. 242 | * @param vector: The vector to convert. 243 | * @param rad: The magnitude of the vector. 244 | * @param theta: The angle from the X axis. 245 | */ 246 | static inline void ToPolar(Vector2 vector, float &rad, float &theta); 247 | 248 | 249 | /** 250 | * Operator overloading. 251 | */ 252 | inline struct Vector2& operator+=(const float rhs); 253 | inline struct Vector2& operator-=(const float rhs); 254 | inline struct Vector2& operator*=(const float rhs); 255 | inline struct Vector2& operator/=(const float rhs); 256 | inline struct Vector2& operator+=(const Vector2 rhs); 257 | inline struct Vector2& operator-=(const Vector2 rhs); 258 | }; 259 | 260 | inline Vector2 operator-(Vector2 rhs); 261 | inline Vector2 operator+(Vector2 lhs, const float rhs); 262 | inline Vector2 operator-(Vector2 lhs, const float rhs); 263 | inline Vector2 operator*(Vector2 lhs, const float rhs); 264 | inline Vector2 operator/(Vector2 lhs, const float rhs); 265 | inline Vector2 operator+(const float lhs, Vector2 rhs); 266 | inline Vector2 operator-(const float lhs, Vector2 rhs); 267 | inline Vector2 operator*(const float lhs, Vector2 rhs); 268 | inline Vector2 operator/(const float lhs, Vector2 rhs); 269 | inline Vector2 operator+(Vector2 lhs, const Vector2 rhs); 270 | inline Vector2 operator-(Vector2 lhs, const Vector2 rhs); 271 | inline bool operator==(const Vector2 lhs, const Vector2 rhs); 272 | inline bool operator!=(const Vector2 lhs, const Vector2 rhs); 273 | 274 | 275 | 276 | /******************************************************************************* 277 | * Implementation 278 | */ 279 | 280 | Vector2::Vector2() : X(0), Y(0) {} 281 | Vector2::Vector2(float data[]) : X(data[0]), Y(data[1]) {} 282 | Vector2::Vector2(float value) : X(value), Y(value) {} 283 | Vector2::Vector2(float x, float y) : X(x), Y(y) {} 284 | 285 | 286 | Vector2 Vector2::Zero() { return Vector2(0, 0); } 287 | Vector2 Vector2::One() { return Vector2(1, 1); } 288 | Vector2 Vector2::Right() { return Vector2(1, 0); } 289 | Vector2 Vector2::Left() { return Vector2(-1, 0); } 290 | Vector2 Vector2::Up() { return Vector2(0, 1); } 291 | Vector2 Vector2::Down() { return Vector2(0, -1); } 292 | 293 | 294 | float Vector2::Angle(Vector2 a, Vector2 b) 295 | { 296 | float v = Dot(a, b) / (Magnitude(a) * Magnitude(b)); 297 | v = fmax(v, -1.0); 298 | v = fmin(v, 1.0); 299 | return acos(v); 300 | } 301 | 302 | Vector2 Vector2::ClampMagnitude(Vector2 vector, float maxLength) 303 | { 304 | float length = Magnitude(vector); 305 | if (length > maxLength) 306 | vector *= maxLength / length; 307 | return vector; 308 | } 309 | 310 | float Vector2::Component(Vector2 a, Vector2 b) 311 | { 312 | return Dot(a, b) / Magnitude(b); 313 | } 314 | 315 | float Vector2::Distance(Vector2 a, Vector2 b) 316 | { 317 | return Vector2::Magnitude(a - b); 318 | } 319 | 320 | float Vector2::Dot(Vector2 lhs, Vector2 rhs) 321 | { 322 | return lhs.X * rhs.X + lhs.Y * rhs.Y; 323 | } 324 | 325 | Vector2 Vector2::FromPolar(float rad, float theta) 326 | { 327 | Vector2 v = Vector2(); 328 | v.X = rad * cos(theta); 329 | v.Y = rad * sin(theta); 330 | return v; 331 | } 332 | 333 | Vector2 Vector2::Lerp(Vector2 a, Vector2 b, float t) 334 | { 335 | if (t < 0) return a; 336 | else if (t > 1) return b; 337 | return LerpUnclamped(a, b, t); 338 | } 339 | 340 | Vector2 Vector2::LerpUnclamped(Vector2 a, Vector2 b, float t) 341 | { 342 | return (b - a) * t + a; 343 | } 344 | 345 | float Vector2::Magnitude(Vector2 v) 346 | { 347 | return sqrt(SqrMagnitude(v)); 348 | } 349 | 350 | Vector2 Vector2::Max(Vector2 a, Vector2 b) 351 | { 352 | float x = a.X > b.X ? a.X : b.X; 353 | float y = a.Y > b.Y ? a.Y : b.Y; 354 | return Vector2(x, y); 355 | } 356 | 357 | Vector2 Vector2::Min(Vector2 a, Vector2 b) 358 | { 359 | float x = a.X > b.X ? b.X : a.X; 360 | float y = a.Y > b.Y ? b.Y : a.Y; 361 | return Vector2(x, y); 362 | } 363 | 364 | Vector2 Vector2::MoveTowards(Vector2 current, Vector2 target, 365 | float maxDistanceDelta) 366 | { 367 | Vector2 d = target - current; 368 | float m = Magnitude(d); 369 | if (m < maxDistanceDelta || m == 0) 370 | return target; 371 | return current + (d * maxDistanceDelta / m); 372 | } 373 | 374 | Vector2 Vector2::Normalized(Vector2 v) 375 | { 376 | float mag = Magnitude(v); 377 | if (mag == 0) 378 | return Vector2::Zero(); 379 | return v / mag; 380 | } 381 | 382 | void Vector2::OrthoNormalize(Vector2 &normal, Vector2 &tangent) 383 | { 384 | normal = Normalized(normal); 385 | tangent = Reject(tangent, normal); 386 | tangent = Normalized(tangent); 387 | } 388 | 389 | Vector2 Vector2::Project(Vector2 a, Vector2 b) 390 | { 391 | float m = Magnitude(b); 392 | return Dot(a, b) / (m * m) * b; 393 | } 394 | 395 | Vector2 Vector2::Reflect(Vector2 vector, Vector2 planeNormal) 396 | { 397 | return vector - 2 * Project(vector, planeNormal); 398 | } 399 | 400 | Vector2 Vector2::Reject(Vector2 a, Vector2 b) 401 | { 402 | return a - Project(a, b); 403 | } 404 | 405 | Vector2 Vector2::RotateTowards(Vector2 current, Vector2 target, 406 | float maxRadiansDelta, 407 | float maxMagnitudeDelta) 408 | { 409 | float magCur = Magnitude(current); 410 | float magTar = Magnitude(target); 411 | float newMag = magCur + maxMagnitudeDelta * 412 | ((magTar > magCur) - (magCur > magTar)); 413 | newMag = fmin(newMag, fmax(magCur, magTar)); 414 | newMag = fmax(newMag, fmin(magCur, magTar)); 415 | 416 | float totalAngle = Angle(current, target) - maxRadiansDelta; 417 | if (totalAngle <= 0) 418 | return Normalized(target) * newMag; 419 | else if (totalAngle >= M_PI) 420 | return Normalized(-target) * newMag; 421 | 422 | float axis = current.X * target.Y - current.Y * target.X; 423 | axis = axis / fabs(axis); 424 | if (!(1 - fabs(axis) < 0.00001)) 425 | axis = 1; 426 | current = Normalized(current); 427 | Vector2 newVector = current * cos(maxRadiansDelta) + 428 | Vector2(-current.Y, current.X) * sin(maxRadiansDelta) * axis; 429 | return newVector * newMag; 430 | } 431 | 432 | Vector2 Vector2::Scale(Vector2 a, Vector2 b) 433 | { 434 | return Vector2(a.X * b.X, a.Y * b.Y); 435 | } 436 | 437 | Vector2 Vector2::Slerp(Vector2 a, Vector2 b, float t) 438 | { 439 | if (t < 0) return a; 440 | else if (t > 1) return b; 441 | return SlerpUnclamped(a, b, t); 442 | } 443 | 444 | Vector2 Vector2::SlerpUnclamped(Vector2 a, Vector2 b, float t) 445 | { 446 | float magA = Magnitude(a); 447 | float magB = Magnitude(b); 448 | a /= magA; 449 | b /= magB; 450 | float dot = Dot(a, b); 451 | dot = fmax(dot, -1.0); 452 | dot = fmin(dot, 1.0); 453 | float theta = acos(dot) * t; 454 | Vector2 relativeVec = Normalized(b - a * dot); 455 | Vector2 newVec = a * cos(theta) + relativeVec * sin(theta); 456 | return newVec * (magA + (magB - magA) * t); 457 | } 458 | 459 | float Vector2::SqrMagnitude(Vector2 v) 460 | { 461 | return v.X * v.X + v.Y * v.Y; 462 | } 463 | 464 | void Vector2::ToPolar(Vector2 vector, float &rad, float &theta) 465 | { 466 | rad = Magnitude(vector); 467 | theta = atan2(vector.Y, vector.X); 468 | } 469 | 470 | 471 | struct Vector2& Vector2::operator+=(const float rhs) 472 | { 473 | X += rhs; 474 | Y += rhs; 475 | return *this; 476 | } 477 | 478 | struct Vector2& Vector2::operator-=(const float rhs) 479 | { 480 | X -= rhs; 481 | Y -= rhs; 482 | return *this; 483 | } 484 | 485 | struct Vector2& Vector2::operator*=(const float rhs) 486 | { 487 | X *= rhs; 488 | Y *= rhs; 489 | return *this; 490 | } 491 | 492 | struct Vector2& Vector2::operator/=(const float rhs) 493 | { 494 | X /= rhs; 495 | Y /= rhs; 496 | return *this; 497 | } 498 | 499 | struct Vector2& Vector2::operator+=(const Vector2 rhs) 500 | { 501 | X += rhs.X; 502 | Y += rhs.Y; 503 | return *this; 504 | } 505 | 506 | struct Vector2& Vector2::operator-=(const Vector2 rhs) 507 | { 508 | X -= rhs.X; 509 | Y -= rhs.Y; 510 | return *this; 511 | } 512 | 513 | Vector2 operator-(Vector2 rhs) { return rhs * -1; } 514 | Vector2 operator+(Vector2 lhs, const float rhs) { return lhs += rhs; } 515 | Vector2 operator-(Vector2 lhs, const float rhs) { return lhs -= rhs; } 516 | Vector2 operator*(Vector2 lhs, const float rhs) { return lhs *= rhs; } 517 | Vector2 operator/(Vector2 lhs, const float rhs) { return lhs /= rhs; } 518 | Vector2 operator+(const float lhs, Vector2 rhs) { return rhs += lhs; } 519 | Vector2 operator-(const float lhs, Vector2 rhs) { return rhs -= lhs; } 520 | Vector2 operator*(const float lhs, Vector2 rhs) { return rhs *= lhs; } 521 | Vector2 operator/(const float lhs, Vector2 rhs) { return rhs /= lhs; } 522 | Vector2 operator+(Vector2 lhs, const Vector2 rhs) { return lhs += rhs; } 523 | Vector2 operator-(Vector2 lhs, const Vector2 rhs) { return lhs -= rhs; } 524 | 525 | bool operator==(const Vector2 lhs, const Vector2 rhs) 526 | { 527 | return lhs.X == rhs.X && lhs.Y == rhs.Y; 528 | } 529 | 530 | bool operator!=(const Vector2 lhs, const Vector2 rhs) 531 | { 532 | return !(lhs == rhs); 533 | } -------------------------------------------------------------------------------- /app/src/main/jni/Include/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 ay::generate_key(__LINE__) 32 | #endif 33 | 34 | namespace ay 35 | { 36 | using size_type = unsigned long long; 37 | using key_type = unsigned long long; 38 | 39 | // Generate a psuedo-random key that spans all 8 bytes 40 | constexpr key_type generate_key(key_type seed) 41 | { 42 | // Use the MurmurHash3 64-bit finalizer to hash our seed 43 | key_type key = seed; 44 | key ^= (key >> 33); 45 | key *= 0xff51afd7ed558ccd; 46 | key ^= (key >> 33); 47 | key *= 0xc4ceb9fe1a85ec53; 48 | key ^= (key >> 33); 49 | 50 | // Make sure that a bit in each byte is set 51 | key |= 0x0101010101010101ull; 52 | 53 | return key; 54 | } 55 | 56 | // Obfuscates or deobfuscates data with key 57 | constexpr void cipher(char* data, size_type size, key_type key) 58 | { 59 | // Obfuscate with a simple XOR cipher based on key 60 | for (size_type i = 0; i < size; i++) 61 | { 62 | data[i] ^= char(key >> ((i % 8) * 8)); 63 | } 64 | } 65 | 66 | // Obfuscates a string at compile time 67 | template 68 | class obfuscator 69 | { 70 | public: 71 | // Obfuscates the string 'data' on construction 72 | constexpr obfuscator(const char* data) 73 | { 74 | // Copy data 75 | for (size_type i = 0; i < N; i++) 76 | { 77 | m_data[i] = data[i]; 78 | } 79 | 80 | // On construction each of the characters in the string is 81 | // obfuscated with an XOR cipher based on key 82 | cipher(m_data, N, KEY); 83 | } 84 | 85 | constexpr const char* data() const 86 | { 87 | return &m_data[0]; 88 | } 89 | 90 | constexpr size_type size() const 91 | { 92 | return N; 93 | } 94 | 95 | constexpr key_type key() const 96 | { 97 | return KEY; 98 | } 99 | 100 | private: 101 | 102 | char m_data[N]{}; 103 | }; 104 | 105 | // Handles decryption and re-encryption of an encrypted string at runtime 106 | template 107 | class obfuscated_data 108 | { 109 | public: 110 | obfuscated_data(const obfuscator& obfuscator) 111 | { 112 | // Copy obfuscated data 113 | for (size_type i = 0; i < N; i++) 114 | { 115 | m_data[i] = obfuscator.data()[i]; 116 | } 117 | } 118 | 119 | ~obfuscated_data() 120 | { 121 | // Zero m_data to remove it from memory 122 | for (size_type i = 0; i < N; i++) 123 | { 124 | m_data[i] = 0; 125 | } 126 | } 127 | 128 | // Returns a pointer to the plain text string, decrypting it if 129 | // necessary 130 | operator char*() 131 | { 132 | decrypt(); 133 | return m_data; 134 | } 135 | 136 | operator std::string() 137 | { 138 | decrypt(); 139 | return m_data; 140 | } 141 | 142 | // Manually decrypt the string 143 | void decrypt() 144 | { 145 | if (m_encrypted) 146 | { 147 | cipher(m_data, N, KEY); 148 | m_encrypted = false; 149 | } 150 | } 151 | 152 | // Manually re-encrypt the string 153 | void encrypt() 154 | { 155 | if (!m_encrypted) 156 | { 157 | cipher(m_data, N, KEY); 158 | m_encrypted = true; 159 | } 160 | } 161 | 162 | // Returns true if this string is currently encrypted, false otherwise. 163 | bool is_encrypted() const 164 | { 165 | return m_encrypted; 166 | } 167 | 168 | private: 169 | 170 | // Local storage for the string. Call is_encrypted() to check whether or 171 | // not the string is currently obfuscated. 172 | char m_data[N]; 173 | 174 | // Whether data is currently encrypted 175 | bool m_encrypted{ true }; 176 | }; 177 | 178 | // This function exists purely to extract the number of elements 'N' in the 179 | // array 'data' 180 | template 181 | constexpr auto make_obfuscator(const char(&data)[N]) 182 | { 183 | return obfuscator(data); 184 | } 185 | } 186 | 187 | // Obfuscates the string 'data' at compile-time and returns a reference to a 188 | // ay::obfuscated_data object with global lifetime that has functions for 189 | // decrypting the string and is also implicitly convertable to a char* 190 | #define OBFUSCATE(data) OBFUSCATE_KEY(data, AY_OBFUSCATE_DEFAULT_KEY) 191 | 192 | // Obfuscates the string 'data' with 'key' at compile-time and returns a 193 | // reference to a ay::obfuscated_data object with global lifetime that has 194 | // functions for decrypting the string and is also implicitly convertable to a 195 | // char* 196 | #define OBFUSCATE_KEY(data, key) \ 197 | []() -> ay::obfuscated_data& { \ 198 | static_assert(sizeof(decltype(key)) == sizeof(ay::key_type), "key must be a 64 bit unsigned integer"); \ 199 | static_assert((key) >= (1ull << 56), "key must span all 8 bytes"); \ 200 | constexpr auto n = sizeof(data)/sizeof(data[0]); \ 201 | constexpr auto obfuscator = ay::make_obfuscator(data); \ 202 | static auto obfuscated_data = ay::obfuscated_data(obfuscator); \ 203 | return obfuscated_data; \ 204 | }() 205 | 206 | /* -------------------------------- LICENSE ------------------------------------ 207 | Public Domain (http://www.unlicense.org) 208 | This is free and unencumbered software released into the public domain. 209 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 210 | software, either in source code form or as a compiled binary, for any purpose, 211 | commercial or non-commercial, and by any means. 212 | In jurisdictions that recognize copyright laws, the author or authors of this 213 | software dedicate any and all copyright interest in the software to the public 214 | domain. We make this dedication for the benefit of the public at large and to 215 | the detriment of our heirs and successors. We intend this dedication to be an 216 | overt act of relinquishment in perpetuity of all present and future rights to 217 | this software under copyright law. 218 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 219 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 220 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE 221 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 222 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 223 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 224 | ----------------------------------------------------------------------------- */ -------------------------------------------------------------------------------- /app/src/main/jni/Include/RemapTools.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by reveny on 10/07/2023. 3 | // Inspired by riru and zygisk 4 | // 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace RemapTools { 13 | struct ProcMapInfo { 14 | uintptr_t start; 15 | uintptr_t end; 16 | uintptr_t offset; 17 | uint8_t perms; 18 | ino_t inode; 19 | char* dev; 20 | char* path; 21 | }; 22 | 23 | std::vector ListModulesWithName(std::string name) { 24 | std::vector returnVal; 25 | 26 | char buffer[512]; 27 | FILE *fp = fopen("/proc/self/maps", "re"); 28 | if (fp != nullptr) { 29 | while (fgets(buffer, sizeof(buffer), fp)) { 30 | if (strstr(buffer, name.c_str())) { 31 | ProcMapInfo info{}; 32 | char perms[10]; 33 | char path[255]; 34 | char dev[25]; 35 | 36 | sscanf(buffer, "%lx-%lx %s %ld %s %ld %s", &info.start, &info.end, perms, &info.offset, dev, &info.inode, path); 37 | 38 | //Process Perms 39 | if (strchr(perms, 'r')) info.perms |= PROT_READ; 40 | if (strchr(perms, 'w')) info.perms |= PROT_WRITE; 41 | if (strchr(perms, 'x')) info.perms |= PROT_EXEC; 42 | if (strchr(perms, 'r')) info.perms |= PROT_READ; 43 | 44 | //Set all other information 45 | info.dev = dev; 46 | info.path = path; 47 | 48 | LOGI("Line: %s", buffer); 49 | returnVal.push_back(info); 50 | } 51 | } 52 | } 53 | return returnVal; 54 | } 55 | 56 | void RemapLibrary(std::string name) { 57 | std::vector maps = ListModulesWithName(name); 58 | 59 | for (ProcMapInfo info : maps) { 60 | void *address = (void *)info.start; 61 | size_t size = info.end - info.start; 62 | void *map = mmap(0, size, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 63 | 64 | if ((info.perms & PROT_READ) == 0) { 65 | LOGI("Removing protection: %s", info.path); 66 | mprotect(address, size, PROT_READ); 67 | } 68 | 69 | if (map == nullptr) { 70 | LOGE("Failed to Allocate Memory: %s", strerror(errno)); 71 | } 72 | LOGI("Allocated at address %p with size of %zu", map, size); 73 | 74 | //Copy to new location 75 | std::memmove(map, address, size); 76 | mremap(map, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, info.start); 77 | 78 | //Reapply protection 79 | mprotect((void *)info.start, size, info.perms); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/Unity.h: -------------------------------------------------------------------------------- 1 | #include "Math/Vector3.hpp" 2 | #include "Math/Quaternion.hpp" 3 | 4 | float NormalizeAngle (float angle){ 5 | while (angle>360) 6 | angle -= 360; 7 | while (angle<0) 8 | angle += 360; 9 | return angle; 10 | } 11 | 12 | Vector3 NormalizeAngles (Vector3 angles){ 13 | angles.X = NormalizeAngle (angles.X); 14 | angles.Y = NormalizeAngle (angles.Y); 15 | angles.Z = NormalizeAngle (angles.Z); 16 | return angles; 17 | } 18 | 19 | bool compareVectorsWithTolerance(Vector3 first, Vector3 second, float tolerance){ 20 | float firstXSubbed = first.X - tolerance; 21 | float firstXAdded = first.X + tolerance; 22 | 23 | float firstYSubbed = first.Y - tolerance; 24 | float firstYAdded = first.Y + tolerance; 25 | 26 | float firstZSubbed = first.Z - tolerance; 27 | float firstZAdded = first.Z + tolerance; 28 | 29 | bool secondXFallsBetween = second.X >= firstXSubbed && second.X <= firstXAdded; 30 | bool secondYFallsBetween = second.Y >= firstYSubbed && second.Y <= firstYAdded; 31 | bool secondZFallsBetween = second.Z >= firstZSubbed && second.Z <= firstZAdded; 32 | 33 | return secondXFallsBetween && secondYFallsBetween && secondZFallsBetween; 34 | } 35 | 36 | Quaternion GetRotationToLocation(Vector3 targetLocation, float y_bias, Vector3 myLoc){ 37 | return Quaternion::LookRotation((targetLocation + Vector3(0, y_bias, 0)) - myLoc, Vector3(0, 1, 0)); 38 | } 39 | 40 | typedef struct _monoString { 41 | void *klass; 42 | void *monitor; 43 | int length; 44 | char16_t chars[1]; 45 | 46 | int getLength() { 47 | return length; 48 | } 49 | 50 | char16_t *getRawChars() { 51 | return chars; 52 | } 53 | } monoString; 54 | 55 | template 56 | struct monoArray 57 | { 58 | void* klass; 59 | void* monitor; 60 | void* bounds; 61 | int max_length; 62 | void* vector [1]; 63 | 64 | int getLength() 65 | { 66 | return max_length; 67 | } 68 | T getPointer() 69 | { 70 | return (T)vector; 71 | } 72 | }; 73 | 74 | template 75 | struct monoList { 76 | void *unk0; 77 | void *unk1; 78 | monoArray *items; 79 | int size; 80 | int version; 81 | 82 | T getItems() { 83 | return items->getPointer(); 84 | } 85 | 86 | int getSize() { 87 | return size; 88 | } 89 | 90 | int getVersion() { 91 | return version; 92 | } 93 | }; 94 | 95 | template 96 | struct monoDictionary { 97 | void *unk0; 98 | void *unk1; 99 | monoArray *table; 100 | monoArray *linkSlots; 101 | monoArray *keys; 102 | monoArray *values; 103 | int touchedSlots; 104 | int emptySlot; 105 | int size; 106 | 107 | K getKeys() { 108 | return keys->getPointer(); 109 | } 110 | 111 | V getValues() { 112 | return values->getPointer(); 113 | } 114 | 115 | int getNumKeys() { 116 | return keys->getLength(); 117 | } 118 | 119 | int getNumValues() { 120 | return values->getLength(); 121 | } 122 | 123 | int getSize() { 124 | return size; 125 | } 126 | }; -------------------------------------------------------------------------------- /app/src/main/jni/Include/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS 2 | #define UTILS 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #if defined(__arm__) 16 | #define process_vm_readv_syscall 376 17 | #define process_vm_writev_syscall 377 18 | #elif defined(__aarch64__) 19 | #define process_vm_readv_syscall 270 20 | #define process_vm_writev_syscall 271 21 | #elif defined(__i386__) 22 | #define process_vm_readv_syscall 347 23 | #define process_vm_writev_syscall 348 24 | #else 25 | #define process_vm_readv_syscall 310 26 | #define process_vm_writev_syscall 311 27 | #endif 28 | 29 | ssize_t process_v(pid_t __pid, const struct iovec *__local_iov, unsigned long __local_iov_count, const struct iovec *__remote_iov, unsigned long __remote_iov_count, unsigned long __flags, bool iswrite) { 30 | return syscall((iswrite ? process_vm_writev_syscall : process_vm_readv_syscall), __pid, __local_iov, __local_iov_count, __remote_iov, __remote_iov_count, __flags); 31 | } 32 | 33 | bool pvm(void *address, void *buffer, size_t size, bool write = false) { 34 | struct iovec local[1]; 35 | struct iovec remote[1]; 36 | 37 | local[0].iov_base = buffer; 38 | local[0].iov_len = size; 39 | remote[0].iov_base = address; 40 | remote[0].iov_len = size; 41 | 42 | ssize_t bytes = process_v(getpid(), local, 1, remote, 1, 0, write); 43 | return bytes == size; 44 | } 45 | 46 | bool ReadAddr(void *addr, void *buffer, size_t length) { 47 | return pvm(addr, buffer, length, false); 48 | } 49 | 50 | typedef unsigned long DWORD; 51 | static uintptr_t libBase; 52 | 53 | bool isGameLibLoaded = false; 54 | 55 | DWORD findLibrary(const char *library) { 56 | char filename[0xFF] = {0}, 57 | buffer[1024] = {0}; 58 | FILE *fp = NULL; 59 | DWORD address = 0; 60 | 61 | pid_t pid = getpid(); 62 | sprintf(filename, "/proc/%d/maps", pid); 63 | 64 | fp = fopen(filename, "rt"); 65 | if (fp == NULL) { 66 | perror("fopen"); 67 | goto done; 68 | } 69 | 70 | while (fgets(buffer, sizeof(buffer), fp)) { 71 | if (strstr(buffer, library)) { 72 | address = (DWORD) strtoul(buffer, NULL, 16); 73 | goto done; 74 | } 75 | } 76 | 77 | done: 78 | if (fp) { 79 | fclose(fp); 80 | } 81 | 82 | return address; 83 | } 84 | 85 | uintptr_t getBaseAddress(const char *name) { 86 | uintptr_t base = 0; 87 | char line[512]; 88 | 89 | FILE *f = fopen("/proc/self/maps", "r"); 90 | 91 | if (!f) { 92 | return 0; 93 | } 94 | 95 | while (fgets(line, sizeof line, f)) { 96 | uintptr_t tmpBase; 97 | char tmpName[256]; 98 | if (sscanf(line, "%" PRIXPTR "-%*" PRIXPTR " %*s %*s %*s %*s %s", &tmpBase, tmpName) > 0) { 99 | if (!strcmp(basename(tmpName), name)) { 100 | base = tmpBase; 101 | break; 102 | } 103 | } 104 | } 105 | 106 | fclose(f); 107 | return base; 108 | } 109 | 110 | uintptr_t getAbsoluteAddress(const char *libraryName, uintptr_t relativeAddr) { 111 | libBase = findLibrary(libraryName); 112 | if (libBase == 0) 113 | return 0; 114 | return (libBase + relativeAddr); 115 | } 116 | 117 | bool isLibraryLoaded(const char *libraryName) { 118 | //isGameLibLoaded = true; 119 | char line[512] = {0}; 120 | 121 | FILE *fp = fopen("/proc/self/maps", "rt"); 122 | if (fp != NULL) { 123 | while (fgets(line, sizeof(line), fp)) { 124 | std::string a = line; 125 | if (strstr(line, libraryName)) { 126 | isGameLibLoaded = true; 127 | return true; 128 | } 129 | } 130 | fclose(fp); 131 | } 132 | return false; 133 | } 134 | 135 | uintptr_t string2Offset(const char *c) { 136 | int base = 16; 137 | // See if this function catches all possibilities. 138 | // If it doesn't, the function would have to be amended 139 | // whenever you add a combination of architecture and 140 | // compiler that is not yet addressed. 141 | static_assert(sizeof(uintptr_t) == sizeof(unsigned long) 142 | || sizeof(uintptr_t) == sizeof(unsigned long long), 143 | "Please add string to handle conversion for this architecture."); 144 | 145 | // Now choose the correct function ... 146 | if (sizeof(uintptr_t) == sizeof(unsigned long)) { 147 | return strtoul(c, nullptr, base); 148 | } 149 | 150 | // All other options exhausted, sizeof(uintptr_t) == sizeof(unsigned long long)) 151 | return strtoull(c, nullptr, base); 152 | } 153 | 154 | namespace Toast { 155 | inline const int LENGTH_LONG = 1; 156 | inline const int LENGTH_SHORT = 0; 157 | } 158 | 159 | struct lib_info { 160 | void* start_address; 161 | void* end_address; 162 | intptr_t size; 163 | std::string name; 164 | }; 165 | 166 | lib_info find_library(const char *module_name){ 167 | lib_info library_info{}; 168 | char line[512], mod_name[64]; 169 | 170 | FILE *fp = fopen("/proc/self/maps", "rt"); 171 | if (fp != NULL) 172 | { 173 | while (fgets(line, sizeof(line), fp)) 174 | { 175 | if (strstr(line, module_name)) 176 | { 177 | sscanf(line, "%llx-%llx %*s %*ld %*s %*d %s", 178 | (long long unsigned *)&library_info.start_address, 179 | (long long unsigned *)&library_info.end_address, mod_name); 180 | 181 | library_info.size = (uintptr_t)library_info.end_address - (uintptr_t)library_info.start_address; 182 | 183 | if (library_info.name.empty()) 184 | library_info.name = std::string(mod_name); 185 | 186 | break; 187 | } 188 | } 189 | fclose(fp); 190 | } 191 | return library_info; 192 | } 193 | 194 | uintptr_t find_pattern(uint8_t* start, const size_t length, const char* pattern) { 195 | const char* pat = pattern; 196 | uint8_t* first_match = 0; 197 | for (auto current_byte = start; current_byte < (start + length); ++current_byte) { 198 | if (*pat == '?' || *current_byte == strtoul(pat, NULL, 16)) { 199 | if (!first_match) 200 | first_match = current_byte; 201 | if (!pat[2]) 202 | return (uintptr_t)first_match; 203 | pat += *(uint16_t*)pat == 16191 || *pat != '?' ? 3 : 2; 204 | } 205 | else if (first_match) { 206 | current_byte = first_match; 207 | pat = pattern; 208 | first_match = 0; 209 | } 210 | } return 0; 211 | } 212 | 213 | uintptr_t find_pattern_in_module(const char* lib_name, const char* pattern) { 214 | lib_info lib_info = find_library(lib_name); 215 | return find_pattern((uint8_t*)lib_info.start_address, lib_info.size, pattern); 216 | } 217 | 218 | uintptr_t find_pattern_in_module_opcode(const char* lib_name, const char* pattern) { 219 | lib_info lib_info = find_library(lib_name); 220 | uintptr_t addr = find_pattern((uint8_t*)lib_info.start_address, lib_info.size, pattern); 221 | void* realAddr = nullptr; 222 | ReadAddr((void*)addr, realAddr, sizeof(void*)); 223 | return (uintptr_t)realAddr; 224 | } 225 | 226 | #endif -------------------------------------------------------------------------------- /app/src/main/jni/Include/xdl/include/xdl.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-04. 23 | 24 | // 25 | // xDL version: 1.2.1 26 | // 27 | // xDL is an enhanced implementation of the Android DL series functions. 28 | // For more information, documentation, and the latest version please check: 29 | // https://github.com/hexhacking/xDL 30 | // 31 | 32 | #ifndef IO_HEXHACKING_XDL 33 | #define IO_HEXHACKING_XDL 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | typedef struct { 44 | // same as Dl_info: 45 | const char *dli_fname; // Pathname of shared object that contains address. 46 | void *dli_fbase; // Address at which shared object is loaded. 47 | const char *dli_sname; // Name of nearest symbol with address lower than addr. 48 | void *dli_saddr; // Exact address of symbol named in dli_sname. 49 | // added by xDL: 50 | size_t dli_ssize; // Symbol size of nearest symbol with address lower than addr. 51 | const ElfW(Phdr) *dlpi_phdr; // Pointer to array of ELF program headers for this object. 52 | size_t dlpi_phnum; // Number of items in dlpi_phdr. 53 | } xdl_info_t; 54 | 55 | // 56 | // Default value for flags in both xdl_open() and xdl_iterate_phdr(). 57 | // 58 | #define XDL_DEFAULT 0x00 59 | 60 | // 61 | // Enhanced dlopen() / dlclose() / dlsym(). 62 | // 63 | #define XDL_TRY_FORCE_LOAD 0x01 64 | #define XDL_ALWAYS_FORCE_LOAD 0x02 65 | void *xdl_open(const char *filename, int flags); 66 | void *xdl_close(void *handle); 67 | void *xdl_sym(void *handle, const char *symbol, size_t *symbol_size); 68 | void *xdl_dsym(void *handle, const char *symbol, size_t *symbol_size); 69 | 70 | // 71 | // Enhanced dladdr(). 72 | // 73 | int xdl_addr(void *addr, xdl_info_t *info, void **cache); 74 | void xdl_addr_clean(void **cache); 75 | 76 | // 77 | // Enhanced dl_iterate_phdr(). 78 | // 79 | #define XDL_FULL_PATHNAME 0x01 80 | int xdl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data, int flags); 81 | 82 | // 83 | // Custom dlinfo(). 84 | // 85 | #define XDL_DI_DLINFO 1 // type of info: xdl_info_t 86 | int xdl_info(void *handle, int request, void *info); 87 | 88 | #ifdef __cplusplus 89 | } 90 | #endif 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/xdl/xdl_iterate.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-04. 23 | 24 | #include "xdl_iterate.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "include/xdl.h" 40 | #include "xdl_linker.h" 41 | #include "xdl_util.h" 42 | 43 | /* 44 | * ========================================================================================================= 45 | * API-LEVEL ANDROID-VERSION SOLUTION 46 | * ========================================================================================================= 47 | * 16 4.1 /proc/self/maps 48 | * 17 4.2 /proc/self/maps 49 | * 18 4.3 /proc/self/maps 50 | * 19 4.4 /proc/self/maps 51 | * 20 4.4W /proc/self/maps 52 | * --------------------------------------------------------------------------------------------------------- 53 | * 21 5.0 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3) 54 | * 22 5.1 dl_iterate_phdr() + __dl__ZL10g_dl_mutex + linker/linker64 from getauxval(3) 55 | * --------------------------------------------------------------------------------------------------------- 56 | * 23 >= 6.0 dl_iterate_phdr() + linker/linker64 from getauxval(3) 57 | * ========================================================================================================= 58 | */ 59 | 60 | extern __attribute((weak)) int dl_iterate_phdr(int (*)(struct dl_phdr_info *, size_t, void *), void *); 61 | extern __attribute((weak)) unsigned long int getauxval(unsigned long int); 62 | 63 | static uintptr_t xdl_iterate_get_min_vaddr(struct dl_phdr_info *info) { 64 | uintptr_t min_vaddr = UINTPTR_MAX; 65 | for (size_t i = 0; i < info->dlpi_phnum; i++) { 66 | const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]); 67 | if (PT_LOAD == phdr->p_type) { 68 | if (min_vaddr > phdr->p_vaddr) min_vaddr = phdr->p_vaddr; 69 | } 70 | } 71 | return min_vaddr; 72 | } 73 | 74 | static int xdl_iterate_open_or_rewind_maps(FILE **maps) { 75 | if (NULL == *maps) { 76 | *maps = fopen("/proc/self/maps", "r"); 77 | if (NULL == *maps) return -1; 78 | } else 79 | rewind(*maps); 80 | 81 | return 0; 82 | } 83 | 84 | static int xdl_iterate_get_pathname_from_maps(uintptr_t base, char *buf, size_t buf_len, FILE **maps) { 85 | // open or rewind maps-file 86 | if (0 != xdl_iterate_open_or_rewind_maps(maps)) return -1; // failed 87 | 88 | char line[1024]; 89 | while (fgets(line, sizeof(line), *maps)) { 90 | // check base address 91 | uintptr_t start, end; 92 | if (2 != sscanf(line, "%" SCNxPTR "-%" SCNxPTR " r", &start, &end)) continue; 93 | if (base < start) break; // failed 94 | if (base >= end) continue; 95 | 96 | // get pathname 97 | char *pathname = strchr(line, '/'); 98 | if (NULL == pathname) break; // failed 99 | xdl_util_trim_ending(pathname); 100 | 101 | // found it 102 | strlcpy(buf, pathname, buf_len); 103 | return 0; // OK 104 | } 105 | 106 | return -1; // failed 107 | } 108 | 109 | static int xdl_iterate_by_linker_cb(struct dl_phdr_info *info, size_t size, void *arg) { 110 | uintptr_t *pkg = (uintptr_t *)arg; 111 | xdl_iterate_phdr_cb_t cb = (xdl_iterate_phdr_cb_t)*pkg++; 112 | void *cb_arg = (void *)*pkg++; 113 | FILE **maps = (FILE **)*pkg++; 114 | uintptr_t linker_load_bias = *pkg++; 115 | int flags = (int)*pkg; 116 | 117 | // ignore invalid ELF 118 | if (0 == info->dlpi_addr || NULL == info->dlpi_name || '\0' == info->dlpi_name[0]) return 0; 119 | 120 | // ignore linker if we have returned it already 121 | if (linker_load_bias == info->dlpi_addr) return 0; 122 | 123 | struct dl_phdr_info info_fixed; 124 | info_fixed.dlpi_addr = info->dlpi_addr; 125 | info_fixed.dlpi_name = info->dlpi_name; 126 | info_fixed.dlpi_phdr = info->dlpi_phdr; 127 | info_fixed.dlpi_phnum = info->dlpi_phnum; 128 | info = &info_fixed; 129 | 130 | // fix dlpi_phdr & dlpi_phnum (from memory) 131 | if (NULL == info->dlpi_phdr || 0 == info->dlpi_phnum) { 132 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)info->dlpi_addr; 133 | info->dlpi_phdr = (ElfW(Phdr) *)(info->dlpi_addr + ehdr->e_phoff); 134 | info->dlpi_phnum = ehdr->e_phnum; 135 | } 136 | 137 | // fix dlpi_name (from /proc/self/maps) 138 | if ('/' != info->dlpi_name[0] && '[' != info->dlpi_name[0] && (0 != (flags & XDL_FULL_PATHNAME))) { 139 | // get base address 140 | uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(info); 141 | if (UINTPTR_MAX == min_vaddr) return 0; // ignore this ELF 142 | uintptr_t base = (uintptr_t)(info->dlpi_addr + min_vaddr); 143 | 144 | char buf[1024]; 145 | if (0 != xdl_iterate_get_pathname_from_maps(base, buf, sizeof(buf), maps)) return 0; // ignore this ELF 146 | 147 | info->dlpi_name = (const char *)buf; 148 | } 149 | 150 | // callback 151 | return cb(info, size, cb_arg); 152 | } 153 | 154 | static uintptr_t xdl_iterate_get_linker_base(void) { 155 | if (NULL == getauxval) return 0; 156 | 157 | uintptr_t base = (uintptr_t)getauxval(AT_BASE); 158 | if (0 == base) return 0; 159 | if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) return 0; 160 | 161 | return base; 162 | } 163 | 164 | static int xdl_iterate_do_callback(xdl_iterate_phdr_cb_t cb, void *cb_arg, uintptr_t base, 165 | const char *pathname, uintptr_t *load_bias) { 166 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; 167 | 168 | struct dl_phdr_info info; 169 | info.dlpi_name = pathname; 170 | info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); 171 | info.dlpi_phnum = ehdr->e_phnum; 172 | 173 | // get load bias 174 | uintptr_t min_vaddr = xdl_iterate_get_min_vaddr(&info); 175 | if (UINTPTR_MAX == min_vaddr) return 0; // ignore invalid ELF 176 | info.dlpi_addr = (ElfW(Addr))(base - min_vaddr); 177 | if (NULL != load_bias) *load_bias = info.dlpi_addr; 178 | 179 | return cb(&info, sizeof(struct dl_phdr_info), cb_arg); 180 | } 181 | 182 | static int xdl_iterate_by_linker(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) { 183 | if (NULL == dl_iterate_phdr) return 0; 184 | 185 | int api_level = xdl_util_get_api_level(); 186 | FILE *maps = NULL; 187 | int r; 188 | 189 | // dl_iterate_phdr(3) does NOT contain linker/linker64 when Android version < 8.1 (API level 27). 190 | // Here we always try to get linker base address from auxv. 191 | uintptr_t linker_load_bias = 0; 192 | uintptr_t linker_base = xdl_iterate_get_linker_base(); 193 | if (0 != linker_base) { 194 | if (0 != 195 | (r = xdl_iterate_do_callback(cb, cb_arg, linker_base, XDL_UTIL_LINKER_PATHNAME, &linker_load_bias))) 196 | return r; 197 | } 198 | 199 | // for other ELF 200 | uintptr_t pkg[5] = {(uintptr_t)cb, (uintptr_t)cb_arg, (uintptr_t)&maps, linker_load_bias, (uintptr_t)flags}; 201 | if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_lock(); 202 | r = dl_iterate_phdr(xdl_iterate_by_linker_cb, pkg); 203 | if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) xdl_linker_unlock(); 204 | 205 | if (NULL != maps) fclose(maps); 206 | return r; 207 | } 208 | 209 | #if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ 210 | static int xdl_iterate_by_maps(xdl_iterate_phdr_cb_t cb, void *cb_arg) { 211 | FILE *maps = fopen("/proc/self/maps", "r"); 212 | if (NULL == maps) return 0; 213 | 214 | int r = 0; 215 | char buf1[1024], buf2[1024]; 216 | char *line = buf1; 217 | uintptr_t prev_base = 0; 218 | bool try_next_line = false; 219 | 220 | while (fgets(line, sizeof(buf1), maps)) { 221 | // Try to find an ELF which loaded by linker. 222 | uintptr_t base, offset; 223 | char exec; 224 | if (3 != sscanf(line, "%" SCNxPTR "-%*" SCNxPTR " r%*c%cp %" SCNxPTR " ", &base, &exec, &offset)) goto clean; 225 | 226 | if ('-' == exec && 0 == offset) { 227 | // r--p 228 | prev_base = base; 229 | line = (line == buf1 ? buf2 : buf1); 230 | try_next_line = true; 231 | continue; 232 | } 233 | else if (exec == 'x') { 234 | // r-xp 235 | char *pathname = NULL; 236 | if (try_next_line && 0 != offset) { 237 | char *prev = (line == buf1 ? buf2 : buf1); 238 | char *prev_pathname = strchr(prev, '/'); 239 | if (NULL == prev_pathname) goto clean; 240 | 241 | pathname = strchr(line, '/'); 242 | if (NULL == pathname) goto clean; 243 | 244 | xdl_util_trim_ending(prev_pathname); 245 | xdl_util_trim_ending(pathname); 246 | if (0 != strcmp(prev_pathname, pathname)) goto clean; 247 | 248 | // we found the line with r-xp in the next line 249 | base = prev_base; 250 | offset = 0; 251 | } 252 | 253 | if (0 != offset) goto clean; 254 | 255 | // get pathname 256 | if (NULL == pathname) { 257 | pathname = strchr(line, '/'); 258 | if (NULL == pathname) goto clean; 259 | xdl_util_trim_ending(pathname); 260 | } 261 | 262 | if (0 != memcmp((void *)base, ELFMAG, SELFMAG)) goto clean; 263 | ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base; 264 | struct dl_phdr_info info; 265 | info.dlpi_name = pathname; 266 | info.dlpi_phdr = (const ElfW(Phdr) *)(base + ehdr->e_phoff); 267 | info.dlpi_phnum = ehdr->e_phnum; 268 | 269 | // callback 270 | if (0 != (r = xdl_iterate_do_callback(cb, cb_arg, base, pathname, NULL))) break; 271 | } 272 | 273 | clean: 274 | try_next_line = false; 275 | } 276 | 277 | fclose(maps); 278 | return r; 279 | } 280 | #endif 281 | 282 | int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags) { 283 | // iterate by /proc/self/maps in Android 4.x (Android 4.x only supports arm32 and x86) 284 | #if (defined(__arm__) || defined(__i386__)) && __ANDROID_API__ < __ANDROID_API_L__ 285 | if (xdl_util_get_api_level() < __ANDROID_API_L__) return xdl_iterate_by_maps(cb, cb_arg); 286 | #endif 287 | 288 | // iterate by dl_iterate_phdr() 289 | return xdl_iterate_by_linker(cb, cb_arg, flags); 290 | } 291 | 292 | int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len) { 293 | FILE *maps = NULL; 294 | int r = xdl_iterate_get_pathname_from_maps(base, buf, buf_len, &maps); 295 | if (NULL != maps) fclose(maps); 296 | return r; 297 | } 298 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/xdl/xdl_iterate.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-04. 23 | 24 | #ifndef IO_HEXHACKING_XDL_ITERATE 25 | #define IO_HEXHACKING_XDL_ITERATE 26 | 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | typedef int (*xdl_iterate_phdr_cb_t)(struct dl_phdr_info *info, size_t size, void *arg); 35 | int xdl_iterate_phdr_impl(xdl_iterate_phdr_cb_t cb, void *cb_arg, int flags); 36 | 37 | int xdl_iterate_get_full_pathname(uintptr_t base, char *buf, size_t buf_len); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/xdl/xdl_linker.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2021-02-21. 23 | 24 | #include "xdl_linker.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "include/xdl.h" 32 | #include "xdl_iterate.h" 33 | #include "xdl_util.h" 34 | 35 | #define XDL_LINKER_SYM_MUTEX "__dl__ZL10g_dl_mutex" 36 | #define XDL_LINKER_SYM_DLOPEN_EXT_N "__dl__ZL10dlopen_extPKciPK17android_dlextinfoPv" 37 | #define XDL_LINKER_SYM_DO_DLOPEN_N "__dl__Z9do_dlopenPKciPK17android_dlextinfoPv" 38 | #define XDL_LINKER_SYM_DLOPEN_O "__dl__Z8__dlopenPKciPKv" 39 | #define XDL_LINKER_SYM_LOADER_DLOPEN_P "__loader_dlopen" 40 | 41 | typedef void *(*xdl_linker_dlopen_n_t)(const char *, int, const void *, void *); 42 | typedef void *(*xdl_linker_dlopen_o_t)(const char *, int, const void *); 43 | 44 | static pthread_mutex_t *xdl_linker_mutex = NULL; 45 | static void *xdl_linker_dlopen = NULL; 46 | 47 | static void *xdl_linker_caller_addr[] = { 48 | NULL, // default 49 | NULL, // art 50 | NULL // vendor 51 | }; 52 | 53 | #ifndef __LP64__ 54 | #define XDL_LINKER_LIB "lib" 55 | #else 56 | #define XDL_LINKER_LIB "lib64" 57 | #endif 58 | static const char *xdl_linker_vendor_path[] = { 59 | // order is important 60 | "/vendor/" XDL_LINKER_LIB "/egl/", "/vendor/" XDL_LINKER_LIB "/hw/", 61 | "/vendor/" XDL_LINKER_LIB "/", "/odm/" XDL_LINKER_LIB "/", 62 | "/vendor/" XDL_LINKER_LIB "/vndk-sp/", "/odm/" XDL_LINKER_LIB "/vndk-sp/"}; 63 | 64 | static void xdl_linker_init(void) { 65 | static bool inited = false; 66 | if (inited) return; 67 | inited = true; 68 | 69 | void *handle = xdl_open(XDL_UTIL_LINKER_BASENAME, XDL_DEFAULT); 70 | if (NULL == handle) return; 71 | 72 | int api_level = xdl_util_get_api_level(); 73 | if (__ANDROID_API_L__ == api_level || __ANDROID_API_L_MR1__ == api_level) { 74 | // == Android 5.x 75 | xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL); 76 | } else if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { 77 | // == Android 7.x 78 | xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_EXT_N, NULL); 79 | if (NULL == xdl_linker_dlopen) { 80 | xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DO_DLOPEN_N, NULL); 81 | xdl_linker_mutex = (pthread_mutex_t *)xdl_dsym(handle, XDL_LINKER_SYM_MUTEX, NULL); 82 | } 83 | } else if (__ANDROID_API_O__ == api_level || __ANDROID_API_O_MR1__ == api_level) { 84 | // == Android 8.x 85 | xdl_linker_dlopen = xdl_dsym(handle, XDL_LINKER_SYM_DLOPEN_O, NULL); 86 | } else if (api_level >= __ANDROID_API_P__) { 87 | // >= Android 9.0 88 | xdl_linker_dlopen = xdl_sym(handle, XDL_LINKER_SYM_LOADER_DLOPEN_P, NULL); 89 | } 90 | 91 | xdl_close(handle); 92 | } 93 | 94 | void xdl_linker_lock(void) { 95 | xdl_linker_init(); 96 | 97 | if (NULL != xdl_linker_mutex) pthread_mutex_lock(xdl_linker_mutex); 98 | } 99 | 100 | void xdl_linker_unlock(void) { 101 | if (NULL != xdl_linker_mutex) pthread_mutex_unlock(xdl_linker_mutex); 102 | } 103 | 104 | static void *xdl_linker_get_caller_addr(struct dl_phdr_info *info) { 105 | for (size_t i = 0; i < info->dlpi_phnum; i++) { 106 | const ElfW(Phdr) *phdr = &(info->dlpi_phdr[i]); 107 | if (PT_LOAD == phdr->p_type) { 108 | return (void *)(info->dlpi_addr + phdr->p_vaddr); 109 | } 110 | } 111 | return NULL; 112 | } 113 | 114 | static int xdl_linker_get_caller_addr_cb(struct dl_phdr_info *info, size_t size, void *arg) { 115 | (void)size; 116 | 117 | size_t *vendor_match = (size_t *)arg; 118 | 119 | if (0 == info->dlpi_addr || NULL == info->dlpi_name) return 0; // continue 120 | 121 | if (NULL == xdl_linker_caller_addr[0] && xdl_util_ends_with(info->dlpi_name, "/libc.so")) 122 | xdl_linker_caller_addr[0] = xdl_linker_get_caller_addr(info); 123 | 124 | if (NULL == xdl_linker_caller_addr[1] && xdl_util_ends_with(info->dlpi_name, "/libart.so")) 125 | xdl_linker_caller_addr[1] = xdl_linker_get_caller_addr(info); 126 | 127 | if (0 != *vendor_match) { 128 | for (size_t i = 0; i < *vendor_match; i++) { 129 | if (xdl_util_starts_with(info->dlpi_name, xdl_linker_vendor_path[i])) { 130 | void *caller_addr = xdl_linker_get_caller_addr(info); 131 | if (NULL != caller_addr) { 132 | xdl_linker_caller_addr[2] = caller_addr; 133 | *vendor_match = i; 134 | } 135 | } 136 | } 137 | } 138 | 139 | if (NULL != xdl_linker_caller_addr[0] && NULL != xdl_linker_caller_addr[1] && 0 == *vendor_match) { 140 | return 1; // finish 141 | } else { 142 | return 0; // continue 143 | } 144 | } 145 | 146 | static void xdl_linker_load_caller_addr(void) { 147 | if (NULL == xdl_linker_caller_addr[0]) { 148 | size_t vendor_match = sizeof(xdl_linker_vendor_path) / sizeof(xdl_linker_vendor_path[0]); 149 | xdl_iterate_phdr_impl(xdl_linker_get_caller_addr_cb, &vendor_match, XDL_DEFAULT); 150 | } 151 | } 152 | 153 | void *xdl_linker_load(const char *filename) { 154 | int api_level = xdl_util_get_api_level(); 155 | 156 | if (api_level <= __ANDROID_API_M__) { 157 | // <= Android 6.0 158 | return dlopen(filename, RTLD_NOW); 159 | } else { 160 | xdl_linker_init(); 161 | if (NULL == xdl_linker_dlopen) return NULL; 162 | xdl_linker_load_caller_addr(); 163 | 164 | void *handle = NULL; 165 | if (__ANDROID_API_N__ == api_level || __ANDROID_API_N_MR1__ == api_level) { 166 | // == Android 7.x 167 | xdl_linker_lock(); 168 | for (size_t i = 0; i < sizeof(xdl_linker_caller_addr) / sizeof(xdl_linker_caller_addr[0]); i++) { 169 | if (NULL != xdl_linker_caller_addr[i]) { 170 | handle = 171 | ((xdl_linker_dlopen_n_t)xdl_linker_dlopen)(filename, RTLD_NOW, NULL, xdl_linker_caller_addr[i]); 172 | if (NULL != handle) break; 173 | } 174 | } 175 | xdl_linker_unlock(); 176 | } else { 177 | // >= Android 8.0 178 | for (size_t i = 0; i < sizeof(xdl_linker_caller_addr) / sizeof(xdl_linker_caller_addr[0]); i++) { 179 | if (NULL != xdl_linker_caller_addr[i]) { 180 | handle = ((xdl_linker_dlopen_o_t)xdl_linker_dlopen)(filename, RTLD_NOW, xdl_linker_caller_addr[i]); 181 | if (NULL != handle) break; 182 | } 183 | } 184 | } 185 | return handle; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/xdl/xdl_linker.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2021-02-21. 23 | 24 | #ifndef IO_HEXHACKING_XDL_LINKER 25 | #define IO_HEXHACKING_XDL_LINKER 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | void xdl_linker_lock(void); 32 | void xdl_linker_unlock(void); 33 | 34 | void *xdl_linker_load(const char *filename); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/xdl/xdl_lzma.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-11-08. 23 | 24 | #include "xdl_lzma.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "include/xdl.h" 36 | #include "xdl_util.h" 37 | 38 | // LZMA library pathname & symbol names 39 | #ifndef __LP64__ 40 | #define XDL_LZMA_PATHNAME "/system/lib/liblzma.so" 41 | #else 42 | #define XDL_LZMA_PATHNAME "/system/lib64/liblzma.so" 43 | #endif 44 | #define XDL_LZMA_SYM_CRCGEN "CrcGenerateTable" 45 | #define XDL_LZMA_SYM_CRC64GEN "Crc64GenerateTable" 46 | #define XDL_LZMA_SYM_CONSTRUCT "XzUnpacker_Construct" 47 | #define XDL_LZMA_SYM_ISFINISHED "XzUnpacker_IsStreamWasFinished" 48 | #define XDL_LZMA_SYM_FREE "XzUnpacker_Free" 49 | #define XDL_LZMA_SYM_CODE "XzUnpacker_Code" 50 | 51 | // LZMA data type definition 52 | #define SZ_OK 0 53 | typedef struct ISzAlloc ISzAlloc; 54 | typedef const ISzAlloc *ISzAllocPtr; 55 | struct ISzAlloc { 56 | void *(*Alloc)(ISzAllocPtr p, size_t size); 57 | void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */ 58 | }; 59 | typedef enum { 60 | CODER_STATUS_NOT_SPECIFIED, /* use main error code instead */ 61 | CODER_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ 62 | CODER_STATUS_NOT_FINISHED, /* stream was not finished */ 63 | CODER_STATUS_NEEDS_MORE_INPUT /* you must provide more input bytes */ 64 | } ECoderStatus; 65 | typedef enum { 66 | CODER_FINISH_ANY, /* finish at any point */ 67 | CODER_FINISH_END /* block must be finished at the end */ 68 | } ECoderFinishMode; 69 | 70 | // LZMA function type definition 71 | typedef void (*xdl_lzma_crcgen_t)(void); 72 | typedef void (*xdl_lzma_crc64gen_t)(void); 73 | typedef void (*xdl_lzma_construct_t)(void *, ISzAllocPtr); 74 | typedef int (*xdl_lzma_isfinished_t)(const void *); 75 | typedef void (*xdl_lzma_free_t)(void *); 76 | typedef int (*xdl_lzma_code_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, ECoderFinishMode, 77 | ECoderStatus *); 78 | typedef int (*xdl_lzma_code_q_t)(void *, uint8_t *, size_t *, const uint8_t *, size_t *, int, 79 | ECoderFinishMode, ECoderStatus *); 80 | 81 | // LZMA function pointor 82 | static xdl_lzma_construct_t xdl_lzma_construct = NULL; 83 | static xdl_lzma_isfinished_t xdl_lzma_isfinished = NULL; 84 | static xdl_lzma_free_t xdl_lzma_free = NULL; 85 | static void *xdl_lzma_code = NULL; 86 | 87 | // LZMA init 88 | static void xdl_lzma_init() { 89 | void *lzma = xdl_open(XDL_LZMA_PATHNAME, XDL_TRY_FORCE_LOAD); 90 | if (NULL == lzma) return; 91 | 92 | xdl_lzma_crcgen_t crcgen = NULL; 93 | xdl_lzma_crc64gen_t crc64gen = NULL; 94 | if (NULL == (crcgen = (xdl_lzma_crcgen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRCGEN, NULL))) goto end; 95 | if (NULL == (crc64gen = (xdl_lzma_crc64gen_t)xdl_sym(lzma, XDL_LZMA_SYM_CRC64GEN, NULL))) goto end; 96 | if (NULL == (xdl_lzma_construct = (xdl_lzma_construct_t)xdl_sym(lzma, XDL_LZMA_SYM_CONSTRUCT, NULL))) 97 | goto end; 98 | if (NULL == (xdl_lzma_isfinished = (xdl_lzma_isfinished_t)xdl_sym(lzma, XDL_LZMA_SYM_ISFINISHED, NULL))) 99 | goto end; 100 | if (NULL == (xdl_lzma_free = (xdl_lzma_free_t)xdl_sym(lzma, XDL_LZMA_SYM_FREE, NULL))) goto end; 101 | if (NULL == (xdl_lzma_code = xdl_sym(lzma, XDL_LZMA_SYM_CODE, NULL))) goto end; 102 | crcgen(); 103 | crc64gen(); 104 | 105 | end: 106 | xdl_close(lzma); 107 | } 108 | 109 | // LZMA internal alloc / free 110 | static void *xdl_lzma_internal_alloc(ISzAllocPtr p, size_t size) { 111 | (void)p; 112 | return malloc(size); 113 | } 114 | static void xdl_lzma_internal_free(ISzAllocPtr p, void *address) { 115 | (void)p; 116 | free(address); 117 | } 118 | 119 | int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size) { 120 | size_t src_offset = 0; 121 | size_t dst_offset = 0; 122 | size_t src_remaining; 123 | size_t dst_remaining; 124 | ISzAlloc alloc = {.Alloc = xdl_lzma_internal_alloc, .Free = xdl_lzma_internal_free}; 125 | long long state[4096 / sizeof(long long)]; // must be enough, 8-bit aligned 126 | ECoderStatus status; 127 | int api_level = xdl_util_get_api_level(); 128 | 129 | // init and check 130 | static bool inited = false; 131 | if (!inited) { 132 | xdl_lzma_init(); 133 | inited = true; 134 | } 135 | if (NULL == xdl_lzma_code) return -1; 136 | 137 | xdl_lzma_construct(&state, &alloc); 138 | 139 | *dst_size = 2 * src_size; 140 | *dst = NULL; 141 | do { 142 | *dst_size *= 2; 143 | if (NULL == (*dst = realloc(*dst, *dst_size))) { 144 | xdl_lzma_free(&state); 145 | return -1; 146 | } 147 | 148 | src_remaining = src_size - src_offset; 149 | dst_remaining = *dst_size - dst_offset; 150 | 151 | int result; 152 | if (api_level >= __ANDROID_API_Q__) { 153 | xdl_lzma_code_q_t lzma_code_q = (xdl_lzma_code_q_t)xdl_lzma_code; 154 | result = lzma_code_q(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining, 1, 155 | CODER_FINISH_ANY, &status); 156 | } else { 157 | xdl_lzma_code_t lzma_code = (xdl_lzma_code_t)xdl_lzma_code; 158 | result = lzma_code(&state, *dst + dst_offset, &dst_remaining, src + src_offset, &src_remaining, 159 | CODER_FINISH_ANY, &status); 160 | } 161 | if (SZ_OK != result) { 162 | free(*dst); 163 | xdl_lzma_free(&state); 164 | return -1; 165 | } 166 | 167 | src_offset += src_remaining; 168 | dst_offset += dst_remaining; 169 | } while (status == CODER_STATUS_NOT_FINISHED); 170 | 171 | xdl_lzma_free(&state); 172 | 173 | if (!xdl_lzma_isfinished(&state)) { 174 | free(*dst); 175 | return -1; 176 | } 177 | 178 | *dst_size = dst_offset; 179 | *dst = realloc(*dst, *dst_size); 180 | return 0; 181 | } 182 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/xdl/xdl_lzma.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-11-08. 23 | 24 | #ifndef IO_HEXHACKING_XDL_LZMA 25 | #define IO_HEXHACKING_XDL_LZMA 26 | 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | int xdl_lzma_decompress(uint8_t *src, size_t src_size, uint8_t **dst, size_t *dst_size); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/xdl/xdl_util.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-04. 23 | 24 | #include "xdl_util.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | bool xdl_util_starts_with(const char *str, const char *start) { 37 | while (*str && *str == *start) { 38 | str++; 39 | start++; 40 | } 41 | 42 | return '\0' == *start; 43 | } 44 | 45 | bool xdl_util_ends_with(const char *str, const char *ending) { 46 | size_t str_len = strlen(str); 47 | size_t ending_len = strlen(ending); 48 | 49 | if (ending_len > str_len) return false; 50 | 51 | return 0 == strcmp(str + (str_len - ending_len), ending); 52 | } 53 | 54 | size_t xdl_util_trim_ending(char *start) { 55 | char *end = start + strlen(start); 56 | while (start < end && isspace((int)(*(end - 1)))) { 57 | end--; 58 | *end = '\0'; 59 | } 60 | return (size_t)(end - start); 61 | } 62 | 63 | static int xdl_util_get_api_level_from_build_prop(void) { 64 | char buf[128]; 65 | int api_level = -1; 66 | 67 | FILE *fp = fopen("/system/build.prop", "r"); 68 | if (NULL == fp) goto end; 69 | 70 | while (fgets(buf, sizeof(buf), fp)) { 71 | if (xdl_util_starts_with(buf, "ro.build.version.sdk=")) { 72 | api_level = atoi(buf + 21); 73 | break; 74 | } 75 | } 76 | fclose(fp); 77 | 78 | end: 79 | return (api_level > 0) ? api_level : -1; 80 | } 81 | 82 | int xdl_util_get_api_level(void) { 83 | static int xdl_util_api_level = -1; 84 | 85 | if (xdl_util_api_level < 0) { 86 | int api_level = android_get_device_api_level(); 87 | if (api_level < 0) 88 | api_level = xdl_util_get_api_level_from_build_prop(); // compatible with unusual models 89 | if (api_level < __ANDROID_API_J__) api_level = __ANDROID_API_J__; 90 | 91 | __atomic_store_n(&xdl_util_api_level, api_level, __ATOMIC_SEQ_CST); 92 | } 93 | 94 | return xdl_util_api_level; 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/jni/Include/xdl/xdl_util.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020-2021 HexHacking Team 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | 22 | // Created by caikelun on 2020-10-04. 23 | 24 | #ifndef IO_HEXHACKING_XDL_UTIL 25 | #define IO_HEXHACKING_XDL_UTIL 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #ifndef __LP64__ 32 | #define XDL_UTIL_LINKER_BASENAME "linker" 33 | #define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker" 34 | #define XDL_UTIL_APP_PROCESS_BASENAME "app_process32" 35 | #define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process32" 36 | #define XDL_UTIL_APP_PROCESS_BASENAME_K "app_process" 37 | #define XDL_UTIL_APP_PROCESS_PATHNAME_K "/system/bin/app_process" 38 | #else 39 | #define XDL_UTIL_LINKER_BASENAME "linker64" 40 | #define XDL_UTIL_LINKER_PATHNAME "/system/bin/linker64" 41 | #define XDL_UTIL_APP_PROCESS_BASENAME "app_process64" 42 | #define XDL_UTIL_APP_PROCESS_PATHNAME "/system/bin/app_process64" 43 | #endif 44 | #define XDL_UTIL_VDSO_BASENAME "[vdso]" 45 | 46 | #define XDL_UTIL_TEMP_FAILURE_RETRY(exp) \ 47 | ({ \ 48 | __typeof__(exp) _rc; \ 49 | do { \ 50 | errno = 0; \ 51 | _rc = (exp); \ 52 | } while (_rc == -1 && errno == EINTR); \ 53 | _rc; \ 54 | }) 55 | 56 | #ifdef __cplusplus 57 | extern "C" { 58 | #endif 59 | 60 | bool xdl_util_starts_with(const char *str, const char *start); 61 | bool xdl_util_ends_with(const char *str, const char *ending); 62 | 63 | size_t xdl_util_trim_ending(char *start); 64 | 65 | int xdl_util_get_api_level(void); 66 | 67 | #ifdef __cplusplus 68 | } 69 | #endif 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /app/src/main/jni/Loader/Loader.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by reveny on 21/08/2023. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../Include/Obfuscate.h" 12 | #include "../Include/Logger.h" 13 | #include "../Include/RemapTools.h" 14 | 15 | std::string GetNativeLibraryDirectory() { 16 | char buffer[512]; 17 | FILE *fp = fopen("/proc/self/maps", "re"); 18 | if (fp != nullptr) { 19 | while (fgets(buffer, sizeof(buffer), fp)) { 20 | if (strstr(buffer, "libLoader.so")) { 21 | RemapTools::ProcMapInfo info{}; 22 | char perms[10]; 23 | char path[255]; 24 | char dev[25]; 25 | 26 | sscanf(buffer, "%lx-%lx %s %ld %s %ld %s", &info.start, &info.end, perms, &info.offset, dev, &info.inode, path); 27 | info.path = path; 28 | 29 | //Remove libLoader.so to get path 30 | std::string pathStr = std::string(info.path); 31 | if (!pathStr.empty()) { 32 | pathStr.resize(pathStr.size() - 12); //libLoader.so = 12 chars 33 | } 34 | 35 | return pathStr; 36 | } 37 | } 38 | } 39 | return ""; 40 | } 41 | 42 | jint JNI_OnLoad(JavaVM* vm, void* reserved) { 43 | LOGI("libLoader.so loaded in %d", getpid()); 44 | std::string native = GetNativeLibraryDirectory(); 45 | if (native.empty()) { 46 | LOGE("Error getting native library directory"); 47 | exit(1); 48 | } 49 | 50 | LOGI("Found native library directory: %s", native.c_str()); 51 | std::string path = native + "libModMenu.so"; 52 | 53 | // Open the library containing the actual code 54 | void *open = dlopen(path.c_str(), RTLD_NOW); 55 | if (open == nullptr) { 56 | LOGE("Error opening libTest.so %s", dlerror()); 57 | return JNI_ERR; 58 | } 59 | 60 | // Call JNI in library 61 | void *jni_load = dlsym(open, "loadJNI"); 62 | if (jni_load == nullptr) { 63 | LOGE("Failed to find symbol loadJNI()"); 64 | return JNI_ERR; 65 | } 66 | 67 | auto loadJNI = (jint (*)(JavaVM *vm)) jni_load; 68 | jint jni = loadJNI(vm); // Call function in main library 69 | 70 | RemapTools::RemapLibrary("libModMenu.so"); 71 | 72 | return jni; 73 | } -------------------------------------------------------------------------------- /app/src/main/jni/Main/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by reveny on 21/08/2023. 3 | // 4 | 5 | #include "../Include/KittyMemory/MemoryPatch.h" 6 | #include "../Include/ImGui.h" 7 | #include "../Include/RemapTools.h" 8 | 9 | #include "../Include/Drawing.h" 10 | #include "../Include/Unity.h" 11 | 12 | void DrawMenu() { 13 | ImGui::ShowDemoWindow(); 14 | } 15 | 16 | void *thread(void *) { 17 | LOGI(OBFUSCATE("Main Thread Loaded: %d"), gettid()); 18 | initModMenu((void *)DrawMenu); 19 | 20 | //Hooks, Patches and Pointers here 21 | //Example: 22 | /* 23 | * DobbyHook(getAbsoluteAddress("libIl2cpp.so", 0x0), FunctionExample, old_FunctionExample); 24 | * SetAimRotation = (void (*)(void *, Quaternion)) getAbsoluteAddress("libIl2cpp.so", 0x0); 25 | */ 26 | 27 | LOGI("Main thread done"); 28 | pthread_exit(0); 29 | } 30 | 31 | // Call anything from JNI_OnLoad here 32 | extern "C" { 33 | // JNI Support 34 | JavaVM *jvm = nullptr; 35 | JNIEnv *env = nullptr; 36 | 37 | __attribute__((visibility ("default"))) 38 | jint loadJNI(JavaVM *vm) { 39 | jvm = vm; 40 | vm->AttachCurrentThread(&env, nullptr); 41 | LOGI("loadJNI(): Initialized"); 42 | 43 | return JNI_VERSION_1_6; 44 | } 45 | } 46 | 47 | __attribute__((constructor)) 48 | void init() { 49 | LOGI("Loaded Mod Menu"); 50 | 51 | pthread_t t; 52 | pthread_create(&t, nullptr, thread, nullptr); 53 | 54 | //Don't leave any traces, remap the loader lib as well 55 | RemapTools::RemapLibrary("libLoader.so"); 56 | } -------------------------------------------------------------------------------- /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/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-ImGui-Mod-Menu/bbec8211d85eca58477e9322e6e30c13c205e687/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF000000 4 | #FFFFFFFF 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Android-ImGui-Mod-Menu 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 |