diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
new file mode 100644
index 0000000..4eafa5e
--- /dev/null
+++ b/.github/workflows/android.yml
@@ -0,0 +1,51 @@
+name: Android CI
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: write
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: '17'
+ cache: gradle
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Build Debug APK
+ run: ./gradlew assembleDebug
+
+ - name: Collect APKs
+ run: |
+ mkdir -p apk_out
+ find app/build -type f -name "*.apk" -exec cp {} apk_out/ \;
+ ls -lh apk_out
+
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: android-apk
+ path: apk_out/*.apk
+
+ - name: Create Release
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: v-${{ github.run_number }}
+ name: Build ${{ github.run_number }}
+ files: apk_out/*.apk
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..c2701da
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+Native-LGL
\ No newline at end of file
diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml
new file mode 100644
index 0000000..4a53bee
--- /dev/null
+++ b/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..b86273d
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..b268ef3
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..97f0a8e
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/markdown.xml b/.idea/markdown.xml
new file mode 100644
index 0000000..c61ea33
--- /dev/null
+++ b/.idea/markdown.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..d15a481
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..16660f1
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 210bcdf..013cc38 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,112 @@
# NativeModMenu
+A **custom reimplementation of the LGL Android Mod Menu** that loads the Java UI **entirely from memory** using `InMemoryDexClassLoader`, without shipping a separate `.dex` file on disk.
+
+This version embeds a prebuilt `FloatingModMenu.dex` as a **HEX string** and dynamically loads it at runtime via JNI.
+
+---
+
+## ✨ Key Differences from Original LGL Mod Menu
+
+- **No external dex file**
+ - The menu dex is embedded as HEX and loaded directly from memory.
+- **In-memory class loading**
+ - Uses `dalvik.system.InMemoryDexClassLoader`
+- **JNI-driven bootstrap**
+ - Menu initialization is triggered fully from native code
+- **Cleaner injection surface**
+ - No file writes, no asset extraction
+- **Compatible with existing LGL menu Java code**
+
+---
+
+## 🧠 How It Works (High-Level)
+
+1. **Dex embedding**
+ - `FloatingModMenu.dex` is converted to a HEX string and stored in native code.
+2. **HEX → ByteBuffer**
+ - HEX string is converted back into bytes at runtime.
+3. **InMemoryDexClassLoader**
+ - Dex is loaded directly from memory.
+4. **JNI native bindings**
+ - Native functions are registered to the Java menu class.
+5. **Menu startup**
+ - Calls `FloatingModMenu.antik(Context)` to launch the floating menu.
+
+---
+
+## 📂 Important Components
+
+### Embedded Dex
+```cpp
+static std::string DI = "HEX_CODE_OF_DEX";
+```
+
+- This must be the **HEX-encoded** version of `FloatingModMenu.dex`.
+
+---
+
+### JNI Native Interface
+
+Registered native methods:
+
+| Method | Description |
+|------|------------|
+| `Icon()` | Menu icon |
+| `IconWebViewData()` | WebView icon data |
+| `getFeatureList()` | Feature list |
+| `settingsList()` | Settings list |
+| `Changes(...)` | Feature toggle handler |
+| `setTitleText(TextView)` | Title customization |
+| `setHeadingText(TextView)` | Heading customization |
+
+---
+
+### Dex Loading Logic
+
+Uses:
+```java
+InMemoryDexClassLoader(ByteBuffer[] dex, ClassLoader parent)
+```
+
+Loads:
+```java
+uk.lgl.modmenu.FloatingModMenu
+```
+
+---
+
+## 🚀 Entry Point
+
+```cpp
+void binJava();
+```
+
+- Called after `JNI_OnLoad`
+- Retrieves `Application` context via `ActivityThread`
+- Starts menu initialization
+
+---
+
+## 🔧 Requirements
+
+- Android 8.0+ (API 26+)
+- ARMv7 / ARM64
+- JNI-based injection environment
+- Prebuilt compatible `FloatingModMenu.dex`
+
+---
+
+## ⚠️ Notes
+
+- This project modifies runtime behavior of apps.
+- Usage may violate application Terms of Service.
+- Intended for educational and research purposes.
+
+---
+
+## 📜 Credits
+
+- Original concept: LGL Android Mod Menu
+- Project Creation: Aniket
+- In-memory dex loader: NepMods
+
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..c1f1dbf
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,90 @@
+import com.android.build.gradle.tasks.ExternalNativeBuildTask
+import java.io.FileInputStream
+
+plugins {
+ alias(libs.plugins.android.application)
+}
+
+class JavaDex {
+ public static String xxd_p(String filePath) {
+ StringBuilder hexString = new StringBuilder()
+ File file = new File(filePath)
+ try (FileInputStream fis = new FileInputStream(file)) {
+ byte[] data = fis.readAllBytes()
+ for (byte b : data) {
+ hexString.append(String.format("%02X", b))
+ }
+ }
+ return hexString.toString()
+ }
+}
+
+android {
+ namespace 'uk.lgl'
+ compileSdk 36
+
+ defaultConfig {
+ applicationId "uk.lgl"
+ minSdk 28
+ targetSdk 36
+ versionCode 1
+ versionName "1.0"
+
+ ndk {
+ abiFilters 'arm64-v8a'
+ }
+
+ multiDexEnabled false
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ externalNativeBuild {
+ ndkBuild {
+ path file('src/main/jni/Android.mk')
+ }
+ }
+
+ android.applicationVariants.all { variant ->
+ def variantName = variant.name.capitalize()
+ def taskName = "Dex2CppAnd8Up${variantName}"
+
+ def customTask = tasks.register(taskName) {
+ outputs.upToDateWhen { false }
+
+ def mergeDexTask = tasks.findByName("mergeDex${variantName}")
+ if (mergeDexTask) {
+ dependsOn mergeDexTask
+ }
+
+ doLast {
+ def dexDir = file("${project.buildDir}/intermediates/dex/${variant.name}/mergeDex${variantName}")
+ def dexFiles = fileTree(dir: dexDir, include: "**/classes.dex")
+
+ if (dexFiles.isEmpty()) {
+ throw new GradleException("classes.dex not found for ${variant.name} in ${dexDir}")
+ }
+
+ def dexFile = dexFiles.first()
+ String hexString = JavaDex.xxd_p(dexFile.absolutePath)
+ def headerFile = file("${project.projectDir}/src/main/jni/JavaGPP/Interface/OreoOrMore.h")
+
+ headerFile.parentFile.mkdirs()
+ headerFile.write("#define OreoOrMore \"${hexString}\"\n")
+ println ">>> Success: Generated header for ${variantName}"
+ }
+ }
+
+ tasks.configureEach { task ->
+ if (task.name.startsWith("buildNdkBuild${variantName}") ||
+ task.name.startsWith("externalNativeBuild${variantName}")) {
+ task.dependsOn customTask
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7d033f3
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/uk/lgl/MainActivity.java b/app/src/main/java/uk/lgl/MainActivity.java
new file mode 100644
index 0000000..1e8cc9d
--- /dev/null
+++ b/app/src/main/java/uk/lgl/MainActivity.java
@@ -0,0 +1,25 @@
+//
+// Created by aantik on 2/1/2026.
+//
+
+package uk.lgl;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import uk.lgl.modmenu.FloatingModMenu;
+
+
+public class MainActivity extends Activity {
+
+ static {
+ System.loadLibrary("nativelgl");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ }
+}
diff --git a/app/src/main/java/uk/lgl/modmenu/FloatingModMenu.java b/app/src/main/java/uk/lgl/modmenu/FloatingModMenu.java
new file mode 100644
index 0000000..58b0b1f
--- /dev/null
+++ b/app/src/main/java/uk/lgl/modmenu/FloatingModMenu.java
@@ -0,0 +1,583 @@
+//
+// Created by aantik on 2/1/2026.
+//
+
+package uk.lgl.modmenu;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Typeface;
+import android.graphics.drawable.GradientDrawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.Html;
+import android.text.InputFilter;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.text.method.DigitsKeyListener;
+import android.util.Base64;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.webkit.WebView;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.RelativeLayout;
+import android.widget.ScrollView;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.widget.RelativeLayout.ALIGN_PARENT_LEFT;
+import static android.widget.RelativeLayout.ALIGN_PARENT_RIGHT;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+
+public class FloatingModMenu {
+
+
+ public static final String TAG = "Mod_Menu"; //Tag for logcat
+ int TEXT_COLOR = Color.parseColor("#82CAFD");
+ int TEXT_COLOR_2 = Color.parseColor("#FFFFFF");
+ int BTN_COLOR = Color.parseColor("#1C262D");
+ int MENU_BG_COLOR = Color.parseColor("#EE1C2A35"); //#AARRGGBB
+ int MENU_FEATURE_BG_COLOR = Color.parseColor("#DD141C22"); //#AARRGGBB
+ int MENU_WIDTH = 290;
+ int MENU_HEIGHT = 210;
+ float MENU_CORNER = 4f;
+ int ICON_SIZE = 45; //Change both width and height of image
+ float ICON_ALPHA = 0.7f; //Transparent
+ int ToggleON = Color.GREEN;
+ int ToggleOFF = Color.RED;
+ int BtnON = Color.parseColor("#1b5e20");
+ int BtnOFF = Color.parseColor("#7f0000");
+ int CategoryBG = Color.parseColor("#2F3D4C");
+ int SeekBarColor = Color.parseColor("#80CBC4");
+ int SeekBarProgressColor = Color.parseColor("#80CBC4");
+ int CheckBoxColor = Color.parseColor("#80CBC4");
+ int RadioColor = Color.parseColor("#FFFFFF");
+ String NumberTxtColor = "#41c300";
+ RelativeLayout mCollapsed, mRootContainer;
+ LinearLayout mExpanded, patches, mSettings, mCollapse;
+ LinearLayout.LayoutParams scrlLLExpanded, scrlLL;
+ WindowManager mWindowManager;
+ WindowManager.LayoutParams params;
+ ImageView startimage;
+ FrameLayout rootFrame;
+ ScrollView scrollView;
+ private Activity activity;
+ boolean stopChecking;
+
+ //initialize methods from the native library
+ native void setTitleText(TextView textView);
+
+ native void setHeadingText(TextView textView);
+
+ native String Icon();
+
+ native String IconWebViewData();
+
+ native String[] getFeatureList();
+
+ native String[] settingsList();
+
+ public static native void Changes(Context con, int fNum, String fName, int i, boolean bool, String str);
+
+
+ public FloatingModMenu(Activity activity) {
+ this.activity = activity;
+ initFloating();
+ }
+
+
+ private static FloatingModMenu instance;
+
+ public static void CreateMenu(final Context context) {
+ if (instance != null) return;
+ if (!(context instanceof Activity)) {
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ Activity activity = getTopActivity();
+ if (activity != null) {
+ instance = new FloatingModMenu(activity);
+ }
+ }
+ });
+ } else {
+ instance = new FloatingModMenu((Activity) context);
+ }
+ }
+
+ private static Activity getTopActivity() {
+ try {
+ Class> at = Class.forName("android.app.ActivityThread");
+ Method current = at.getDeclaredMethod("currentActivityThread");
+ current.setAccessible(true);
+ Object atObj = current.invoke(null);
+
+ Field activities = at.getDeclaredField("mActivities");
+ activities.setAccessible(true);
+ Map, ?> map = (Map, ?>) activities.get(atObj);
+
+ for (Object record : map.values()) {
+ Class> ar = record.getClass();
+ Field paused = ar.getDeclaredField("paused");
+ paused.setAccessible(true);
+ if (!paused.getBoolean(record)) {
+ Field activity = ar.getDeclaredField("activity");
+ activity.setAccessible(true);
+ return (Activity) activity.get(record);
+ }
+ }
+ } catch (Throwable ignored) {}
+ return null;
+ }
+
+
+
+ @SuppressLint({"WrongConstant", "ClickableViewAccessibility", "ObsoleteSdkInt", "SetTextI18n"})
+ private void initFloating() {
+ rootFrame = new FrameLayout(activity);
+ rootFrame.setOnTouchListener(onTouchListener());
+ mRootContainer = new RelativeLayout(activity);
+ mCollapsed = new RelativeLayout(activity);
+ mCollapsed.setVisibility(View.VISIBLE);
+ mCollapsed.setAlpha(ICON_ALPHA);
+
+ //********** The box of the mod menu **********
+ mExpanded = new LinearLayout(activity);
+ mExpanded.setVisibility(View.GONE);
+ mExpanded.setBackgroundColor(MENU_BG_COLOR);
+ mExpanded.setOrientation(LinearLayout.VERTICAL);
+ mExpanded.setLayoutParams(new LinearLayout.LayoutParams(dp(MENU_WIDTH), WRAP_CONTENT));
+ GradientDrawable gdMenuBody = new GradientDrawable();
+ gdMenuBody.setCornerRadius(MENU_CORNER);
+ gdMenuBody.setColor(MENU_BG_COLOR);
+ gdMenuBody.setStroke(1, Color.parseColor("#32cb00"));
+
+
+ startimage = new ImageView(activity);
+ startimage.setLayoutParams(new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ int applyDimension = (int) TypedValue.applyDimension(1, ICON_SIZE, activity.getResources().getDisplayMetrics()); //Icon size
+ startimage.getLayoutParams().height = applyDimension;
+ startimage.getLayoutParams().width = applyDimension;
+ //startimage.requestLayout();
+ startimage.setScaleType(ImageView.ScaleType.FIT_XY);
+ byte[] decode = Base64.decode(Icon(), 0);
+ startimage.setImageBitmap(BitmapFactory.decodeByteArray(decode, 0, decode.length));
+ ((ViewGroup.MarginLayoutParams) startimage.getLayoutParams()).topMargin = convertDipToPixels(10);
+ //Initialize event handlers for buttons, etc.
+ startimage.setOnTouchListener(onTouchListener());
+ startimage.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ mCollapsed.setVisibility(View.GONE);
+ mExpanded.setVisibility(View.VISIBLE);
+ }
+ });
+
+ //********** The icon in Webview to open mod menu **********
+ WebView wView = new WebView(activity); //Icon size width=\"50\" height=\"50\"
+ wView.setLayoutParams(new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ int applyDimension2 = (int) TypedValue.applyDimension(1, ICON_SIZE, activity.getResources().getDisplayMetrics()); //Icon size
+ wView.getLayoutParams().height = applyDimension2;
+ wView.getLayoutParams().width = applyDimension2;
+ wView.loadData("" +
+ "
" +
+ "" +
+ "
" +
+ "" +
+ "", "text/html", "utf-8");
+ wView.setBackgroundColor(0x00000000); //Transparent
+ wView.setAlpha(ICON_ALPHA);
+ wView.setOnTouchListener(onTouchListener());
+
+ TextView settings = new TextView(activity);
+ settings.setText(Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M ? "⚙" : "\uD83D\uDD27");
+ settings.setTextColor(TEXT_COLOR);
+ settings.setTypeface(Typeface.DEFAULT_BOLD);
+ settings.setTextSize(20.0f);
+ RelativeLayout.LayoutParams rlsettings = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ rlsettings.addRule(ALIGN_PARENT_RIGHT);
+ settings.setLayoutParams(rlsettings);
+ settings.setOnClickListener(new View.OnClickListener() {
+ boolean settingsOpen;
+
+ @Override
+ public void onClick(View v) throws IllegalStateException {
+ settingsOpen = !settingsOpen;
+ if (settingsOpen) {
+ scrollView.removeView(patches);
+ scrollView.addView(mSettings);
+ scrollView.scrollTo(0, 0);
+ } else {
+ scrollView.removeView(mSettings);
+ scrollView.addView(patches);
+ }
+ }
+ });
+
+ //********** Settings **********
+ mSettings = new LinearLayout(activity);
+ mSettings.setOrientation(LinearLayout.VERTICAL);
+ featureList(settingsList(), mSettings);
+
+ //********** Title text **********
+ RelativeLayout titleText = new RelativeLayout(activity);
+ titleText.setPadding(10, 5, 10, 5);
+ titleText.setVerticalGravity(16);
+ TextView title = new TextView(activity);
+ title.setTextColor(TEXT_COLOR);
+ title.setTextSize(18.0f);
+ title.setGravity(Gravity.CENTER);
+ RelativeLayout.LayoutParams rl = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ rl.addRule(RelativeLayout.CENTER_HORIZONTAL);
+ title.setLayoutParams(rl);
+ setTitleText(title);
+
+ //********** Heading text **********
+ TextView heading = new TextView(activity);
+ heading.setEllipsize(TextUtils.TruncateAt.MARQUEE);
+ heading.setMarqueeRepeatLimit(-1);
+ heading.setSingleLine(true);
+ heading.setSelected(true);
+ heading.setTextColor(TEXT_COLOR);
+ heading.setTextSize(10.0f);
+ heading.setGravity(Gravity.CENTER);
+ heading.setPadding(0, 0, 0, 5);
+ setHeadingText(heading);
+
+
+ scrollView = new ScrollView(activity);
+ //Auto size. To set size manually, change the width and height example 500, 500
+ scrlLL = new LinearLayout.LayoutParams(MATCH_PARENT, dp(MENU_HEIGHT));
+ scrlLLExpanded = new LinearLayout.LayoutParams(mExpanded.getLayoutParams());
+ scrlLLExpanded.weight = 1.0f;
+ scrollView.setLayoutParams(scrlLL);
+ scrollView.setBackgroundColor(MENU_FEATURE_BG_COLOR);
+ patches = new LinearLayout(activity);
+ patches.setOrientation(LinearLayout.VERTICAL);
+
+ //********** RelativeLayout for buttons **********
+ RelativeLayout relativeLayout = new RelativeLayout(activity);
+ relativeLayout.setPadding(10, 3, 10, 3);
+ relativeLayout.setVerticalGravity(Gravity.CENTER);
+
+ //********** Hide/Kill button **********
+ RelativeLayout.LayoutParams lParamsHideBtn = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ lParamsHideBtn.addRule(ALIGN_PARENT_LEFT);
+
+ Button hideBtn = new Button(activity);
+ hideBtn.setLayoutParams(lParamsHideBtn);
+ hideBtn.setBackgroundColor(Color.TRANSPARENT);
+ hideBtn.setText("HIDE/KILL (Hold)");
+ hideBtn.setTextColor(TEXT_COLOR);
+ hideBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ mCollapsed.setVisibility(View.VISIBLE);
+ mCollapsed.setAlpha(0);
+ mExpanded.setVisibility(View.GONE);
+ Toast.makeText(view.getContext(), "Icon hidden. Remember the hidden icon position", Toast.LENGTH_LONG).show();
+ }
+ });
+ hideBtn.setOnLongClickListener(new View.OnLongClickListener() {
+ public boolean onLongClick(View view) {
+ Toast.makeText(view.getContext(), "Menu service killed", Toast.LENGTH_LONG).show();
+ mWindowManager.removeView(rootFrame);
+ return false;
+ }
+ });
+
+ //********** Close button **********
+ RelativeLayout.LayoutParams lParamsCloseBtn = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
+ lParamsCloseBtn.addRule(ALIGN_PARENT_RIGHT);
+
+ Button closeBtn = new Button(activity);
+ closeBtn.setLayoutParams(lParamsCloseBtn);
+ closeBtn.setBackgroundColor(Color.TRANSPARENT);
+ closeBtn.setText("MINIMIZE");
+ closeBtn.setTextColor(TEXT_COLOR);
+ closeBtn.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ mCollapsed.setVisibility(View.VISIBLE);
+ mCollapsed.setAlpha(ICON_ALPHA);
+ mExpanded.setVisibility(View.GONE);
+ }
+ });
+
+ //********** Params **********
+ //Variable to check later if the phone supports Draw over other apps permission
+
+ params = new WindowManager.LayoutParams(
+ WRAP_CONTENT,
+ WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_APPLICATION,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSPARENT
+ );
+ params.gravity = Gravity.TOP | Gravity.LEFT;
+ params.x = 0;
+ params.y = 100;
+
+
+ //********** Adding view components **********
+ rootFrame.addView(mRootContainer);
+ mRootContainer.addView(mCollapsed);
+ mRootContainer.addView(mExpanded);
+ if (IconWebViewData() != null) {
+ mCollapsed.addView(wView);
+ } else {
+ mCollapsed.addView(startimage);
+ }
+ titleText.addView(title);
+ titleText.addView(settings);
+ mExpanded.addView(titleText);
+ mExpanded.addView(heading);
+ scrollView.addView(patches);
+ mExpanded.addView(scrollView);
+ relativeLayout.addView(hideBtn);
+ relativeLayout.addView(closeBtn);
+ mExpanded.addView(relativeLayout);
+ mWindowManager = activity.getWindowManager();
+ mWindowManager.addView(rootFrame, params);
+ patches.removeAllViews();
+ featureList(getFeatureList(), patches);
+ }
+
+ private View.OnTouchListener onTouchListener() {
+ return new View.OnTouchListener() {
+ final View collapsedView = mCollapsed;
+ final View expandedView = mExpanded;
+ private float initialTouchX, initialTouchY;
+ private int initialX, initialY;
+
+ @SuppressLint("ClickableViewAccessibility")
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ switch (motionEvent.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ initialX = params.x;
+ initialY = params.y;
+ initialTouchX = motionEvent.getRawX();
+ initialTouchY = motionEvent.getRawY();
+ return true;
+ case MotionEvent.ACTION_UP:
+ int rawX = (int) (motionEvent.getRawX() - initialTouchX);
+ int rawY = (int) (motionEvent.getRawY() - initialTouchY);
+ mExpanded.setAlpha(1f);
+ mCollapsed.setAlpha(1f);
+ if (rawX < 10 && rawY < 10 && isViewCollapsed()) {
+ try {
+ collapsedView.setVisibility(View.GONE);
+ expandedView.setVisibility(View.VISIBLE);
+ } catch (NullPointerException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ mExpanded.setAlpha(0.5f);
+ mCollapsed.setAlpha(0.5f);
+ //Calculate the X and Y coordinates of the view.
+ params.x = initialX + ((int) (motionEvent.getRawX() - initialTouchX));
+ params.y = initialY + ((int) (motionEvent.getRawY() - initialTouchY));
+ //Update the layout with new X & Y coordinate
+ mWindowManager.updateViewLayout(rootFrame, params);
+ return true;
+ default:
+ return false;
+ }
+ }
+ };
+ }
+
+
+ private void featureList(String[] listFT, LinearLayout linearLayout) {
+ //Currently looks messy right now. Let me know if you have improvements
+ int featNum, subFeat = 0;
+ LinearLayout llBak = linearLayout;
+
+ for (int i = 0; i < listFT.length; i++) {
+ boolean switchedOn = false;
+ //Log.i("featureList", listFT[i]);
+ String feature = listFT[i];
+ if (feature.contains("True_")) {
+ switchedOn = true;
+ feature = feature.replaceFirst("True_", "");
+ }
+
+ linearLayout = llBak;
+ if (feature.contains("CollapseAdd_")) {
+ //if (collapse != null)
+ linearLayout = mCollapse;
+ feature = feature.replaceFirst("CollapseAdd_", "");
+ }
+ String[] str = feature.split("_");
+
+ //Assign feature number
+ if (TextUtils.isDigitsOnly(str[0]) || str[0].matches("-[0-9]*")) {
+ featNum = Integer.parseInt(str[0]);
+ feature = feature.replaceFirst(str[0] + "_", "");
+ subFeat++;
+ } else {
+ //Subtract feature number. We don't want to count ButtonLink, Category, RichTextView and RichWebView
+ featNum = i - subFeat;
+ }
+ String[] strSplit = feature.split("_");
+ switch (strSplit[0]) {
+ case "Toggle":
+ linearLayout.addView(Switch(featNum, strSplit[1], switchedOn));
+ break;
+ case "Category":
+ subFeat++;
+ linearLayout.addView(Category(strSplit[1]));
+ break;
+ case "RichTextView":
+ subFeat++;
+ linearLayout.addView(RichTextView(strSplit[1]));
+ break;
+ case "RichWebView":
+ subFeat++;
+ linearLayout.addView(RichWebView(strSplit[1]));
+ break;
+ }
+ }
+ }
+
+ private View Switch(final int featNum, final String featName, boolean swiOn) {
+ final Switch switchR = new Switch(activity);
+ ColorStateList buttonStates = new ColorStateList(
+ new int[][]{
+ new int[]{-android.R.attr.state_enabled},
+ new int[]{android.R.attr.state_checked},
+ new int[]{}
+ },
+ new int[]{
+ Color.BLUE,
+ ToggleON, // ON
+ ToggleOFF // OFF
+ }
+ );
+ //Set colors of the switch. Comment out if you don't like it
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ switchR.getThumbDrawable().setTintList(buttonStates);
+ switchR.getTrackDrawable().setTintList(buttonStates);
+ }
+ switchR.setText(featName);
+ switchR.setTextColor(TEXT_COLOR_2);
+ switchR.setPadding(10, 5, 0, 5);
+ switchR.setChecked(swiOn);
+ switchR.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ public void onCheckedChanged(CompoundButton compoundButton, boolean bool) {
+ switch (featNum) {
+ case -1: //Save perferences
+ if (bool == false)
+ break;
+ case -3:
+ scrollView.setLayoutParams(bool ? scrlLLExpanded : scrlLL);
+ break;
+ }
+ }
+ });
+ return switchR;
+ }
+
+ private View Category(String text) {
+ TextView textView = new TextView(activity);
+ textView.setBackgroundColor(CategoryBG);
+ textView.setText(Html.fromHtml(text));
+ textView.setGravity(Gravity.CENTER);
+ textView.setTextColor(TEXT_COLOR_2);
+ textView.setTypeface(null, Typeface.BOLD);
+ textView.setPadding(0, 5, 0, 5);
+ return textView;
+ }
+
+ private View RichTextView(String text) {
+ TextView textView = new TextView(activity);
+ textView.setText(Html.fromHtml(text));
+ textView.setTextColor(TEXT_COLOR_2);
+ textView.setPadding(10, 5, 10, 5);
+ return textView;
+ }
+
+ private View RichWebView(String text) {
+ WebView wView = new WebView(activity);
+ wView.loadData(text, "text/html", "utf-8");
+ wView.setBackgroundColor(0x00000000); //Transparent
+ wView.setPadding(0, 5, 0, 5);
+ return wView;
+ }
+
+
+
+
+ private boolean isViewCollapsed() {
+ return rootFrame == null || mCollapsed.getVisibility() == View.VISIBLE;
+ }
+
+ private int convertDipToPixels(int i) {
+ return (int) ((((float) i) * activity.getResources().getDisplayMetrics().density) + 0.5f);
+ }
+
+ @SuppressLint("WrongConstant")
+ private int dp(int i) {
+ return (int) TypedValue.applyDimension(1, (float) i, activity.getResources().getDisplayMetrics());
+ }
+ private boolean isNotInGame() {
+ RunningAppProcessInfo runningAppProcessInfo = new RunningAppProcessInfo();
+ ActivityManager.getMyMemoryState(runningAppProcessInfo);
+ return runningAppProcessInfo.importance != 100;
+ }
+
+ private void Thread() {
+ if (rootFrame == null) {
+ return;
+ }
+ if (isNotInGame()) {
+ rootFrame.setVisibility(View.INVISIBLE);
+ } else {
+ rootFrame.setVisibility(View.VISIBLE);
+ }
+ }
+}
diff --git a/app/src/main/jni/And64InlineHook/And64InlineHook.cpp b/app/src/main/jni/And64InlineHook/And64InlineHook.cpp
new file mode 100644
index 0000000..72702dd
--- /dev/null
+++ b/app/src/main/jni/And64InlineHook/And64InlineHook.cpp
@@ -0,0 +1,614 @@
+/*
+ * @date : 2018/04/18
+ * @author : Rprop (r_prop@outlook.com)
+ * https://github.com/Rprop/And64InlineHook
+ */
+/*
+ MIT License
+
+ Copyright (c) 2018 Rprop (r_prop@outlook.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+#define __STDC_FORMAT_MACROS
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(__aarch64__)
+
+#include "And64InlineHook.hpp"
+
+#define A64_MAX_INSTRUCTIONS 5
+#define A64_MAX_REFERENCES (A64_MAX_INSTRUCTIONS * 2)
+#define A64_NOP 0xd503201fu
+#define A64_JNIEXPORT __attribute__((visibility("hidden")))
+#define A64_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "A64_HOOK", __VA_ARGS__))
+#ifndef NDEBUG
+# define A64_LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "A64_HOOK", __VA_ARGS__))
+#else
+# define A64_LOGI(...) ((void)0)
+#endif // NDEBUG
+typedef uint32_t *__restrict *__restrict instruction;
+typedef struct {
+ struct fix_info {
+ uint32_t *bp;
+ uint32_t ls; // left-shift counts
+ uint32_t ad; // & operand
+ };
+ struct insns_info {
+ union {
+ uint64_t insu;
+ int64_t ins;
+ void *insp;
+ };
+ fix_info fmap[A64_MAX_REFERENCES];
+ };
+ int64_t basep;
+ int64_t endp;
+ insns_info dat[A64_MAX_INSTRUCTIONS];
+
+public:
+ inline bool is_in_fixing_range(const int64_t absolute_addr) {
+ return absolute_addr >= this->basep && absolute_addr < this->endp;
+ }
+
+ inline intptr_t get_ref_ins_index(const int64_t absolute_addr) {
+ return static_cast((absolute_addr - this->basep) / sizeof(uint32_t));
+ }
+
+ inline intptr_t get_and_set_current_index(uint32_t *__restrict inp, uint32_t *__restrict outp) {
+ intptr_t current_idx = this->get_ref_ins_index(reinterpret_cast(inp));
+ this->dat[current_idx].insp = outp;
+ return current_idx;
+ }
+
+ inline void reset_current_ins(const intptr_t idx, uint32_t *__restrict outp) {
+ this->dat[idx].insp = outp;
+ }
+
+ void
+ insert_fix_map(const intptr_t idx, uint32_t *bp, uint32_t ls = 0u, uint32_t ad = 0xffffffffu) {
+ for (auto &f : this->dat[idx].fmap) {
+ if (f.bp == NULL) {
+ f.bp = bp;
+ f.ls = ls;
+ f.ad = ad;
+ return;
+ } //if
+ }
+ // What? GGing..
+ }
+
+ void process_fix_map(const intptr_t idx) {
+ for (auto &f : this->dat[idx].fmap) {
+ if (f.bp == NULL) break;
+ *(f.bp) = *(f.bp) |
+ (((int32_t(this->dat[idx].ins - reinterpret_cast(f.bp)) >> 2)
+ << f.ls) & f.ad);
+ f.bp = NULL;
+ }
+ }
+} context;
+
+//-------------------------------------------------------------------------
+
+static bool __fix_branch_imm(instruction inpp, instruction outpp, context *ctxp) {
+ static constexpr uint32_t mbits = 6u;
+ static constexpr uint32_t mask = 0xfc000000u; // 0b11111100000000000000000000000000
+ static constexpr uint32_t rmask = 0x03ffffffu; // 0b00000011111111111111111111111111
+ static constexpr uint32_t op_b = 0x14000000u; // "b" ADDR_PCREL26
+ static constexpr uint32_t op_bl = 0x94000000u; // "bl" ADDR_PCREL26
+
+ const uint32_t ins = *(*inpp);
+ const uint32_t opc = ins & mask;
+ switch (opc) {
+ case op_b:
+ case op_bl: {
+ intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp);
+ int64_t absolute_addr = reinterpret_cast(*inpp) +
+ (static_cast(ins << mbits)
+ >> (mbits - 2u)); // sign-extended
+ int64_t new_pc_offset =
+ static_cast(absolute_addr - reinterpret_cast(*outpp))
+ >> 2; // shifted
+ bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr);
+ // whether the branch should be converted to absolute jump
+ if (!special_fix_type && llabs(new_pc_offset) >= (rmask >> 1)) {
+ bool b_aligned = (reinterpret_cast(*outpp + 2) & 7u) == 0u;
+ if (opc == op_b) {
+ if (b_aligned != true) {
+ (*outpp)[0] = A64_NOP;
+ ctxp->reset_current_ins(current_idx, ++(*outpp));
+ } //if
+ (*outpp)[0] = 0x58000051u; // LDR X17, #0x8
+ (*outpp)[1] = 0xd61f0220u; // BR X17
+ memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr));
+ *outpp += 4;
+ } else {
+ if (b_aligned == true) {
+ (*outpp)[0] = A64_NOP;
+ ctxp->reset_current_ins(current_idx, ++(*outpp));
+ } //if
+ (*outpp)[0] = 0x58000071u; // LDR X17, #12
+ (*outpp)[1] = 0x1000009eu; // ADR X30, #16
+ (*outpp)[2] = 0xd61f0220u; // BR X17
+ memcpy(*outpp + 3, &absolute_addr, sizeof(absolute_addr));
+ *outpp += 5;
+ } //if
+ } else {
+ if (special_fix_type) {
+ intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr);
+ if (ref_idx <= current_idx) {
+ new_pc_offset = static_cast(ctxp->dat[ref_idx].ins -
+ reinterpret_cast(*outpp))
+ >> 2;
+ } else {
+ ctxp->insert_fix_map(ref_idx, *outpp, 0u, rmask);
+ new_pc_offset = 0;
+ } //if
+ } //if
+
+ (*outpp)[0] = opc | (new_pc_offset & ~mask);
+ ++(*outpp);
+ } //if
+
+ ++(*inpp);
+ return ctxp->process_fix_map(current_idx), true;
+ }
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------
+
+static bool __fix_cond_comp_test_branch(instruction inpp, instruction outpp, context *ctxp) {
+ static constexpr uint32_t lsb = 5u;
+ static constexpr uint32_t lmask01 = 0xff00001fu; // 0b11111111000000000000000000011111
+ static constexpr uint32_t mask0 = 0xff000010u; // 0b11111111000000000000000000010000
+ static constexpr uint32_t op_bc = 0x54000000u; // "b.c" ADDR_PCREL19
+ static constexpr uint32_t mask1 = 0x7f000000u; // 0b01111111000000000000000000000000
+ static constexpr uint32_t op_cbz = 0x34000000u; // "cbz" Rt, ADDR_PCREL19
+ static constexpr uint32_t op_cbnz = 0x35000000u; // "cbnz" Rt, ADDR_PCREL19
+ static constexpr uint32_t lmask2 = 0xfff8001fu; // 0b11111111111110000000000000011111
+ static constexpr uint32_t mask2 = 0x7f000000u; // 0b01111111000000000000000000000000
+ static constexpr uint32_t op_tbz = 0x36000000u; // 0b00110110000000000000000000000000 "tbz" Rt, BIT_NUM, ADDR_PCREL14
+ static constexpr uint32_t op_tbnz = 0x37000000u; // 0b00110111000000000000000000000000 "tbnz" Rt, BIT_NUM, ADDR_PCREL14
+
+ const uint32_t ins = *(*inpp);
+ uint32_t lmask = lmask01;
+ if ((ins & mask0) != op_bc) {
+ uint32_t opc = ins & mask1;
+ if (opc != op_cbz && opc != op_cbnz) {
+ opc = ins & mask2;
+ if (opc != op_tbz && opc != op_tbnz) {
+ return false;
+ } //if
+ lmask = lmask2;
+ } //if
+ } //if
+
+ intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp);
+ int64_t absolute_addr = reinterpret_cast(*inpp) + ((ins & ~lmask) >> (lsb - 2u));
+ int64_t new_pc_offset =
+ static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; // shifted
+ bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr);
+ if (!special_fix_type && llabs(new_pc_offset) >= (~lmask >> (lsb + 1))) {
+ if ((reinterpret_cast(*outpp + 4) & 7u) != 0u) {
+ (*outpp)[0] = A64_NOP;
+ ctxp->reset_current_ins(current_idx, ++(*outpp));
+ } //if
+ (*outpp)[0] = (((8u >> 2u) << lsb) & ~lmask) | (ins & lmask); // B.C #0x8
+ (*outpp)[1] = 0x14000005u; // B #0x14
+ (*outpp)[2] = 0x58000051u; // LDR X17, #0x8
+ (*outpp)[3] = 0xd61f0220u; // BR X17
+ memcpy(*outpp + 4, &absolute_addr, sizeof(absolute_addr));
+ *outpp += 6;
+ } else {
+ if (special_fix_type) {
+ intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr);
+ if (ref_idx <= current_idx) {
+ new_pc_offset = static_cast(ctxp->dat[ref_idx].ins -
+ reinterpret_cast(*outpp)) >> 2;
+ } else {
+ ctxp->insert_fix_map(ref_idx, *outpp, lsb, ~lmask);
+ new_pc_offset = 0;
+ } //if
+ } //if
+
+ (*outpp)[0] = (static_cast(new_pc_offset << lsb) & ~lmask) | (ins & lmask);
+ ++(*outpp);
+ } //if
+
+ ++(*inpp);
+ return ctxp->process_fix_map(current_idx), true;
+}
+
+//-------------------------------------------------------------------------
+
+static bool __fix_loadlit(instruction inpp, instruction outpp, context *ctxp) {
+ const uint32_t ins = *(*inpp);
+
+ // memory prefetch("prfm"), just skip it
+ // http://infocenter.arm.com/help/topic/com.arm.doc.100069_0608_00_en/pge1427897420050.html
+ if ((ins & 0xff000000u) == 0xd8000000u) {
+ ctxp->process_fix_map(ctxp->get_and_set_current_index(*inpp, *outpp));
+ ++(*inpp);
+ return true;
+ } //if
+
+ static constexpr uint32_t msb = 8u;
+ static constexpr uint32_t lsb = 5u;
+ static constexpr uint32_t mask_30 = 0x40000000u; // 0b01000000000000000000000000000000
+ static constexpr uint32_t mask_31 = 0x80000000u; // 0b10000000000000000000000000000000
+ static constexpr uint32_t lmask = 0xff00001fu; // 0b11111111000000000000000000011111
+ static constexpr uint32_t mask_ldr = 0xbf000000u; // 0b10111111000000000000000000000000
+ static constexpr uint32_t op_ldr = 0x18000000u; // 0b00011000000000000000000000000000 "LDR Wt/Xt, label" | ADDR_PCREL19
+ static constexpr uint32_t mask_ldrv = 0x3f000000u; // 0b00111111000000000000000000000000
+ static constexpr uint32_t op_ldrv = 0x1c000000u; // 0b00011100000000000000000000000000 "LDR St/Dt/Qt, label" | ADDR_PCREL19
+ static constexpr uint32_t mask_ldrsw = 0xff000000u; // 0b11111111000000000000000000000000
+ static constexpr uint32_t op_ldrsw = 0x98000000u; // "LDRSW Xt, label" | ADDR_PCREL19 | load register signed word
+ // LDR S0, #0 | 0b00011100000000000000000000000000 | 32-bit
+ // LDR D0, #0 | 0b01011100000000000000000000000000 | 64-bit
+ // LDR Q0, #0 | 0b10011100000000000000000000000000 | 128-bit
+ // INVALID | 0b11011100000000000000000000000000 | may be 256-bit
+
+ uint32_t mask = mask_ldr;
+ uintptr_t faligned = (ins & mask_30) ? 7u : 3u;
+ if ((ins & mask_ldr) != op_ldr) {
+ mask = mask_ldrv;
+ if (faligned != 7u)
+ faligned = (ins & mask_31) ? 15u : 3u;
+ if ((ins & mask_ldrv) != op_ldrv) {
+ if ((ins & mask_ldrsw) != op_ldrsw) {
+ return false;
+ } //if
+ mask = mask_ldrsw;
+ faligned = 7u;
+ } //if
+ } //if
+
+ intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp);
+ int64_t absolute_addr = reinterpret_cast(*inpp) +
+ ((static_cast(ins << msb) >> (msb + lsb - 2u)) & ~3u);
+ int64_t new_pc_offset =
+ static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; // shifted
+ bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr);
+ // special_fix_type may encounter issue when there are mixed data and code
+ if (special_fix_type || (llabs(new_pc_offset) + (faligned + 1u - 4u) / 4u) >=
+ (~lmask >> (lsb + 1))) { // inaccurate, but it works
+ while ((reinterpret_cast(*outpp + 2) & faligned) != 0u) {
+ *(*outpp)++ = A64_NOP;
+ }
+ ctxp->reset_current_ins(current_idx, *outpp);
+
+ // Note that if memory at absolute_addr is writeable (non-const), we will fail to fetch it.
+ // And what's worse, we may unexpectedly overwrite something if special_fix_type is true...
+ uint32_t ns = static_cast((faligned + 1) / sizeof(uint32_t));
+ (*outpp)[0] = (((8u >> 2u) << lsb) & ~mask) | (ins & lmask); // LDR #0x8
+ (*outpp)[1] = 0x14000001u + ns; // B #0xc
+ memcpy(*outpp + 2, reinterpret_cast(absolute_addr), faligned + 1);
+ *outpp += 2 + ns;
+ } else {
+ faligned >>= 2; // new_pc_offset is shifted and 4-byte aligned
+ while ((new_pc_offset & faligned) != 0) {
+ *(*outpp)++ = A64_NOP;
+ new_pc_offset =
+ static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2;
+ }
+ ctxp->reset_current_ins(current_idx, *outpp);
+
+ (*outpp)[0] = (static_cast(new_pc_offset << lsb) & ~mask) | (ins & lmask);
+ ++(*outpp);
+ } //if
+
+ ++(*inpp);
+ return ctxp->process_fix_map(current_idx), true;
+}
+
+//-------------------------------------------------------------------------
+
+static bool __fix_pcreladdr(instruction inpp, instruction outpp, context *ctxp) {
+ // Load a PC-relative address into a register
+ // http://infocenter.arm.com/help/topic/com.arm.doc.100069_0608_00_en/pge1427897645644.html
+ static constexpr uint32_t msb = 8u;
+ static constexpr uint32_t lsb = 5u;
+ static constexpr uint32_t mask = 0x9f000000u; // 0b10011111000000000000000000000000
+ static constexpr uint32_t rmask = 0x0000001fu; // 0b00000000000000000000000000011111
+ static constexpr uint32_t lmask = 0xff00001fu; // 0b11111111000000000000000000011111
+ static constexpr uint32_t fmask = 0x00ffffffu; // 0b00000000111111111111111111111111
+ static constexpr uint32_t max_val = 0x001fffffu; // 0b00000000000111111111111111111111
+ static constexpr uint32_t op_adr = 0x10000000u; // "adr" Rd, ADDR_PCREL21
+ static constexpr uint32_t op_adrp = 0x90000000u; // "adrp" Rd, ADDR_ADRP
+
+ const uint32_t ins = *(*inpp);
+ intptr_t current_idx;
+ switch (ins & mask) {
+ case op_adr: {
+ current_idx = ctxp->get_and_set_current_index(*inpp, *outpp);
+ int64_t lsb_bytes = static_cast(ins << 1u) >> 30u;
+ int64_t absolute_addr = reinterpret_cast(*inpp) +
+ (((static_cast(ins << msb) >> (msb + lsb - 2u)) &
+ ~3u) | lsb_bytes);
+ int64_t new_pc_offset = static_cast(absolute_addr -
+ reinterpret_cast(*outpp));
+ bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr);
+ if (!special_fix_type && llabs(new_pc_offset) >= (max_val >> 1)) {
+ if ((reinterpret_cast(*outpp + 2) & 7u) != 0u) {
+ (*outpp)[0] = A64_NOP;
+ ctxp->reset_current_ins(current_idx, ++(*outpp));
+ } //if
+
+ (*outpp)[0] =
+ 0x58000000u | (((8u >> 2u) << lsb) & ~mask) | (ins & rmask); // LDR #0x8
+ (*outpp)[1] = 0x14000003u; // B #0xc
+ memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr));
+ *outpp += 4;
+ } else {
+ if (special_fix_type) {
+ intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr & ~3ull);
+ if (ref_idx <= current_idx) {
+ new_pc_offset = static_cast(ctxp->dat[ref_idx].ins -
+ reinterpret_cast(*outpp));
+ } else {
+ ctxp->insert_fix_map(ref_idx, *outpp, lsb, fmask);
+ new_pc_offset = 0;
+ } //if
+ } //if
+
+ // the lsb_bytes will never be changed, so we can use lmask to keep it
+ (*outpp)[0] = (static_cast(new_pc_offset << (lsb - 2u)) & fmask) |
+ (ins & lmask);
+ ++(*outpp);
+ } //if
+ }
+ break;
+ case op_adrp: {
+ current_idx = ctxp->get_and_set_current_index(*inpp, *outpp);
+ int32_t lsb_bytes = static_cast(ins << 1u) >> 30u;
+ int64_t absolute_addr = (reinterpret_cast(*inpp) & ~0xfffll) +
+ ((((static_cast(ins << msb) >> (msb + lsb - 2u)) &
+ ~3u) | lsb_bytes) << 12);
+ A64_LOGI("ins = 0x%.8X, pc = %p, abs_addr = %p",
+ ins, *inpp, reinterpret_cast(absolute_addr));
+ if (ctxp->is_in_fixing_range(absolute_addr)) {
+ intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr/* & ~3ull*/);
+ if (ref_idx > current_idx) {
+ // the bottom 12 bits of absolute_addr are masked out,
+ // so ref_idx must be less than or equal to current_idx!
+ A64_LOGE("ref_idx must be less than or equal to current_idx!");
+ } //if
+
+ // *absolute_addr may be changed due to relocation fixing
+ A64_LOGI("What is the correct way to fix this?");
+ *(*outpp)++ = ins; // 0x90000000u;
+ } else {
+ if ((reinterpret_cast(*outpp + 2) & 7u) != 0u) {
+ (*outpp)[0] = A64_NOP;
+ ctxp->reset_current_ins(current_idx, ++(*outpp));
+ } //if
+
+ (*outpp)[0] =
+ 0x58000000u | (((8u >> 2u) << lsb) & ~mask) | (ins & rmask); // LDR #0x8
+ (*outpp)[1] = 0x14000003u; // B #0xc
+ memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); // potential overflow?
+ *outpp += 4;
+ } //if
+ }
+ break;
+ default:
+ return false;
+ }
+
+ ctxp->process_fix_map(current_idx);
+ ++(*inpp);
+ return true;
+}
+
+//-------------------------------------------------------------------------
+#define __flush_cache(c, n) __builtin___clear_cache(reinterpret_cast(c), reinterpret_cast(c) + n)
+
+static void __fix_instructions(uint32_t *__restrict inp, int32_t count, uint32_t *__restrict outp) {
+ context ctx;
+ ctx.basep = reinterpret_cast(inp);
+ ctx.endp = reinterpret_cast(inp + count);
+ memset(ctx.dat, 0, sizeof(ctx.dat));
+ static_assert(sizeof(ctx.dat) / sizeof(ctx.dat[0]) == A64_MAX_INSTRUCTIONS,
+ "please use A64_MAX_INSTRUCTIONS!");
+#ifndef NDEBUG
+ if (count > A64_MAX_INSTRUCTIONS) {
+ A64_LOGE("too many fixing instructions!");
+ } //if
+#endif // NDEBUG
+
+ uint32_t *const outp_base = outp;
+
+ while (--count >= 0) {
+ if (__fix_branch_imm(&inp, &outp, &ctx)) continue;
+ if (__fix_cond_comp_test_branch(&inp, &outp, &ctx)) continue;
+ if (__fix_loadlit(&inp, &outp, &ctx)) continue;
+ if (__fix_pcreladdr(&inp, &outp, &ctx)) continue;
+
+ // without PC-relative offset
+ ctx.process_fix_map(ctx.get_and_set_current_index(inp, outp));
+ *(outp++) = *(inp++);
+ }
+
+ static constexpr uint_fast64_t mask = 0x03ffffffu; // 0b00000011111111111111111111111111
+ auto callback = reinterpret_cast(inp);
+ auto pc_offset = static_cast(callback - reinterpret_cast(outp)) >> 2;
+ if (llabs(pc_offset) >= (mask >> 1)) {
+ if ((reinterpret_cast(outp + 2) & 7u) != 0u) {
+ outp[0] = A64_NOP;
+ ++outp;
+ } //if
+ outp[0] = 0x58000051u; // LDR X17, #0x8
+ outp[1] = 0xd61f0220u; // BR X17
+ *reinterpret_cast(outp + 2) = callback;
+ outp += 4;
+ } else {
+ outp[0] = 0x14000000u | (pc_offset & mask); // "B" ADDR_PCREL26
+ ++outp;
+ } //if
+
+ const uintptr_t total = (outp - outp_base) * sizeof(uint32_t);
+ __flush_cache(outp_base, total); // necessary
+}
+
+//-------------------------------------------------------------------------
+
+extern "C" {
+#define __attribute __attribute__
+#define aligned(x) __aligned__(x)
+#define __intval(p) reinterpret_cast(p)
+#define __uintval(p) reinterpret_cast(p)
+#define __ptr(p) reinterpret_cast(p)
+#define __page_size 4096
+#define __page_align(n) __align_up(static_cast(n), __page_size)
+#define __ptr_align(x) __ptr(__align_down(reinterpret_cast(x), __page_size))
+#define __align_up(x, n) (((x) + ((n) - 1)) & ~((n) - 1))
+#define __align_down(x, n) ((x) & -(n))
+#define __countof(x) static_cast(sizeof(x) / sizeof((x)[0])) // must be signed
+#define __atomic_increase(p) __sync_add_and_fetch(p, 1)
+#define __sync_cmpswap(p, v, n) __sync_bool_compare_and_swap(p, v, n)
+#define __predict_true(exp) __builtin_expect((exp) != 0, 1)
+#define __make_rwx(p, n) ::mprotect(__ptr_align(p), \
+ __page_align(__uintval(p) + n) != __page_align(__uintval(p)) ? __page_align(n) + __page_size : __page_align(n), \
+ PROT_READ | PROT_WRITE | PROT_EXEC)
+
+//-------------------------------------------------------------------------
+
+static __attribute((aligned(__page_size))) uint32_t __insns_pool[A64_MAX_BACKUPS][
+ A64_MAX_INSTRUCTIONS * 10];
+
+//-------------------------------------------------------------------------
+
+class A64HookInit {
+public:
+ A64HookInit() {
+ __make_rwx(__insns_pool, sizeof(__insns_pool));
+ A64_LOGI("insns pool initialized.");
+ }
+};
+static A64HookInit __init;
+
+//-------------------------------------------------------------------------
+
+static uint32_t *FastAllocateTrampoline() {
+ static_assert((A64_MAX_INSTRUCTIONS * 10 * sizeof(uint32_t)) % 8 == 0, "8-byte align");
+ static volatile int32_t __index = -1;
+
+ int32_t i = __atomic_increase(&__index);
+ if (__predict_true(i >= 0 && i < __countof(__insns_pool))) {
+ return __insns_pool[i];
+ } //if
+
+ A64_LOGE("failed to allocate trampoline!");
+ return NULL;
+}
+
+//-------------------------------------------------------------------------
+
+A64_JNIEXPORT void *A64HookFunctionV(void *const symbol, void *const replace,
+ void *const rwx, const uintptr_t rwx_size) {
+ static constexpr uint_fast64_t mask = 0x03ffffffu; // 0b00000011111111111111111111111111
+
+ uint32_t *trampoline = static_cast(rwx), *original = static_cast(symbol);
+
+ static_assert(A64_MAX_INSTRUCTIONS >= 5, "please fix A64_MAX_INSTRUCTIONS!");
+ auto pc_offset = static_cast(__intval(replace) - __intval(symbol)) >> 2;
+ if (llabs(pc_offset) >= (mask >> 1)) {
+ int32_t count = (reinterpret_cast(original + 2) & 7u) != 0u ? 5 : 4;
+ if (trampoline) {
+ if (rwx_size < count * 10u) {
+ A64_LOGI("rwx size is too small to hold %u bytes backup instructions!",
+ count * 10u);
+ return NULL;
+ } //if
+ __fix_instructions(original, count, trampoline);
+ } //if
+
+ if (__make_rwx(original, 5 * sizeof(uint32_t)) == 0) {
+ if (count == 5) {
+ original[0] = A64_NOP;
+ ++original;
+ } //if
+ original[0] = 0x58000051u; // LDR X17, #0x8
+ original[1] = 0xd61f0220u; // BR X17
+ *reinterpret_cast(original + 2) = __intval(replace);
+ __flush_cache(symbol, 5 * sizeof(uint32_t));
+
+ A64_LOGI("inline hook %p->%p successfully! %zu bytes overwritten",
+ symbol, replace, 5 * sizeof(uint32_t));
+ } else {
+ A64_LOGE("mprotect failed with errno = %d, p = %p, size = %zu",
+ errno, original, 5 * sizeof(uint32_t));
+ trampoline = NULL;
+ } //if
+ } else {
+ if (trampoline) {
+ if (rwx_size < 1u * 10u) {
+ A64_LOGI("rwx size is too small to hold %u bytes backup instructions!", 1u * 10u);
+ return NULL;
+ } //if
+ __fix_instructions(original, 1, trampoline);
+ } //if
+
+ if (__make_rwx(original, 1 * sizeof(uint32_t)) == 0) {
+ __sync_cmpswap(original, *original,
+ 0x14000000u | (pc_offset & mask)); // "B" ADDR_PCREL26
+ __flush_cache(symbol, 1 * sizeof(uint32_t));
+
+ A64_LOGI("inline hook %p->%p successfully! %zu bytes overwritten",
+ symbol, replace, 1 * sizeof(uint32_t));
+ } else {
+ A64_LOGE("mprotect failed with errno = %d, p = %p, size = %zu",
+ errno, original, 1 * sizeof(uint32_t));
+ trampoline = NULL;
+ } //if
+ } //if
+
+ return trampoline;
+}
+
+//-------------------------------------------------------------------------
+
+A64_JNIEXPORT void A64HookFunction(void *const symbol, void *const replace, void **result) {
+ void *trampoline = NULL;
+ if (result != NULL) {
+ trampoline = FastAllocateTrampoline();
+ *result = trampoline;
+ if (trampoline == NULL) return;
+ } //if
+
+ //fix Android 10 .text segment is read-only by default
+ __make_rwx(symbol, 5 * sizeof(size_t));
+
+ trampoline = A64HookFunctionV(symbol, replace, trampoline, A64_MAX_INSTRUCTIONS * 10u);
+ if (trampoline == NULL && result != NULL) {
+ *result = NULL;
+ } //if
+}
+}
+
+#endif // defined(__aarch64__)
\ No newline at end of file
diff --git a/app/src/main/jni/And64InlineHook/And64InlineHook.hpp b/app/src/main/jni/And64InlineHook/And64InlineHook.hpp
new file mode 100644
index 0000000..d274848
--- /dev/null
+++ b/app/src/main/jni/And64InlineHook/And64InlineHook.hpp
@@ -0,0 +1,41 @@
+/*
+ * @date : 2018/04/18
+ * @author : Rprop (r_prop@outlook.com)
+ * https://github.com/Rprop/And64InlineHook
+ */
+/*
+ MIT License
+
+ Copyright (c) 2018 Rprop (r_prop@outlook.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ */
+#pragma once
+#define A64_MAX_BACKUPS 256
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void A64HookFunction(void *const symbol, void *const replace, void **result);
+void *A64HookFunctionV(void *const symbol, void *const replace, void *const rwx, const uintptr_t rwx_size);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/app/src/main/jni/And64InlineHook/LICENSE b/app/src/main/jni/And64InlineHook/LICENSE
new file mode 100644
index 0000000..75a6020
--- /dev/null
+++ b/app/src/main/jni/And64InlineHook/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 RLib
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/app/src/main/jni/And64InlineHook/README.md b/app/src/main/jni/And64InlineHook/README.md
new file mode 100644
index 0000000..44f651d
--- /dev/null
+++ b/app/src/main/jni/And64InlineHook/README.md
@@ -0,0 +1,7 @@
+# And64InlineHook
+Lightweight ARMv8-A(ARM64, AArch64, Little-Endian) Inline Hook Library for Android C/C++
+
+# References
+[Arm Compiler armasm User Guide](http://infocenter.arm.com/help/topic/com.arm.doc.100069_0610_00_en/pge1427898258836.html)
+[Procedure Call Standard for the Arm® 64-bit Architecture (AArch64)](https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst)
+
diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk
new file mode 100644
index 0000000..e0404db
--- /dev/null
+++ b/app/src/main/jni/Android.mk
@@ -0,0 +1,31 @@
+LOCAL_PATH := $(call my-dir)
+MAIN_LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := nativelgl
+
+LOCAL_CFLAGS := -Wno-error=format-security -fvisibility=hidden -ffunction-sections -fdata-sections -w
+LOCAL_CFLAGS += -fno-rtti -fno-exceptions -fpermissive
+LOCAL_CPPFLAGS := -Wno-error=format-security -fvisibility=hidden -ffunction-sections -fdata-sections -w -Werror -s -std=c++17
+LOCAL_CPPFLAGS += -Wno-error=c++11-narrowing -fms-extensions -fno-rtti -fno-exceptions -fpermissive
+LOCAL_LDFLAGS += -Wl,--gc-sections,--strip-all, -llog
+LOCAL_ARM_MODE := arm
+
+LOCAL_C_INCLUDES += $(MAIN_LOCAL_PATH)
+
+LOCAL_SRC_FILES := Main.cpp \
+ JavaGPP/Interface/Interface.cpp \
+ Substrate/hde64.c \
+ Substrate/SubstrateDebug.cpp \
+ Substrate/SubstrateHook.cpp \
+ Substrate/SubstratePosixMemory.cpp \
+ Substrate/SymbolFinder.cpp \
+ KittyMemory/KittyMemory.cpp \
+ KittyMemory/MemoryPatch.cpp \
+ KittyMemory/MemoryBackup.cpp \
+ KittyMemory/KittyUtils.cpp \
+ And64InlineHook/And64InlineHook.cpp \
+
+LOCAL_LDLIBS := -llog -landroid -lGLESv2
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk
new file mode 100644
index 0000000..7c4c051
--- /dev/null
+++ b/app/src/main/jni/Application.mk
@@ -0,0 +1,5 @@
+APP_ABI := arm64-v8a
+APP_STL := c++_static
+APP_OPTIM := release
+APP_THIN_ARCHIVE := true
+APP_PIE := true
diff --git a/app/src/main/jni/Includes/Logger.h b/app/src/main/jni/Includes/Logger.h
new file mode 100644
index 0000000..72021bf
--- /dev/null
+++ b/app/src/main/jni/Includes/Logger.h
@@ -0,0 +1,21 @@
+#ifndef DAWN_LOGGER_H
+#define DAWN_LOGGER_H
+
+#include
+
+enum daLogType {
+ daDEBUG = 3,
+ daERROR = 6,
+ daINFO = 4,
+ daWARN = 5
+};
+
+//Change this to another Log Tag if ya want. IN the batch script I provide you change the log tag then too
+#define TAG OBFUSCATE("Mod_Menu")
+
+#define LOGD(...) ((void)__android_log_print(daDEBUG, TAG, __VA_ARGS__))
+#define LOGE(...) ((void)__android_log_print(daERROR, TAG, __VA_ARGS__))
+#define LOGI(...) ((void)__android_log_print(daINFO, TAG, __VA_ARGS__))
+#define LOGW(...) ((void)__android_log_print(daWARN, TAG, __VA_ARGS__))
+
+#endif //DAWN_LOGGER_H
\ No newline at end of file
diff --git a/app/src/main/jni/Includes/Macros.h b/app/src/main/jni/Includes/Macros.h
new file mode 100644
index 0000000..852a0f0
--- /dev/null
+++ b/app/src/main/jni/Includes/Macros.h
@@ -0,0 +1,71 @@
+#ifndef ANDROID_MOD_MENU_MACROS_H
+#define ANDROID_MOD_MENU_MACROS_H
+
+// thanks to shmoo and joeyjurjens for the usefull stuff under this comment.
+
+#if defined(__aarch64__) //Compile for arm64 lib only
+#include
+
+#define HOOK(offset, ptr, orig) A64HookFunction((void *)getAbsoluteAddress(targetLibName, string2Offset(OBFUSCATE(offset))), (void *)ptr, (void **)&orig)
+#define HOOK_LIB(lib, offset, ptr, orig) A64HookFunction((void *)getAbsoluteAddress(OBFUSCATE(lib), string2Offset(OBFUSCATE(offset))), (void *)ptr, (void **)&orig)
+
+#define HOOK_NO_ORIG(offset, ptr) A64HookFunction((void *)getAbsoluteAddress(targetLibName, string2Offset(OBFUSCATE(offset))), (void *)ptr, NULL)
+#define HOOK_LIB_NO_ORIG(lib, offset, ptr) A64HookFunction((void *)getAbsoluteAddress(OBFUSCATE(lib), string2Offset(OBFUSCATE(offset))), (void *)ptr, NULL)
+
+#define HOOKSYM(sym, ptr, org) A64HookFunction(dlsym(dlopen(targetLibName, 4), OBFUSCATE(sym)), (void *)ptr, (void **)&org)
+#define HOOKSYM_LIB(lib, sym, ptr, org) A64HookFunction(dlsym(dlopen(OBFUSCATE(lib), 4), OBFUSCATE(sym)), (void *)ptr, (void **)&org)
+
+#define HOOKSYM_NO_ORIG(sym, ptr) A64HookFunction(dlsym(dlopen(targetLibName, 4), OBFUSCATE(sym)), (void *)ptr, NULL)
+#define HOOKSYM_LIB_NO_ORIG(lib, sym, ptr) A64HookFunction(dlsym(dlopen(OBFUSCATE(lib), 4), OBFUSCATE(sym)), (void *)ptr, NULL)
+
+#else //Compile for armv7 lib only. Do not worry about greyed out highlighting code, it still works
+
+#include
+#include
+
+#define HOOK(offset, ptr, orig) MSHookFunction((void *)getAbsoluteAddress(targetLibName, string2Offset(OBFUSCATE(offset))), (void *)ptr, (void **)&orig)
+#define HOOK_LIB(lib, offset, ptr, orig) MSHookFunction((void *)getAbsoluteAddress(OBFUSCATE(lib), string2Offset(OBFUSCATE(offset))), (void *)ptr, (void **)&orig)
+
+#define HOOK_NO_ORIG(offset, ptr) MSHookFunction((void *)getAbsoluteAddress(targetLibName, string2Offset(OBFUSCATE(offset))), (void *)ptr, NULL)
+#define HOOK_LIB_NO_ORIG(lib, offset, ptr) MSHookFunction((void *)getAbsoluteAddress(OBFUSCATE(lib), string2Offset(OBFUSCATE(offset))), (void *)ptr, NULL)
+
+#define HOOKSYM(sym, ptr, org) MSHookFunction(dlsym(dlopen(targetLibName, 4), OBFUSCATE(sym)), (void *)ptr, (void **)&org)
+#define HOOKSYM_LIB(lib, sym, ptr, org) MSHookFunction(dlsym(dlopen(OBFUSCATE(lib), 4), OBFUSCATE(sym)), (void *)ptr, (void **)&org)
+
+#define HOOKSYM_NO_ORIG(sym, ptr) MSHookFunction(dlsym(dlopen(targetLibName, 4), OBFUSCATE(sym)), (void *)ptr, NULL)
+#define HOOKSYM_LIB_NO_ORIG(lib, sym, ptr) MSHookFunction(dlsym(dlopen(OBFUSCATE(lib), 4), OBFUSCATE(sym)), (void *)ptr, NULL)
+
+#endif
+
+// Obfuscate offset
+#define OBFUSCATEOFFSET(str) string2Offset(OBFUSCATE(str)) // Encrypt offset
+
+// Patching a offset without switch.
+void patchOffset(const char *fileName, uint64_t offset, std::string hexBytes) {
+ MemoryPatch patch = MemoryPatch::createWithHex(fileName, offset, hexBytes);
+ if(!patch.isValid()){
+ LOGE(OBFUSCATE("Failing offset: 0x%llu, please re-check the hex you entered."), offset);
+ return;
+ }
+ if(!patch.Modify()) {
+ LOGE(OBFUSCATE("Something went wrong while patching this offset: 0x%llu"), offset);
+ return;
+ }
+}
+
+void patchOffset(uint64_t offset, std::string hexBytes) {
+ MemoryPatch patch = MemoryPatch::createWithHex(targetLibName, offset, hexBytes);
+ if(!patch.isValid()){
+ LOGE(OBFUSCATE("Failing offset: 0x%llu, please re-check the hex you entered."), offset);
+ return;
+ }
+ if(!patch.Modify()) {
+ LOGE(OBFUSCATE("Something went wrong while patching this offset: 0x%llu"), offset);
+ return;
+ }
+}
+
+#define PATCHOFFSET(offset, hex) patchOffset(string2Offset(OBFUSCATE(offset)), OBFUSCATE(hex))
+#define PATCHOFFSET_LIB(lib, offset, hex) patchOffset(OBFUSCATE(lib), string2Offset(OBFUSCATE(offset)), OBFUSCATE(hex))
+
+#endif //ANDROID_MOD_MENU_MACROS_H
diff --git a/app/src/main/jni/Includes/Utils.h b/app/src/main/jni/Includes/Utils.h
new file mode 100644
index 0000000..b254c3a
--- /dev/null
+++ b/app/src/main/jni/Includes/Utils.h
@@ -0,0 +1,102 @@
+#ifndef UTILS
+#define UTILS
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "Logger.h"
+
+typedef unsigned long DWORD;
+static uintptr_t libBase;
+
+bool isGameLibLoaded = false;
+
+DWORD findLibrary(const char *library) {
+ char filename[0xFF] = {0},
+ buffer[1024] = {0};
+ FILE *fp = NULL;
+ DWORD address = 0;
+
+ sprintf(filename, OBFUSCATE("/proc/self/maps"));
+
+ fp = fopen(filename, OBFUSCATE("rt"));
+ if (fp == NULL) {
+ perror(OBFUSCATE("fopen"));
+ goto done;
+ }
+
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ if (strstr(buffer, library)) {
+ address = (DWORD) strtoul(buffer, NULL, 16);
+ goto done;
+ }
+ }
+
+ done:
+
+ if (fp) {
+ fclose(fp);
+ }
+
+ return address;
+}
+
+DWORD getAbsoluteAddress(const char *libraryName, DWORD relativeAddr) {
+ libBase = findLibrary(libraryName);
+ if (libBase == 0)
+ return 0;
+ return (reinterpret_cast(libBase + relativeAddr));
+}
+
+extern "C" {
+JNIEXPORT jboolean JNICALL
+Java_uk_lgl_modmenu_FloatingModMenuService_isGameLibLoaded(JNIEnv *env, jobject thiz) {
+ return isGameLibLoaded;
+}
+}
+
+bool isLibraryLoaded(const char *libraryName) {
+ //isGameLibLoaded = true;
+ char line[512] = {0};
+ FILE *fp = fopen(OBFUSCATE("/proc/self/maps"), OBFUSCATE("rt"));
+ if (fp != NULL) {
+ while (fgets(line, sizeof(line), fp)) {
+ std::string a = line;
+ if (strstr(line, libraryName)) {
+ isGameLibLoaded = true;
+ return true;
+ }
+ }
+ fclose(fp);
+ }
+ return false;
+}
+
+uintptr_t string2Offset(const char *c) {
+ int base = 16;
+ // See if this function catches all possibilities.
+ // If it doesn't, the function would have to be amended
+ // whenever you add a combination of architecture and
+ // compiler that is not yet addressed.
+ static_assert(sizeof(uintptr_t) == sizeof(unsigned long)
+ || sizeof(uintptr_t) == sizeof(unsigned long long),
+ "Please add string to handle conversion for this architecture.");
+
+ // Now choose the correct function ...
+ if (sizeof(uintptr_t) == sizeof(unsigned long)) {
+ return strtoul(c, nullptr, base);
+ }
+
+ // All other options exhausted, sizeof(uintptr_t) == sizeof(unsigned long long))
+ return strtoull(c, nullptr, base);
+}
+
+namespace Toast {
+ inline const int LENGTH_LONG = 1;
+ inline const int LENGTH_SHORT = 0;
+}
+
+#endif
\ No newline at end of file
diff --git a/app/src/main/jni/Includes/obfuscate.h b/app/src/main/jni/Includes/obfuscate.h
new file mode 100644
index 0000000..cec60ae
--- /dev/null
+++ b/app/src/main/jni/Includes/obfuscate.h
@@ -0,0 +1,224 @@
+/* --------------------------------- ABOUT -------------------------------------
+Original Author: Adam Yaxley
+Website: https://github.com/adamyaxley
+License: See end of file
+Obfuscate
+Guaranteed compile-time string literal obfuscation library for C++14
+Usage:
+Pass string literals into the AY_OBFUSCATE macro to obfuscate them at compile
+time. AY_OBFUSCATE returns a reference to an ay::obfuscated_data object with the
+following traits:
+ - Guaranteed obfuscation of string
+ The passed string is encrypted with a simple XOR cipher at compile-time to
+ prevent it being viewable in the binary image
+ - Global lifetime
+ The actual instantiation of the ay::obfuscated_data takes place inside a
+ lambda as a function level static
+ - Implicitly convertable to a char*
+ This means that you can pass it directly into functions that would normally
+ take a char* or a const char*
+Example:
+const char* obfuscated_string = AY_OBFUSCATE("Hello World");
+std::cout << obfuscated_string << std::endl;
+----------------------------------------------------------------------------- */
+#include
+#include
+
+#ifndef AY_OBFUSCATE_DEFAULT_KEY
+// The default 64 bit key to obfuscate strings with.
+// This can be user specified by defining AY_OBFUSCATE_DEFAULT_KEY before
+// including obfuscate.h
+#define AY_OBFUSCATE_DEFAULT_KEY ay::generate_key(__LINE__)
+#endif
+
+namespace ay
+{
+ using size_type = unsigned long long;
+ using key_type = unsigned long long;
+
+ // Generate a psuedo-random key that spans all 8 bytes
+ constexpr key_type generate_key(key_type seed)
+ {
+ // Use the MurmurHash3 64-bit finalizer to hash our seed
+ key_type key = seed;
+ key ^= (key >> 33);
+ key *= 0xff51afd7ed558ccd;
+ key ^= (key >> 33);
+ key *= 0xc4ceb9fe1a85ec53;
+ key ^= (key >> 33);
+
+ // Make sure that a bit in each byte is set
+ key |= 0x0101010101010101ull;
+
+ return key;
+ }
+
+ // Obfuscates or deobfuscates data with key
+ constexpr void cipher(char* data, size_type size, key_type key)
+ {
+ // Obfuscate with a simple XOR cipher based on key
+ for (size_type i = 0; i < size; i++)
+ {
+ data[i] ^= char(key >> ((i % 8) * 8));
+ }
+ }
+
+ // Obfuscates a string at compile time
+ template
+ class obfuscator
+ {
+ public:
+ // Obfuscates the string 'data' on construction
+ constexpr obfuscator(const char* data)
+ {
+ // Copy data
+ for (size_type i = 0; i < N; i++)
+ {
+ m_data[i] = data[i];
+ }
+
+ // On construction each of the characters in the string is
+ // obfuscated with an XOR cipher based on key
+ cipher(m_data, N, KEY);
+ }
+
+ constexpr const char* data() const
+ {
+ return &m_data[0];
+ }
+
+ constexpr size_type size() const
+ {
+ return N;
+ }
+
+ constexpr key_type key() const
+ {
+ return KEY;
+ }
+
+ private:
+
+ char m_data[N]{};
+ };
+
+ // Handles decryption and re-encryption of an encrypted string at runtime
+ template
+ class obfuscated_data
+ {
+ public:
+ obfuscated_data(const obfuscator& obfuscator)
+ {
+ // Copy obfuscated data
+ for (size_type i = 0; i < N; i++)
+ {
+ m_data[i] = obfuscator.data()[i];
+ }
+ }
+
+ ~obfuscated_data()
+ {
+ // Zero m_data to remove it from memory
+ for (size_type i = 0; i < N; i++)
+ {
+ m_data[i] = 0;
+ }
+ }
+
+ // Returns a pointer to the plain text string, decrypting it if
+ // necessary
+ operator char*()
+ {
+ decrypt();
+ return m_data;
+ }
+
+ operator std::string()
+ {
+ decrypt();
+ return m_data;
+ }
+
+ // Manually decrypt the string
+ void decrypt()
+ {
+ if (m_encrypted)
+ {
+ cipher(m_data, N, KEY);
+ m_encrypted = false;
+ }
+ }
+
+ // Manually re-encrypt the string
+ void encrypt()
+ {
+ if (!m_encrypted)
+ {
+ cipher(m_data, N, KEY);
+ m_encrypted = true;
+ }
+ }
+
+ // Returns true if this string is currently encrypted, false otherwise.
+ bool is_encrypted() const
+ {
+ return m_encrypted;
+ }
+
+ private:
+
+ // Local storage for the string. Call is_encrypted() to check whether or
+ // not the string is currently obfuscated.
+ char m_data[N];
+
+ // Whether data is currently encrypted
+ bool m_encrypted{ true };
+ };
+
+ // This function exists purely to extract the number of elements 'N' in the
+ // array 'data'
+ template
+ constexpr auto make_obfuscator(const char(&data)[N])
+ {
+ return obfuscator(data);
+ }
+}
+
+// Obfuscates the string 'data' at compile-time and returns a reference to a
+// ay::obfuscated_data object with global lifetime that has functions for
+// decrypting the string and is also implicitly convertable to a char*
+#define OBFUSCATE(data) OBFUSCATE_KEY(data, AY_OBFUSCATE_DEFAULT_KEY)
+
+// Obfuscates the string 'data' with 'key' at compile-time and returns a
+// reference to a ay::obfuscated_data object with global lifetime that has
+// functions for decrypting the string and is also implicitly convertable to a
+// char*
+#define OBFUSCATE_KEY(data, key) \
+ []() -> ay::obfuscated_data& { \
+ static_assert(sizeof(decltype(key)) == sizeof(ay::key_type), "key must be a 64 bit unsigned integer"); \
+ static_assert((key) >= (1ull << 56), "key must span all 8 bytes"); \
+ constexpr auto n = sizeof(data)/sizeof(data[0]); \
+ constexpr auto obfuscator = ay::make_obfuscator(data); \
+ static auto obfuscated_data = ay::obfuscated_data(obfuscator); \
+ return obfuscated_data; \
+ }()
+
+/* -------------------------------- LICENSE ------------------------------------
+Public Domain (http://www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+----------------------------------------------------------------------------- */
\ No newline at end of file
diff --git a/app/src/main/jni/JavaGPP/Interface/Interface.cpp b/app/src/main/jni/JavaGPP/Interface/Interface.cpp
new file mode 100644
index 0000000..70cd77d
--- /dev/null
+++ b/app/src/main/jni/JavaGPP/Interface/Interface.cpp
@@ -0,0 +1,148 @@
+//
+// Created by aantik on 2/1/2026.
+//
+// Interface.cpp
+
+#include "Interface.h"
+#include "OreoOrMore.h"
+#include
+
+JavaVM *antik = nullptr;
+JNIEnv *antikYt = nullptr;
+
+namespace InterfaceMethods {
+ void *Icon = nullptr;
+ void *IconWebViewData = nullptr;
+ void *getFeatureList = nullptr;
+ void *settingsList = nullptr;
+ void *Changes = nullptr;
+ void *setTitleText = nullptr;
+ void *setHeadingText = nullptr;
+};
+jclass menuClass;
+/*
+ * 🔐 Your prebuilt FloatingModMenu.dex HEX
+ */
+
+/*
+ * HEX → ByteBuffer[]
+ */
+static jobjectArray HEX(JNIEnv* env, const std::string& hex) {
+ int len = hex.length() / 2;
+
+ jbyteArray arr = env->NewByteArray(len);
+ jbyte* buf = env->GetByteArrayElements(arr, nullptr);
+
+ for (int i = 0; i < len; i++) {
+ char h = hex[i * 2];
+ char l = hex[i * 2 + 1];
+
+ int hi = isdigit(h) ? h - '0' : tolower(h) - 'a' + 10;
+ int lo = isdigit(l) ? l - '0' : tolower(l) - 'a' + 10;
+
+ buf[i] = (jbyte)((hi << 4) | lo);
+ }
+
+ env->ReleaseByteArrayElements(arr, buf, 0);
+
+ jclass bbCls = env->FindClass("java/nio/ByteBuffer");
+ jmethodID wrap = env->GetStaticMethodID(
+ bbCls,
+ "wrap",
+ "([B)Ljava/nio/ByteBuffer;"
+ );
+
+ jobject bb = env->CallStaticObjectMethod(bbCls, wrap, arr);
+ return env->NewObjectArray(1, bbCls, bb);
+}
+
+/*
+ * Register JNI methods
+ */
+static void RegisterMethods(JNIEnv *env) {
+ JNINativeMethod NativeMethodsClassMethods[] = {
+ {"Icon", "()Ljava/lang/String;", InterfaceMethods::Icon},
+ {"IconWebViewData", "()Ljava/lang/String;", InterfaceMethods::IconWebViewData},
+ {"getFeatureList", "()[Ljava/lang/String;", InterfaceMethods::getFeatureList},
+ {"settingsList", "()[Ljava/lang/String;", InterfaceMethods::settingsList},
+ {"Changes", "(Landroid/content/Context;ILjava/lang/String;IZLjava/lang/String;)V", InterfaceMethods::Changes},
+ {"setTitleText", "(Landroid/widget/TextView;)V", InterfaceMethods::setTitleText},
+ {"setHeadingText", "(Landroid/widget/TextView;)V", InterfaceMethods::setHeadingText},
+ };
+ env->RegisterNatives(menuClass, NativeMethodsClassMethods,
+ sizeof(NativeMethodsClassMethods) /
+ sizeof(NativeMethodsClassMethods[0]));
+}
+
+/*
+ * 🧠 Load dex + call FloatingModMenu.antik(Context)
+ */
+static void loadDex(JNIEnv* env, jobject context) {
+
+ jobjectArray buffers = HEX(env, OreoOrMore);
+ if (!buffers) return;
+
+ // ClassLoader::getSystemClassLoader()
+ jclass classLoaderClass = env->FindClass("java/lang/ClassLoader");
+ jmethodID getSystemClassLoader = env->GetStaticMethodID(
+ classLoaderClass,
+ "getSystemClassLoader",
+ "()Ljava/lang/ClassLoader;"
+ );
+ jobject parentCl = env->CallStaticObjectMethod(classLoaderClass, getSystemClassLoader);
+
+ // InMemoryDexClassLoader
+ jclass imCls = env->FindClass("dalvik/system/InMemoryDexClassLoader");
+ jmethodID ctor = env->GetMethodID(
+ imCls,
+ "",
+ "([Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V"
+ );
+ jobject dexLoader = env->NewObject(imCls, ctor, buffers, parentCl);
+
+ // loadClass
+ jmethodID loadClass = env->GetMethodID(
+ imCls,
+ "loadClass",
+ "(Ljava/lang/String;)Ljava/lang/Class;"
+ );
+
+ jstring clsName = env->NewStringUTF("uk.lgl.modmenu.FloatingModMenu");
+ menuClass = (jclass) env->CallObjectMethod(dexLoader, loadClass, clsName);
+
+ if (!menuClass) return;
+ menuClass = (jclass) env->NewGlobalRef(menuClass);
+ RegisterMethods(env);
+ // static void antik(Context)
+ jmethodID antikMethod = env->GetStaticMethodID(
+ menuClass,
+ "CreateMenu",
+ "(Landroid/content/Context;)V"
+ );
+ if (!antikMethod) return;
+
+ env->CallStaticVoidMethod(menuClass, antikMethod, context);
+}
+
+/*
+ * 🚀 Entry from JNI_OnLoad
+ */
+void binJava() {
+
+ if (antik->AttachCurrentThread(&antikYt, nullptr) != JNI_OK)
+ return;
+
+ jclass atCls = antikYt->FindClass("android/app/ActivityThread");
+ jmethodID curApp = antikYt->GetStaticMethodID(
+ atCls,
+ "currentApplication",
+ "()Landroid/app/Application;"
+ );
+
+ jobject app = antikYt->CallStaticObjectMethod(atCls, curApp);
+ if (!app) return;
+
+ loadDex(antikYt, app);
+}
+
+
diff --git a/app/src/main/jni/JavaGPP/Interface/Interface.h b/app/src/main/jni/JavaGPP/Interface/Interface.h
new file mode 100644
index 0000000..c44b23b
--- /dev/null
+++ b/app/src/main/jni/JavaGPP/Interface/Interface.h
@@ -0,0 +1,28 @@
+//
+// Created by aantik on 2/1/2026.
+//
+// Interface.h
+#ifndef NATIVE_LGL_INTERFACE_H
+#define NATIVE_LGL_INTERFACE_H
+
+
+#include
+#include
+
+extern JavaVM *antik;
+extern JNIEnv *antikYt;
+
+namespace InterfaceMethods {
+ extern void *Icon;
+ extern void *IconWebViewData;
+ extern void *getFeatureList;
+ extern void *settingsList;
+ extern void *Changes;
+ extern void *setTitleText;
+ extern void *setHeadingText;
+}
+
+void binJava();
+
+
+#endif //NATIVE_LGL_INTERFACE_H
diff --git a/app/src/main/jni/JavaGPP/Interface/OreoOrMore.h b/app/src/main/jni/JavaGPP/Interface/OreoOrMore.h
new file mode 100644
index 0000000..898c7b2
--- /dev/null
+++ b/app/src/main/jni/JavaGPP/Interface/OreoOrMore.h
@@ -0,0 +1 @@
+#define OreoOrMore "6465780A30333900443E0FAC441ECB08547FD042EE2BA2776F1134BB835930B5BC4F000070000000785634120000000000000000EC4E0000900100007000000061000000B0060000500000003408000048000000F40B0000BB000000340E0000110000000C140000903900002C160000402F0000422F0000552F0000612F00006C2F0000752F00007E2F0000872F0000902F0000992F0000A22F0000AB2F0000B42F0000BF2F0000CA2F0000D32F0000D82F0000E12F0000EB2F00002E30000036300000403000004B300000533000005A3000006430000070300000793000008830000096300000A2300000B0300000B3300000B9300000C1300000D7300000E9300000EC300000F830000003310000073100000B31000011310000413100005231000055310000593100005F31000063310000683100006E310000733100007931000091310000C6310000E531000000320000263200004732000062320000843200009E320000BB320000E13200000F330000243300003A3300004F330000643300007C330000A1330000BB330000D2330000F13300000C340000283400004D340000763400009B340000B0340000D7340000043500002F3500004D3500006735000080350000B9350000DA350000F83500001E3600003A3600006636000085360000B3360000D4360000F13600000A370000253700003D3700006137000086370000A6370000C9370000EF3700000E3800002A38000044380000573800006D38000090380000A5380000C7380000DB380000F13800000F390000233900003E390000523900006939000084390000A0390000B8390000CE390000DF390000F43900000B3A00001D3A0000323A0000453A0000583A00006B3A00007D3A0000893A0000AD3A0000D13A0000F53A0000193B00003D3B0000613B0000853B0000A93B0000CB3B0000D43B0000E33B0000F03B0000073C0000143C0000203C00002A3C00003D3C0000523C00005C3C00006C3C0000783C0000863C0000933C0000A13C0000B73C0000BF3C0000C43C0000D03C0000DE3C0000E63C0000EE3C0000F93C0000033D00000A3D00000D3D0000113D0000153D00001A3D0000213D0000293D00002D3D0000323D00003B3D0000403D0000463D00004B3D00004F3D0000523D0000563D00005B3D00005F3D0000633D0000773D00008C3D0000A13D0000A63D0000A93D0000B53D0000C13D0000CD3D0000DA3D0000E63D0000F03D0000FF3D0000083E0000113E00002D3E0000373E00003F3E00004F3E0000603E0000643E0000683E00006F3E0000803E0000863E0000943E00009E3E0000AD3E0000B43E0000C43E0000CE3E0000D73E0000EB3E0000F43E00000B3F0000133F0000243F00002D3F0000313F00003B3F00003E3F0000463F0000543F00005E3F0000673F0000703F00007D3F0000863F0000903F00009C3F0000A13F0000AC3F0000B83F0000C23F0000CE3F0000E03F0000F33F00000640000016400000274000003640000048400000514000005A400000684000007A4000008A4000009C400000AB400000BD400000C6400000CF400000D8400000E2400000EB400000F3400000FC400000FF4000000C410000244100003C4100004F4100005B410000694100007841000087410000914100009B410000A5410000AD410000BB410000C8410000D9410000E3410000F4410000044200000C4200001A420000224200002942000033420000404200004D42000058420000644200006F4200007F4200008A4200009A420000A4420000A9420000B2420000BA420000C7420000CD420000D4420000DF420000E5420000F7420000004300000A43000017430000204300003143000039430000454300004F4300005843000060430000664300006C430000724300007A4300008A4300009A430000A6430000B4430000B8430000C4430000CF430000D4430000EB430000FF4300000744000017440000214400002D4400003C440000464400005A440000664400007044000080440000914400009F440000AB440000BB440000CB440000DC440000F34400000F450000234500003B4500004F4500005F4500006B450000794500008645000095450000A0450000A9450000B7450000C4450000D1450000DF450000EC450000004600000F4600001946000027460000354600003B460000424600004E4600005C460000614600006B460000734600007A460000834600008A460000934600009F460000A5460000B0460000BA460000C2460000C9460000D4460000DE460000E9460000FB4600000247000005470000124700001947000021470000274700002E470000364700003D470000404700004447000047470000AF470000594A00001F000000240000003400000035000000360000003700000038000000390000003A0000003B0000003C0000003D0000003E0000003F000000400000004100000042000000430000004400000045000000460000004700000048000000490000004A0000004B0000004C0000004D0000004E0000004F000000500000005100000052000000530000005400000055000000560000005700000058000000590000005A0000005B0000005C0000005D0000005E0000005F000000600000006100000062000000630000006400000065000000660000006700000068000000690000006A0000006C0000006D0000006E0000006F000000700000007100000072000000730000007400000075000000760000007700000078000000790000007A0000007C0000007D0000007E0000007F000000800000008100000082000000830000008400000085000000860000008700000088000000890000008A0000008B0000008C000000A6000000B3000000B6000000B7000000B8000000B9000000BA000000BB0000001F00000000000000000000002000000000000000D02D00002400000001000000000000002700000001000000DC2D00002800000001000000E42D00002C00000002000000000000002C00000005000000000000002C00000007000000000000003100000008000000EC2D00002C0000000C000000000000002C00000010000000000000002F00000012000000E42D00002C00000016000000000000002C0000001B000000000000002E0000001C000000F82D00002F0000001C000000E42D00002C0000001D000000000000002C0000002000000000000000330000002F000000042E00002C00000038000000000000002F00000038000000E42D00002C0000003C000000000000002F0000003C000000102E0000320000003C000000182E00002C0000003F00000000000000320000003F000000202E00002D00000040000000DC2D00002F00000040000000E42D00002F00000043000000E42D00003200000044000000282E00002C00000045000000000000002C00000046000000000000002F00000058000000302E0000A60000005900000000000000A700000059000000382E0000A800000059000000DC2D0000A900000059000000402E0000AA00000059000000482E0000AB00000059000000542E0000AC00000059000000642E0000AC000000590000006C2E0000AC00000059000000742E0000AE000000590000007C2E0000AC000000590000008C2E0000AC00000059000000942E0000AC000000590000009C2E0000AD00000059000000A42E0000AC00000059000000AC2E0000AC00000059000000B42E0000AC00000059000000BC2E0000AC00000059000000B02D0000AC00000059000000B82D0000AC00000059000000C02D0000AC00000059000000C42E0000AF00000059000000CC2E0000AC00000059000000D42E0000AC00000059000000C82D0000B100000059000000DC2E0000AC00000059000000E42E0000AC00000059000000EC2E0000AC00000059000000F42E0000AC00000059000000E42D0000B000000059000000FC2E0000AC00000059000000082F0000AC00000059000000302E0000AD00000059000000102F0000B200000059000000182F0000AF00000059000000202F0000AF00000059000000282F0000B30000005A00000000000000B40000005A000000C42E0000B50000005A000000302F0000B40000005A000000F42E0000B40000005A000000102E0000B40000005A000000A82D0000B40000005A000000E42D0000B40000005A000000302E0000300000005B000000382F00002C0000005F000000000000002F0000005F000000E42D000003000100090100000B000B001E000000130013008D00000016000000DC0000001D000100020100001D000100890100001E0001007F0100001F000100FD0000001F0001008A0100001F0001008C0100002600260021000000280000008801000049000100CE0000004A000100060100004A000100070100004B000100C30000004C000100050100004C000100080100004D000100C70000004E00010014000000510058007B01000052005A006B010000520058007B010000530058007B010000540058007B010000550058007B01000056001C00D200000056001C00E1000000560000000B010000560000000C010000560001000D010000560001000E010000560058007B010000570058007B01000057000100830100005800010015000000580001001600000058000100170000005800010019000000580001001B00000058000000250000005800010026000000580001008E000000580000008F00000058000100900000005800010091000000580001009200000058003F00970000005800010098000000580001009B000000580001009C00000058003F009E000000580001009F00000058000100A000000058000100A300000058000100A400000058000200C2000000580058000F010000580029001E01000058002B001F010000580029002001000058002B00210100005800290022010000580020002301000058001F0033010000580029003601000058002500420100005800280046010000580028004701000058002C0049010000580027006E01000058005A006F010000020021001300000002000700F700000002001100FC00000002002F002F010000030021001300000004002800F4000000060044001300000007000C00F000000009000800DB0000000A000400340100000C002B00640100000D002100130000000D0023004E0100000D002200500100000D002400600100000F003000130000000F004A003801000010000A00F300000011000B00E7000000140048001101000015004D00DA00000017000100C900000018000200EA00000018000000F500000018000000F60000001C000600ED0000001C002300680100001F0026001300000020003600C5000000200035003E0100002000360080010000210029001300000021001000F200000021003E001B010000210022004B010000210023004C0100002100370055010000210034005A010000210025005C0100002200290013000000220023004C01000022003700550100002200320058010000220033005901000022003C00610100002200230062010000250029001300000025003500C5000000250034005A0100002500230068010000270029001300000027001000F200000027002C005401000027003700550100002700320058010000270034005A01000027003A005D01000028002400130000002800370013000000290029001300000029003500C500000029001000F2000000290021003D010000290022004B010000290023004C0100002900370055010000290023005B01000029002300680100002A002400130000002A002300C40000002B002900130000002B003500C50000002B000200FB0000002B0022004B0100002B0025005C0100002B002300670100002B002300680100002C002900130000002C003500C50000002C0035003E0100002C002400480100002C0023004C0100002C003700550100002D002900130000002D000900F80000002D000900FA0000002D0042004D0100002D003800570100002D0025005C0100002D003C00610100002D002300620100002E002900130000002E0023004C0100002E003100510100002E002300520100002E003700550100002E002300560100002E003200580100002E0025005C0100002E0042005E0100002E0042005F0100002E003C00610100002E002300620100002E002200630100002E002D00660100002E002E00660100002F001200240100002F0021006C01000038001400E600000038001C00EE00000038001D00EF0000003A000400350100003C002100130000003C001300EC0000003E003F00130000003F004800D50000003F004900E00000003F000200000100003F004B00260100003F0019003F0100003F004F006D010000400021001300000040001A00C800000040001B00C8000000400018007E01000041003D001C01000043001600E900000043004900EB000000430042004A0100004400170010010000440042004A01000045001F001401000046004500FF000000460015002C01000047001E00850100004800210011000000480021001300000048002F002F010000480023004F01000049002100130000004A002100130000004B002100130000004C002100130000004D002100130000004E002100130000004F00210013000000500021001300000050002100430100005100400013000000510035002E0100005200400013000000520035002E0100005300400013000000530035002E010000540040001300000054004600300100005500400013000000550035002E010000560040001300000056004700310100005700410013000000570039002D010000580027001300000058000F001800000058002A001A000000580029001D0000005800180029000000580018002B00000058000F009900000058000F009A00000058000E009D00000058002100A100000058000500BD00000058002000BE00000058004C00BF00000058000300D700000058000300DD00000058004300E500000058004E00F100000058000500F9000000580021000A0100005800450012010000580045001301000058000D003201000058003B005301000058003B006501000058004E006A0100004800000001000000020000000000000094000000000000001A4B00000000000049000000110000003C00000000000000FFFFFFFFBC4D0000304B0000EC4C00004A000000110000003C00000000000000FFFFFFFFCC4D00003D4B0000F24C00004B000000110000003C00000000000000FFFFFFFFDC4D00004C4B0000FD4C00004C000000110000003C00000000000000FFFFFFFFEC4D0000594B0000034D00004D000000110000003C00000000000000FFFFFFFFFC4D0000684B00000E4D00004E000000110000003C00000000000000FFFFFFFF0C4E0000754B0000144D00004F000000110000003C00000000000000FFFFFFFF1C4E0000824B00000000000050000000000000003C000000A82D0000220000002C4E00008D4B00000000000051000000000000003C000000B02D0000220000003C4E00009D4B00000000000052000000000000003C000000B02D000022000000544E0000B04B00000000000053000000000000003C000000B02D000022000000744E0000C54B00000000000054000000000000003C000000B82D0000220000008C4E0000D84B00000000000055000000000000003C000000B02D000022000000A44E0000EB4B00000000000056000000000000003C000000C02D000022000000BC4E0000FE4B00000000000057000000000000003C000000C82D000022000000D44E00001D4C00000000000058000000010000003C000000000000002200000000000000334C00001A4D00000100000001000000262A0000060000001A002B0171107D0000000E0001000100010000002B2A0000040000007010000000000E0003000200020000002F2A0000090000006F200300210060000F006E208A0001000E000000010001000100000000000000040000007010700000000E00010001000100000000000000040000007010700000000E00010001000100000000000000040000007010700000000E00010001000100000000000000040000007010700000000E00010001000100000000000000040000007010700000000E00010001000100000000000000040000007010700000000E00010001000100000000000000040000007010700000000E000100010001000000372A0000040000007010700000000E0003000100020000003C2A00000F0000007100AC0000000C0038000A00220158007020A20001007110AD0001000E0000000200020001000000492A0000060000005B0114007010700000000E000400020002000000502A0000120000005420140054003B00130108006E204C0010005420140054003C0012016E20430010000E000200020001000000592A0000060000005B0116007010700000000E000400020003000000602A00004000000055201500DF0000015C2015005520150038002100542016005400450054211600541141006E204F00100054201600540045005421160054113E006E204E001000542016005400450012016E3050001001281754201600540045005421160054113E006E204F001000542016005400450054211600541141006E204E0010000E0002000200010000006E2A0000060000005B0117007010700000000E000500020003000000752A0000280000005430170054003B0012016E204C0010005430170054003B0012016E20490010005430170054003C00130108006E20430010006E10190004000C001A012A00121271306A0010020C006E106B0000000E000500020003000000802A00001B0000006E10190004000C001A019500121271306A0010020C006E106B0000005430180054003F00543118005411420072201D00100012000F0000000200020001000000892A0000060000005B0118007010700000000E000200020001000000902A0000060000005B0119007010700000000E000400020002000000972A00001D0000005420190054003B0012016E204C0010005420190054003B0054211900521128006E20490010005420190054003C00130108006E20430010000E0000000900030003000100A12A0000B60000006E10160008000A00120112122B00A60000000F015460200054003C001501003F6E203F0010005460200054003B006E2049001000546020005400400052611E006E10170008000A0352641C00C7438733B03159010800546020005400400052611F006E10180008000A0352641D00C7438733B031590109005460200054003F005461200054114200546320005433400072301E0010030F026E10170008000A0052631C00C73087006E10180008000A0352641D00C74387335464200054443C001505803F6E203F0054005464200054443B006E204900540013040A003540200035431E00546420007110AE0004000A043804160054641A00130508006E201A00540054641B006E201A00140028080D0122023E0070207200120027020F0254602000540040005200080059601E0054602000540040005200090059601F006E10170008000A0059601C006E10180008000A0059601D000F0200010300000000008900000046000000040000007A0000000C00010001013B87010000000300020001000000E22A0000120000005B1220007010700001005410200054003B005B101A005410200054003C005B101B000E000300030001000000EB2A0000080000005B012100590222007010700000000E000500030002000000F32A000024000000522022002B0018000000281439040300281154202100540045005421210038040500541144002803541143006E20520010000E0000010300FDFFFFFF0700000003000000040000000100000000000000022B0000050000007100B30000000C00110000000C00000003000100062B00006300000012001A01C60071106C0001000C011A02D900120323345D006E306E0021040C0212146E208200420023335E006E30810002030C031A051D016E206D0051000C056E20800045006E207E0035000C061F0647007210860006000C077210830007000C077210840007000A0838082A007210850007000C086E10710008000C091A0A37016E206D00A9000C0A6E2080004A006E207F008A000A0B390B12001A07C2006E206D0079000C076E20800047006E207E0087000C041F040200110428D328020D01000011000000010000005C000100010060000500020005000000632B00002800000022002E005431380070205B001000523126006E205C0010007110120004000C016E2065001000130111006E205E001000523135006E2066001000120112126E3069001002120112526E526200101211000500020005000000762B00001A00000022002E005431380070205B0010007110120004000C016E2065001000523135006E206600100013010A0012526E526200101211000500020005000000862B000017000000220021005431380070201F0010001A0179011A0281016E402100402112016E202300100012526E5226001012110000000B00040005000000962B00005500000022002D005471380070205300100022010600140262FFFEFE24105C0002000C021403A000010124105C0003000C03120423455C002430600032050C0252733700527536001406FF0000FF24305C0036050C0370300600210300006E10540000000C026E200A0012006E10550000000C026E200A0012006E2059009000527235006E205A00200013020A0012536E53580020436E205600A000220257007030A00072086E2057002000110000000200010002000000BB2B0000060000002200560070209E00100011000200010001000000C02B0000050000007010B60001000A000F0000000400010001000000C62B000012000000220003007010040000007110050000005201000013026400322104001211280212010F010200010001000000D22B0000110000005410420038000D0054103B006E10480000000A003900030028031200280212100F0000000400020001000000D72B0000130000008230542138006E10010001000C016E10070001000C0152110300C8101501003FC61087000F0000000500020003000000DE2B0000120000008240543138006E10010001000C016E10070001000C0112127130150002010A0087000F000100010000000000C02B00000300000069003900110000000500020001000000E52B0000850000007010700003001A000B007110090000000A00593034001A000E007110090000000A01593135001A0104007110090001000A01593123001A010D007110090001000A0159312A001A010C007110090001000A0159312C001301220159312E001301D20059312D001501804059312B0013012D005931290014013333333F59312800140100FF00FF593137001501FFFF593136001A0105007110090001000A01593125001A0109007110090001000A01593124001A0106007110090001000A01593126001A010A007110090001000A02593231007110090001000A02593232007110090001000A01593127007110090000000A00593030001A0008005B302F005B3438007010B40003000E0000000300010002000000062C00002600000062003900380003000E00202002003900140022000F007100110000000C0170200F001000220150007010920001006E2010001000280B2200580007211F0102007020A2001000690039000E0003000100020000001A2C00001900000054204200390003000E007010B50002000A00380009005420420012416E203100100028075420420012016E20310010000E0000000F00030004000000242C0000F0000000120007E1120221D33532CD00120346040D021A05A5006E20730054000A061A0700003806070012136E30770054070C04071E1A051C006E20730054000A063806080054CE3A006E30770054070C041A05BC006E20780054000C061208460906087110130009000A0939091000460906081A0A10006E207600A9000A093809030028049107020028214609060871106F0009000A09220A4000701079000A00460B06086E207B00BA000C0A6E207B005A000C0A6E107C000A000C0A6E307700A4070C04D800000101976E20780054000C05460905086E10750009000A0A121B2C0A6300000028281A089A006E20740089000A083808F9FF1238281F1A0818006E20740089000A083808EFFF01B828151A0899006E20740089000A083808E5FF1228280B1A0AA2006E207400A9000A093809DBFF280212F82B0849000000282FD80000014608050B7020A9008C000C086E203C008E002823D80000014608050B7020A8008C000C086E203C008E002817D80000014608050B7020A3008C000C086E203C008E00280B4608050B7040AA007C380C086E203C008E000000D8020201290034FF0E000002040074A7A3958E1AF1041E21DD067D17771922000000180000000E000000040000000001040000000000280000001C00000010000000040000001B00010006000000A32C00005B03000008001A00220125005402380070202E0021005B014200540142007010B70000000C026E203000210022012B00540238007020460021005B013D0022012B00540238007020460021005B013B0054013B0012026E204C00210054013B00520328006E2049003100220129005403380070203B0031005B013C0054013C00130308006E204300310054013C0052032A006E204000310054013C0012136E204200310054013C002204280052052E007020B00050000A0512E67030390054066E204100410022010D0070100B00010052042B006E200D00410052042A006E200C0041001A0407007110090004000A046E300E00310422042700540538007020320054005B0446005404460022052A007030440065066E2035005400520429008244540538006E10010005000C056E10070005000C057130150043050A048744540546006E10330005000C0559540400540546006E10330005000C05595405005405460062070A006E20380075006E10A60000000C057120140025000C055407460021587130080025080C086E2034008700540746006E10330007000C071F071E0013080A007020AF0080000A0959790600540746007010B70000000C096E203700970054074600220951007020940009006E2036009700220721005409380070201F00970022092A007030440069066E2024009700520929008299540A38006E1001000A000C0A6E1007000A000C0A71301500930A0A0987996E10200007000C0A59A904006E10200007000C0A59A90500220A4000701079000A001A0B12006E207B00BA000C0A6E10A70000000C0B6E207B00BA000C0A1A0B03006E207B00BA000C0A520B29006E207A00BA000C0A1A0B02006E207B00BA000C0A520B29006E207A00BA000C0A1A0B01006E207B00BA000C0A6E107C000A000C0A1A0B79011A0C81016E402100A7CB6E2023002700520A28006E202200A7007010B70000000C0A6E202500A700220A2E00540B380070205B00BA001A0B8F016E206500BA00520B34006E206600BA00620B01006E206800BA00150BA0416E206700BA00220B2A00703044006B06130C0B006E204500CB006E205F00BA00220D5200702096000D006E206100DA00220D2900540E380070203B00ED005B0D3E00540D3E006E2042003D006E10BA0000000C0D540E3E007030B100D00E220D2B00540E380070204600ED00125E6E5E4A008D8E130F10006E204B00FD00220F2E00540C380070205B00CF00520C34006E206600CF00150C90416E206700CF00130C11006E205E00CF0022082A0070304400680613060E006E20450068006E205F008F006E20B900F00022062E005402380070205B002600620202006E205D00260012F26E20600026006E20640036006E2063003600520334006E2066003600150320416E20670036006E205E00C60012036E5E620036336E20B800600022032C00540E380070204D00E3005B03450022032800520E2D007020B000E0000A0E70303900230E5B0343002202280054033C006E103D0003000C0370203A0032005B024400540244001503803F59230B0054024500540343006E20520032005402450052032C006E2051003200220229005403380070203B0032005B0241005402410012136E204200320022022B00540338007020460032001233130E0A006E534A00E2E36E204B00C20022032A0012EC70304400C30C130C09006E204500C300220C2200540E380070202700EC006E2029003C00120E6E202800EC001A0E23006E202C00EC00520E34006E202D00EC00220E5300702098000E006E202A00EC00220E540070209A000E006E202B00EC00220E2A000811010012E1703044001E0113010B006E2045001E002201220008100300540338007020270031006E202900E10012036E20280031001A0393006E202C003100520334006E202D0031002203550070209C0003006E202A00310022141F001418080180001319FEFF1315FEFF1316FEFF1317020076061B001400080314005B03400054034000021204001304330059340700540340001204593408005403400013046400593409005403420054043D006E202F00430054033D0054043B006E204700430054033D0054043C006E20470043006E10A70000000C033803080054033B006E2047007300280854033B00540446006E20470043006E204700FD006E204700AD0054033C006E203C00D30054033C006E203C00630054034500540441006E204E00430054033C00540445006E203C0043006E204700C2006E204700120054033C006E203C002300540338006E10020003000C035B033F0054033F0054044200081301005401400072301C004301540141006E103E0001006E10B20000000C01540341007030B10010030E0010000E5A000D000E001501C6020E3C5B008901000E008C01000E4B0300C301032D8800D30101FC020E00D5010187030E968700F20101FC020E00F7010183030E694BB4B497B4B500BA0201FC020E00BC020187030E878796E100C4020187030EE1B400C20201FC020E00D30201FC020E00D5020187030E87B496008D03028703A9020E021F9502771D967901110F011110F0026B1D870300BA0202870303BB02029678E2785C1B1E0301E0013C6B0501026E1D050005038787696900850301FC020E5A6900FE0302FC02000E00800402D501D0010E6A2D1F011011004A000E009901000E780401CD01396C960302D901454B6A0303CE013D690305C201443C6A0406A602487C01120F0308BC023D4B0409CC01396C69030AB802443C69690307C301443C79050705080509050A02771D05010502050305050506272D008E0401F9020E780300FB022F5A785A5A5A5A00990401F9020E780300FB022F785A6900A10401F9020E780300880322784B4B00E80303E401E301F6020E780300F7022E020D01250E0301D101071E78793C5A693C020C86008503000E004A018C030E00B804000E5A0300C502043C00AC04000E00B0040185020E00B5040185020E007D01C3010E02513B87878787874B4B4B4B5A5A4B87878787696969021D4A2D3C00860101D7010E5A4B960300FF01108F05001EA600BE04000E4B1F69796A00B503029A0299020E1E0300F502021F03019B022A010103028502023C1F0303F8025B2D0304E50140A51E4D1E882D4C6B0306F1026001141401020307E40102027B1D0507690309E4010201170F40050906074B0305F20260020D01370E2D027BA42D96027A1D2D96027B1D9602621D0503050405050506022D4A0502050700B401000EB4969696697A9678786901110F5A0301E9010E5A5A9896A501120F0304CA0102878878870305DB015CA501101096AC7803078803228701120F0309CB01026969B6012A0B7D3C5A7978030AEA022F5A5A5A5A5A030BC2022B5A3C021286965A9878030DFE022C4B5A78030FFD022F5A5A5A5A0308C1022B5A3C3E78030682022F5A4B3C3C5A5A3C4B3E97D2D2697878966B780302BD022C693E69030397022B5B78030C8402233C4B5A5A8E8F010505010311E9010E3C030E96022B5B01040503031097022B5A0301D201233C4B5A5A020B86011417010405040312CA01024B5A6C787878696A793C3C5A5A78783C3C5A87010605010313D201235A5A9600000000010000003D0000000100000019000000010000001A000000010000001B00000001000000230000000300000001000000160000000100000001000000010000003F000000030000005B000100010000000300000001003F005A000000030000000500370001000000010000003C000000020000003C005E00020000003F003F00020000003F005D000100000058000000010000000000000002000000010001000400000001000100010001000500000001000100010001000100000001000000020000000100000003000000010000000500000006000000050001003F0001005A003F0001000000060000000100000008000000010000000B000000020000000B000100010000000E00000001000000100000000100000013000000010000001C000000020000001C001D00010000001D0000000200000024005A000100000026000000010000002E0000000100000037000000030000003F003F003F00000001000000420000000200000058000100010000005A000000020000005F0029000200000060005C00020000001C001800020000003F00010000001122203E3C2F626F64793E3C2F68746D6C3E000A22206865696768743D220009222077696474683D2200072331433236324400072331623565323000072332463344344300072333326362303000072334316333303000072337663030303000072338304342433400072338324341464400092344443134314332320009234545314332413335000723464646464646000328295600072D5B302D395D2A00083C636C696E69743E00413C68746D6C3E3C686561643E3C2F686561643E3C626F6479207374796C653D226D617267696E3A20303B2070616464696E673A2030223E3C696D67207372633D2200063C696E69743E00084170705468656D65000942544E5F434F4C4F52000642746E4F4646000542746E4F4E000843617465676F7279000A43617465676F7279424700074368616E676573000D436865636B426F78436F6C6F72000C436F6C6C617073654164645F000A4372656174654D656E75000C44454641554C545F424F4C4400014600044649464C00064649545F58590014466C6F6174696E674D6F644D656E752E6A6176610010484944452F4B494C4C2028486F6C6429000149000A49434F4E5F414C504841000949434F4E5F53495A45000249490002494C000449636F6E002E49636F6E2068696464656E2E2052656D656D626572207468652068696464656E2069636F6E20706F736974696F6E000F49636F6E576562566965774461746100014C00024C4900044C494C5A00024C4C00034C4C4900044C4C494900034C4C4C00044C4C4C4900164C616E64726F69642F6170702F41637469766974793B00334C616E64726F69642F6170702F41637469766974794D616E616765722452756E6E696E6741707050726F63657373496E666F3B001D4C616E64726F69642F6170702F41637469766974794D616E616765723B00194C616E64726F69642F636F6E74656E742F436F6E746578743B00244C616E64726F69642F636F6E74656E742F7265732F436F6C6F7253746174654C6973743B001F4C616E64726F69642F636F6E74656E742F7265732F5265736F75726365733B00194C616E64726F69642F67726170686963732F4269746D61703B00204C616E64726F69642F67726170686963732F4269746D6170466163746F72793B00184C616E64726F69642F67726170686963732F436F6C6F723B001B4C616E64726F69642F67726170686963732F54797065666163653B00244C616E64726F69642F67726170686963732F6472617761626C652F4472617761626C653B002C4C616E64726F69642F67726170686963732F6472617761626C652F4772616469656E744472617761626C653B00134C616E64726F69642F6F732F42756E646C653B00144C616E64726F69642F6F732F48616E646C65723B00134C616E64726F69642F6F732F4C6F6F7065723B00134C616E64726F69642F746578742F48746D6C3B00164C616E64726F69642F746578742F5370616E6E65643B00234C616E64726F69642F746578742F546578745574696C73245472756E6361746541743B00184C616E64726F69642F746578742F546578745574696C733B00154C616E64726F69642F7574696C2F4261736536343B001D4C616E64726F69642F7574696C2F446973706C61794D6574726963733B00194C616E64726F69642F7574696C2F547970656456616C75653B001A4C616E64726F69642F766965772F4D6F74696F6E4576656E743B00234C616E64726F69642F766965772F56696577244F6E436C69636B4C697374656E65723B00274C616E64726F69642F766965772F56696577244F6E4C6F6E67436C69636B4C697374656E65723B00234C616E64726F69642F766965772F56696577244F6E546F7563684C697374656E65723B00134C616E64726F69642F766965772F566965773B00254C616E64726F69642F766965772F5669657747726F7570244C61796F7574506172616D733B002B4C616E64726F69642F766965772F5669657747726F7570244D617267696E4C61796F7574506172616D733B00294C616E64726F69642F766965772F57696E646F774D616E61676572244C61796F7574506172616D733B001C4C616E64726F69642F766965772F57696E646F774D616E616765723B00184C616E64726F69642F7765626B69742F576562566965773B00174C616E64726F69642F7769646765742F427574746F6E3B00374C616E64726F69642F7769646765742F436F6D706F756E64427574746F6E244F6E436865636B65644368616E67654C697374656E65723B001F4C616E64726F69642F7769646765742F436F6D706F756E64427574746F6E3B001C4C616E64726F69642F7769646765742F4672616D654C61796F75743B00244C616E64726F69642F7769646765742F496D61676556696577245363616C65547970653B001A4C616E64726F69642F7769646765742F496D616765566965773B002A4C616E64726F69642F7769646765742F4C696E6561724C61796F7574244C61796F7574506172616D733B001D4C616E64726F69642F7769646765742F4C696E6561724C61796F75743B002C4C616E64726F69642F7769646765742F52656C61746976654C61796F7574244C61796F7574506172616D733B001F4C616E64726F69642F7769646765742F52656C61746976654C61796F75743B001B4C616E64726F69642F7769646765742F5363726F6C6C566965773B00174C616E64726F69642F7769646765742F5377697463683B00194C616E64726F69642F7769646765742F54657874566965773B00164C616E64726F69642F7769646765742F546F6173743B00224C64616C76696B2F616E6E6F746174696F6E2F456E636C6F73696E67436C6173733B00234C64616C76696B2F616E6E6F746174696F6E2F456E636C6F73696E674D6574686F643B001E4C64616C76696B2F616E6E6F746174696F6E2F496E6E6572436C6173733B00214C64616C76696B2F616E6E6F746174696F6E2F4D656D626572436C61737365733B00244C64616C76696B2F616E6E6F746174696F6E2F4D6574686F64506172616D65746572733B001D4C64616C76696B2F616E6E6F746174696F6E2F5369676E61747572653B001A4C64616C76696B2F616E6E6F746174696F6E2F5468726F77733B00184C6A6176612F6C616E672F4368617253657175656E63653B00114C6A6176612F6C616E672F436C6173733B00144C6A6176612F6C616E672F436C6173733C2A3E3B00214C6A6176612F6C616E672F496C6C6567616C5374617465457863657074696F6E3B00134C6A6176612F6C616E672F496E74656765723B00204C6A6176612F6C616E672F4E756C6C506F696E746572457863657074696F6E3B00124C6A6176612F6C616E672F4F626A6563743B00144C6A6176612F6C616E672F52756E6E61626C653B001C4C6A6176612F6C616E672F52756E74696D65457863657074696F6E3B00124C6A6176612F6C616E672F537472696E673B00194C6A6176612F6C616E672F537472696E674275696C6465723B00124C6A6176612F6C616E672F53797374656D3B00154C6A6176612F6C616E672F5468726F7761626C653B00194C6A6176612F6C616E672F7265666C6563742F4669656C643B001A4C6A6176612F6C616E672F7265666C6563742F4D6574686F643B00164C6A6176612F7574696C2F436F6C6C656374696F6E3B00144C6A6176612F7574696C2F4974657261746F723B000F4C6A6176612F7574696C2F4D61703B00134C6A6176612F7574696C2F4D61703C2A2A3E3B00154C756B2F6C676C2F4D61696E41637469766974793B00104C756B2F6C676C2F5224636F6C6F723B00134C756B2F6C676C2F52246472617761626C653B00114C756B2F6C676C2F52246C61796F75743B00114C756B2F6C676C2F52246D69706D61703B00114C756B2F6C676C2F5224737472696E673B00104C756B2F6C676C2F52247374796C653B000A4C756B2F6C676C2F523B00224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524313B00224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524323B00224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524333B00224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524343B00224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524353B00224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524363B00224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524373B00224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524383B00204C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E753B00074D415251554545000D4D454E555F42475F434F4C4F52000B4D454E555F434F524E455200154D454E555F464541545552455F42475F434F4C4F52000B4D454E555F484549474854000A4D454E555F574944544800084D494E494D495A4500114D61696E41637469766974792E6A61766100134D656E752073657276696365206B696C6C656400084D6F645F4D656E75000E4E756D626572547874436F6C6F72000A526164696F436F6C6F72000C526963685465787456696577000B5269636857656256696577000C5365656B426172436F6C6F7200145365656B42617250726F6772657373436F6C6F7200065377697463680003544147000A544558545F434F4C4F52000C544558545F434F4C4F525F3200065468726561640006546F67676C650009546F67676C654F46460008546F67676C654F4E0005547275655F000156000256460002564900035649490005564949494900065649494949490002564C0003564C490007564C494C495A4C0003564C4C0004564C4C4C0003564C5A0002565A00015A00025A4C00035A4C4C00025B4200025B4900125B4C6A6176612F6C616E672F436C6173733B00135B4C6A6176612F6C616E672F4F626A6563743B00135B4C6A6176612F6C616E672F537472696E673B00035B5B4900015F000A61636365737324303030000A61636365737324313032000A61636365737324323030000B616363657373466C616773000A6163746976697469657300086163746976697479000D61637469766974795F6D61696E000761646452756C65000761646456696577001A616E64726F69642E6170702E416374697669747954687265616400086170705F6E616D650006617070656E64000E6170706C7944696D656E73696F6E000F6170706C7944696D656E73696F6E320002617200026174000561744F626A000F6261636B67726F756E64436F6C6F720004626F6F6C000C627574746F6E5374617465730008636C6F736542746E000D636F6C6C6170736564566965770005636F6C6F72000E636F6D706F756E64427574746F6E0008636F6E7461696E730007636F6E746578740012636F6E76657274446970546F506978656C73000763757272656E74001563757272656E74416374697669747954687265616400066465636F6465000F6465636F6465427974654172726179000764656E736974790002647000086472617761626C650001650006657175616C73000C657870616E646564566965770008666561744E616D650007666561744E756D000766656174757265000B666561747572654C6973740007666F724E616D65000866726F6D48746D6C000A67644D656E75426F647900036765740009676574416374696F6E000A676574426F6F6C65616E0008676574436C617373000A676574436F6E7465787400106765744465636C617265644669656C6400116765744465636C617265644D6574686F640011676574446973706C61794D657472696373000E676574466561747572654C697374000F6765744C61796F7574506172616D73000D6765744D61696E4C6F6F70657200106765744D794D656D6F72795374617465000767657452617758000767657452617759000C6765745265736F757263657300106765745468756D624472617761626C65000E676574546F7041637469766974790010676574547261636B4472617761626C65000D6765745669736962696C697479001067657457696E646F774D616E61676572000767726176697479000768616E646C657200076861734E657874000868617368436F6465000768656164696E67000668656967687400076869646542746E000169000B69635F6C61756E63686572001669635F6C61756E636865725F6261636B67726F756E64001669635F6C61756E636865725F666F726567726F756E64001169635F6C61756E636865725F726F756E64000A696D706F7274616E6365000C696E6974466C6F6174696E67000D696E697469616C546F75636858000D696E697469616C546F756368590008696E697469616C580008696E697469616C590008696E7374616E63650006696E766F6B65000C69734469676974734F6E6C79000B69734E6F74496E47616D65000F697356696577436F6C6C617073656400086974657261746F72000F6C506172616D73436C6F736542746E000E6C506172616D734869646542746E00066C61796F7574000C6C696E6561724C61796F757400066C697374465400056C6C42616B00086C6F616444617461000B6C6F61644C696272617279000B6D4163746976697469657300096D436F6C6C61707365000A6D436F6C6C617073656400096D457870616E646564000E6D526F6F74436F6E7461696E657200096D53657474696E6773000E6D57696E646F774D616E6167657200086D616B655465787400036D617000076D61746368657300066D69706D6170000B6D6F74696F6E4576656E7400046E616D6500056E616D657300096E61746976656C676C00046E65787400106F6E436865636B65644368616E67656400076F6E436C69636B00086F6E437265617465000B6F6E4C6F6E67436C69636B00076F6E546F756368000F6F6E546F7563684C697374656E65720006706172616D73000A7061727365436F6C6F7200087061727365496E7400077061746368657300067061757365640004706F737400047261775800047261775900067265636F7264000E72656C61746976654C61796F7574000E72656D6F7665416C6C5669657773000A72656D6F766556696577000C7265706C61636546697273740002726C000A726C73657474696E67730009726F6F744672616D65000372756E001572756E6E696E6741707050726F63657373496E666F00127361766564496E7374616E6365537461746500067363726C4C4C000E7363726C4C4C457870616E64656400087363726F6C6C546F000A7363726F6C6C56696577000D73657441636365737369626C650008736574416C70686100127365744261636B67726F756E64436F6C6F72000A736574436865636B65640008736574436F6C6F72000E736574436F6E74656E7456696577000F736574436F726E6572526164697573000C736574456C6C697073697A65000A73657447726176697479000E73657448656164696E6754657874000E736574496D6167654269746D6170000F7365744C61796F7574506172616D7300157365744D6172717565655265706561744C696D6974001A7365744F6E436865636B65644368616E67654C697374656E657200127365744F6E436C69636B4C697374656E657200167365744F6E4C6F6E67436C69636B4C697374656E657200127365744F6E546F7563684C697374656E6572000E7365744F7269656E746174696F6E000A73657450616464696E67000C7365745363616C6554797065000B73657453656C6563746564000D73657453696E676C654C696E6500097365745374726F6B65000773657454657874000C73657454657874436F6C6F72000B7365745465787453697A65000B73657454696E744C697374000C7365745469746C6554657874000B73657454797065666163650012736574566572746963616C47726176697479000D7365745669736962696C697479000873657474696E6773000C73657474696E67734C697374000C73657474696E67734F70656E000473686F77000573706C6974000A7374617274696D616765000C73746F70436865636B696E670003737472000873747253706C69740006737472696E6700057374796C6500077375624665617400057377694F6E000773776974636852000A73776974636865644F6E0004746578740009746578742F68746D6C00087465787456696577000674686973243000057469746C6500097469746C65546578740008746F537472696E670009746F704D617267696E0010757064617465566965774C61796F757400057574662D38000176000B76616C24666561744E756D000576616C7565000676616C756573000476696577000577566965770006776569676874000577696474680001780002783000017900667E7E44387B226261636B656E64223A22646578222C22636F6D70696C6174696F6E2D6D6F6465223A226465627567222C226861732D636865636B73756D73223A747275652C226D696E2D617069223A32382C2276657273696F6E223A22382E31332E3139227D00A7057E7E7E7B224C756B2F6C676C2F4D61696E41637469766974793B223A223333376637303031222C224C756B2F6C676C2F5224636F6C6F723B223A223563303432323433222C224C756B2F6C676C2F52246472617761626C653B223A226534383863663836222C224C756B2F6C676C2F52246C61796F75743B223A226137316639643361222C224C756B2F6C676C2F52246D69706D61703B223A226338363638376533222C224C756B2F6C676C2F5224737472696E673B223A223163383662656234222C224C756B2F6C676C2F52247374796C653B223A226462383932616136222C224C756B2F6C676C2F523B223A223363383335346463222C224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524313B223A223733326164363638222C224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524323B223A226562346634636537222C224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524333B223A223836623937623930222C224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524343B223A223637303261386131222C224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524353B223A226330306139393934222C224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524363B223A223663353662303839222C224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524373B223A223836336535643736222C224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E7524383B223A223934646266663863222C224C756B2F6C676C2F6D6F646D656E752F466C6F6174696E674D6F644D656E753B223A226562326664373133227D0001E29A9900023202C0010419A90217D30230018403184F023202C0010419A90217DE023202C0010419A902371701023202C0010419A902372701023202C0010419A902377201023202C0010419A90237730102330184031C061849184A184B184C184D184E02310184031AA5023202C0010400A9021E023402C0011C0144108000AA021C011E02310184031AB402360184031C01183902310184031AB7023402C0011C0244108000241010AA021C021E1E02350184031C01170F02310184031AAA000002018701888004AC2C01818004C82C890104E02C010001000C098B01828004842D020001000D0901098C018280049C2D010001000F098D01828004B42D02000100100901098E01828004CC2D0100010012098F01828004E42D0100010013099001828004FC2D000001009101828004942E000001019201808004AC2E930101C42E000101011490209401808004F42E950101902F0002010115000190209601808004C42F970101E02F000101011790209801808004F0309901018C31000101011890209A01808004B4329B0101EC31000101011990209C01808004D0329D0101EC32000701011A10011001020102010201020190209E01808004C4369F0101B83300020101219020019020A001808004F836A101019837022313063319060A23000100010001000100010001000100010001000100010001000100010001000200010001000100010202000100010001000100010001000100010001000100010001000100A201818004B43F0102F039018902000109D0410302D03A0102943B0102D43B0102AC42018820F0370188209C3F018820AC3D0102B03E0102E83E0102F042020A8C380102E0460102C83D0102FC3D0102903DA601800200018002000B80020006800200018002000180020001640000017F02640100027F640200027F01640000037F02640000047F640100047F01640000057F01640000067F01179600000002000000694A00005E4A000002000000694A0000704A000002000000694A00007B4A000002000000694A0000874A000002000000694A0000934A000002000000694A00009F4A000001000000AB4A000002000000BE4A0000C54A000001000000CF4A000002000000DF4A0000C54A000001000000E64A000002000000EF4A0000C54A000002000000F64A00000A4B000002000000134B0000C54A0000204D00000000000000000000000000002C4D0000000000000000000000000000384D0000000000000000000000000000444D0000000000000000000000000000504D00000000000000000000000000005C4D0000000000000000000000000000684D0000000000000000000000000000704D0000000000000000000000000000844D0000000000000100000000000000940000007C4D0000844D0000000000000200000000000000960000007C4D000097000000904D0000844D0000000000000100000000000000980000007C4D0000844D00000000000001000000000000009A0000007C4D0000844D00000000000001000000000000009C0000007C4D0000984D00000000000001000000000000009E0000007C4D0000B04D0000000000000100000000000000A0000000A44D0000110000000000000001000000000000000100000090010000700000000200000061000000B00600000300000050000000340800000400000048000000F40B000005000000BB000000340E000006000000110000000C140000012000002C0000002C1600000320000024000000262A0000011000002E000000A82D00000220000090010000402F000004200000110000005E4A000000200000110000001A4B00000520000007000000EC4C0000031000000E000000204D0000062000000F000000BC4D00000010000001000000EC4E0000"
diff --git a/app/src/main/jni/KittyMemory/KittyMemory.cpp b/app/src/main/jni/KittyMemory/KittyMemory.cpp
new file mode 100644
index 0000000..7beb667
--- /dev/null
+++ b/app/src/main/jni/KittyMemory/KittyMemory.cpp
@@ -0,0 +1,149 @@
+//
+// KittyMemory.cpp
+//
+// Created by MJ (Ruit) on 1/1/19.
+//
+
+#include
+#include "KittyMemory.h"
+
+using KittyMemory::Memory_Status;
+using KittyMemory::ProcMap;
+
+
+struct mapsCache {
+ std::string identifier;
+ ProcMap map;
+};
+
+static std::vector __mapsCache;
+static ProcMap findMapInCache(std::string id){
+ ProcMap ret;
+ for(int i = 0; i < __mapsCache.size(); i++){
+ if(__mapsCache[i].identifier.compare(id) == 0){
+ ret = __mapsCache[i].map;
+ break;
+ }
+ }
+ return ret;
+}
+
+
+bool KittyMemory::ProtectAddr(void *addr, size_t length, int protection) {
+ uintptr_t pageStart = _PAGE_START_OF_(addr);
+ uintptr_t pageLen = _PAGE_LEN_OF_(addr, length);
+ return (
+ mprotect(reinterpret_cast(pageStart), pageLen, protection) != -1
+ );
+}
+
+
+Memory_Status KittyMemory::memWrite(void *addr, const void *buffer, size_t len) {
+ if (addr == NULL)
+ return INV_ADDR;
+
+ if (buffer == NULL)
+ return INV_BUF;
+
+ if (len < 1 || len > INT_MAX)
+ return INV_LEN;
+
+ if (!ProtectAddr(addr, len, _PROT_RWX_))
+ return INV_PROT;
+
+ if (memcpy(addr, buffer, len) != NULL && ProtectAddr(addr, len, _PROT_RX_))
+ return SUCCESS;
+
+ return FAILED;
+}
+
+
+Memory_Status KittyMemory::memRead(void *buffer, const void *addr, size_t len) {
+ if (addr == NULL)
+ return INV_ADDR;
+
+ if (buffer == NULL)
+ return INV_BUF;
+
+ if (len < 1 || len > INT_MAX)
+ return INV_LEN;
+
+ if (memcpy(buffer, addr, len) != NULL)
+ return SUCCESS;
+
+ return FAILED;
+}
+
+
+std::string KittyMemory::read2HexStr(const void *addr, size_t len) {
+ char temp[len];
+ memset(temp, 0, len);
+
+ const size_t bufferLen = len * 2 + 1;
+ char buffer[bufferLen];
+ memset(buffer, 0, bufferLen);
+
+ std::string ret;
+
+ if (memRead(temp, addr, len) != SUCCESS)
+ return ret;
+
+ for (int i = 0; i < len; i++) {
+ sprintf(&buffer[i * 2], "%02X", (unsigned char) temp[i]);
+ }
+
+ ret += buffer;
+ return ret;
+}
+
+ProcMap KittyMemory::getLibraryMap(const char *libraryName) {
+ ProcMap retMap;
+ char line[512] = {0};
+
+ FILE *fp = fopen(OBFUSCATE("/proc/self/maps"), OBFUSCATE("rt"));
+ if (fp != NULL) {
+ while (fgets(line, sizeof(line), fp)) {
+ if (strstr(line, libraryName)) {
+ char tmpPerms[5] = {0}, tmpDev[12] = {0}, tmpPathname[444] = {0};
+ // parse a line in maps file
+ // (format) startAddress-endAddress perms offset dev inode pathname
+ sscanf(line, "%llx-%llx %s %ld %s %d %s",
+ (long long unsigned *) &retMap.startAddr,
+ (long long unsigned *) &retMap.endAddr,
+ tmpPerms, &retMap.offset, tmpDev, &retMap.inode, tmpPathname);
+
+ retMap.length = (uintptr_t) retMap.endAddr - (uintptr_t) retMap.startAddr;
+ retMap.perms = tmpPerms;
+ retMap.dev = tmpDev;
+ retMap.pathname = tmpPathname;
+
+ break;
+ }
+ }
+ fclose(fp);
+ }
+ return retMap;
+}
+
+uintptr_t KittyMemory::getAbsoluteAddress(const char *libraryName, uintptr_t relativeAddr, bool useCache) {
+ ProcMap libMap;
+
+ if(useCache){
+ libMap = findMapInCache(libraryName);
+ if(libMap.isValid())
+ return (reinterpret_cast(libMap.startAddr) + relativeAddr);
+ }
+
+ libMap = getLibraryMap(libraryName);
+ if (!libMap.isValid())
+ return 0;
+
+ if(useCache){
+ mapsCache cachedMap;
+ cachedMap.identifier = libraryName;
+ cachedMap.map = libMap;
+ __mapsCache.push_back(cachedMap);
+ }
+
+ return (reinterpret_cast(libMap.startAddr) + relativeAddr);
+}
diff --git a/app/src/main/jni/KittyMemory/KittyMemory.h b/app/src/main/jni/KittyMemory/KittyMemory.h
new file mode 100644
index 0000000..21f0160
--- /dev/null
+++ b/app/src/main/jni/KittyMemory/KittyMemory.h
@@ -0,0 +1,169 @@
+//
+// KittyMemory.hpp
+//
+// Created by MJ (Ruit) on 1/1/19.
+//
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+
+#define _SYS_PAGE_SIZE_ (sysconf(_SC_PAGE_SIZE))
+
+#define _PAGE_START_OF_(x) ((uintptr_t)x & ~(uintptr_t)(_SYS_PAGE_SIZE_ - 1))
+#define _PAGE_END_OF_(x, len) (_PAGE_START_OF_((uintptr_t)x + len - 1))
+#define _PAGE_LEN_OF_(x, len) (_PAGE_END_OF_(x, len) - _PAGE_START_OF_(x) + _SYS_PAGE_SIZE_)
+#define _PAGE_OFFSET_OF_(x) ((uintptr_t)x - _PAGE_START_OF_(x))
+
+#define _PROT_RWX_ (PROT_READ | PROT_WRITE | PROT_EXEC)
+#define _PROT_RX_ (PROT_READ | PROT_EXEC)
+
+
+#define EMPTY_VEC_OFFSET std::vector()
+
+namespace KittyMemory {
+
+ typedef enum {
+ FAILED = 0,
+ SUCCESS = 1,
+ INV_ADDR = 2,
+ INV_LEN = 3,
+ INV_BUF = 4,
+ INV_PROT = 5
+ } Memory_Status;
+
+
+ struct ProcMap {
+ void *startAddr;
+ void *endAddr;
+ size_t length;
+ std::string perms;
+ long offset;
+ std::string dev;
+ int inode;
+ std::string pathname;
+
+ bool isValid() { return (startAddr != NULL && endAddr != NULL && !pathname.empty()); }
+ };
+
+ /*
+ * Changes protection of an address with given length
+ */
+ bool ProtectAddr(void *addr, size_t length, int protection);
+
+ /*
+ * Writes buffer content to an address
+ */
+ Memory_Status memWrite(void *addr, const void *buffer, size_t len);
+
+ /*
+ * Reads an address content into a buffer
+ */
+ Memory_Status memRead(void *buffer, const void *addr, size_t len);
+
+ /*
+ * Reads an address content and returns hex string
+ */
+ std::string read2HexStr(const void *addr, size_t len);
+
+
+ /*
+ * Wrapper to dereference & get value of a multi level pointer
+ * Make sure to use the correct data type!
+ */
+ template
+ Type readMultiPtr(void *ptr, std::vector offsets) {
+ Type defaultVal = {};
+ if (ptr == NULL)
+ return defaultVal;
+
+ uintptr_t finalPtr = reinterpret_cast(ptr);
+ int offsetsSize = offsets.size();
+ if (offsetsSize > 0) {
+ for (int i = 0; finalPtr != 0 && i < offsetsSize; i++) {
+ if (i == (offsetsSize - 1))
+ return *reinterpret_cast(finalPtr + offsets[i]);
+
+ finalPtr = *reinterpret_cast(finalPtr + offsets[i]);
+ }
+ }
+
+ if (finalPtr == 0)
+ return defaultVal;
+
+ return *reinterpret_cast(finalPtr);
+ }
+
+
+ /*
+ * Wrapper to dereference & set value of a multi level pointer
+ * Make sure to use the correct data type!, const objects won't work
+ */
+ template
+ bool writeMultiPtr(void *ptr, std::vector offsets, Type val) {
+ if (ptr == NULL)
+ return false;
+
+ uintptr_t finalPtr = reinterpret_cast(ptr);
+ int offsetsSize = offsets.size();
+ if (offsetsSize > 0) {
+ for (int i = 0; finalPtr != 0 && i < offsetsSize; i++) {
+ if (i == (offsetsSize - 1)) {
+ *reinterpret_cast(finalPtr + offsets[i]) = val;
+ return true;
+ }
+
+ finalPtr = *reinterpret_cast(finalPtr + offsets[i]);
+ }
+ }
+
+ if (finalPtr == 0)
+ return false;
+
+ *reinterpret_cast(finalPtr) = val;
+ return true;
+ }
+
+ /*
+ * Wrapper to dereference & get value of a pointer
+ * Make sure to use the correct data type!
+ */
+ template
+ Type readPtr(void *ptr) {
+ Type defaultVal = {};
+ if (ptr == NULL)
+ return defaultVal;
+
+ return *reinterpret_cast(ptr);
+ }
+
+ /*
+ * Wrapper to dereference & set value of a pointer
+ * Make sure to use the correct data type!, const objects won't work
+ */
+ template
+ bool writePtr(void *ptr, Type val) {
+ if (ptr == NULL)
+ return false;
+
+ *reinterpret_cast(ptr) = val;
+ return true;
+ }
+
+ /*
+ * Gets info of a mapped library in self process
+ */
+ ProcMap getLibraryMap(const char *libraryName);
+
+ /*
+ * Expects a relative address in a library
+ * Returns final absolute address
+ */
+ uintptr_t
+ getAbsoluteAddress(const char *libraryName, uintptr_t relativeAddr, bool useCache = false);
+};
diff --git a/app/src/main/jni/KittyMemory/KittyUtils.cpp b/app/src/main/jni/KittyMemory/KittyUtils.cpp
new file mode 100644
index 0000000..b459287
--- /dev/null
+++ b/app/src/main/jni/KittyMemory/KittyUtils.cpp
@@ -0,0 +1,82 @@
+#include "KittyUtils.h"
+
+static void xtrim(std::string &hex){
+ if(hex.compare(0, 2, "0x") == 0){
+ hex.erase(0, 2);
+ }
+
+ // https://www.techiedelight.com/remove-whitespaces-string-cpp/
+ hex.erase(std::remove_if(hex.begin(), hex.end(), [](char c){
+ return (c == ' ' || c == '\n' || c == '\r' ||
+ c == '\t' || c == '\v' || c == '\f');
+ }),
+ hex.end());
+}
+
+
+bool KittyUtils::validateHexString(std::string &xstr){
+ if(xstr.length() < 2) return false;
+ xtrim(xstr); // first remove spaces
+ if(xstr.length() % 2 != 0) return false;
+ for(size_t i = 0; i < xstr.length(); i++){
+ if(!std::isxdigit((unsigned char)xstr[i])){
+ return false;
+ }
+ }
+ return true;
+}
+
+
+// https://tweex.net/post/c-anything-tofrom-a-hex-string/
+#include
+#include
+
+
+// ------------------------------------------------------------------
+/*!
+ Convert a block of data to a hex string
+*/
+void KittyUtils::toHex(
+ void *const data, //!< Data to convert
+ const size_t dataLength, //!< Length of the data to convert
+ std::string &dest //!< Destination string
+ )
+{
+ unsigned char *byteData = reinterpret_cast(data);
+ std::stringstream hexStringStream;
+
+ hexStringStream << std::hex << std::setfill('0');
+ for(size_t index = 0; index < dataLength; ++index)
+ hexStringStream << std::setw(2) << static_cast(byteData[index]);
+ dest = hexStringStream.str();
+}
+
+
+// ------------------------------------------------------------------
+/*!
+ Convert a hex string to a block of data
+*/
+void KittyUtils::fromHex(
+ const std::string &in, //!< Input hex string
+ void *const data //!< Data store
+ )
+{
+ size_t length = in.length();
+ unsigned char *byteData = reinterpret_cast(data);
+
+ std::stringstream hexStringStream; hexStringStream >> std::hex;
+ for(size_t strIndex = 0, dataIndex = 0; strIndex < length; ++dataIndex)
+ {
+ // Read out and convert the string two characters at a time
+ const char tmpStr[3] = { in[strIndex++], in[strIndex++], 0 };
+
+ // Reset and fill the string stream
+ hexStringStream.clear();
+ hexStringStream.str(tmpStr);
+
+ // Do the conversion
+ int tmpValue = 0;
+ hexStringStream >> tmpValue;
+ byteData[dataIndex] = static_cast(tmpValue);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/jni/KittyMemory/KittyUtils.h b/app/src/main/jni/KittyMemory/KittyUtils.h
new file mode 100644
index 0000000..ffd81c6
--- /dev/null
+++ b/app/src/main/jni/KittyMemory/KittyUtils.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include
+#include
+
+namespace KittyUtils {
+
+ bool validateHexString(std::string &xstr);
+ void toHex(void *const data, const size_t dataLength, std::string &dest);
+ void fromHex(const std::string &in, void *const data);
+
+}
\ No newline at end of file
diff --git a/app/src/main/jni/KittyMemory/MemoryBackup.cpp b/app/src/main/jni/KittyMemory/MemoryBackup.cpp
new file mode 100644
index 0000000..acf7a82
--- /dev/null
+++ b/app/src/main/jni/KittyMemory/MemoryBackup.cpp
@@ -0,0 +1,82 @@
+//
+// MemoryBackup.cpp
+//
+// Created by MJ (Ruit) on 4/19/20.
+//
+
+#include
+#include "MemoryBackup.h"
+
+
+MemoryBackup::MemoryBackup() {
+ _address = 0;
+ _size = 0;
+ _orig_code.clear();
+}
+
+MemoryBackup::MemoryBackup(const char *libraryName, uintptr_t address, size_t backup_size, bool useMapCache) {
+ MemoryBackup();
+
+ if (libraryName == NULL || address == 0 || backup_size < 1)
+ return;
+
+ _address = KittyMemory::getAbsoluteAddress(libraryName, address, useMapCache);
+ if(_address == 0) return;
+
+ _size = backup_size;
+
+ _orig_code.resize(backup_size);
+
+ // backup current content
+ KittyMemory::memRead(&_orig_code[0], reinterpret_cast(_address), backup_size);
+}
+
+
+MemoryBackup::MemoryBackup(uintptr_t absolute_address, size_t backup_size) {
+ MemoryBackup();
+
+ if (absolute_address == 0 || backup_size < 1)
+ return;
+
+ _address = absolute_address;
+
+ _size = backup_size;
+
+ _orig_code.resize(backup_size);
+
+ // backup current content
+ KittyMemory::memRead(&_orig_code[0], reinterpret_cast(_address), backup_size);
+}
+
+ MemoryBackup::~MemoryBackup() {
+ // clean up
+ _orig_code.clear();
+ }
+
+
+ bool MemoryBackup::isValid() const {
+ return (_address != 0 && _size > 0
+ && _orig_code.size() == _size);
+ }
+
+ size_t MemoryBackup::get_BackupSize() const{
+ return _size;
+ }
+
+ uintptr_t MemoryBackup::get_TargetAddress() const{
+ return _address;
+ }
+
+ bool MemoryBackup::Restore() {
+ if (!isValid()) return false;
+ return KittyMemory::memWrite(reinterpret_cast(_address), &_orig_code[0], _size) == Memory_Status::SUCCESS;
+ }
+
+ std::string MemoryBackup::get_CurrBytes() {
+ if (!isValid())
+ _hexString = std::string(OBFUSCATE("0xInvalid"));
+ else
+ _hexString = KittyMemory::read2HexStr(reinterpret_cast(_address), _size);
+
+ return _hexString;
+ }
diff --git a/app/src/main/jni/KittyMemory/MemoryBackup.h b/app/src/main/jni/KittyMemory/MemoryBackup.h
new file mode 100644
index 0000000..fde1ea4
--- /dev/null
+++ b/app/src/main/jni/KittyMemory/MemoryBackup.h
@@ -0,0 +1,66 @@
+//
+// MemoryBackup.h
+//
+// Created by MJ (Ruit) on 4/19/20.
+//
+
+#pragma once
+
+#include
+
+#include "KittyMemory.h"
+using KittyMemory::Memory_Status;
+using KittyMemory::ProcMap;
+
+
+class MemoryBackup {
+private:
+ uintptr_t _address;
+ size_t _size;
+
+ std::vector _orig_code;
+
+ std::string _hexString;
+
+public:
+ MemoryBackup();
+
+ /*
+ * expects library name and relative address
+ */
+ MemoryBackup(const char *libraryName, uintptr_t address, size_t backup_size, bool useMapCache=true);
+
+ /*
+ * expects absolute address
+ */
+ MemoryBackup(uintptr_t absolute_address, size_t backup_size);
+
+
+ ~MemoryBackup();
+
+
+ /*
+ * Validate patch
+ */
+ bool isValid() const;
+
+
+ size_t get_BackupSize() const;
+
+ /*
+ * Returns pointer to the target address
+ */
+ uintptr_t get_TargetAddress() const;
+
+
+ /*
+ * Restores backup code
+ */
+ bool Restore();
+
+
+ /*
+ * Returns current target address bytes as hex string
+ */
+ std::string get_CurrBytes();
+};
diff --git a/app/src/main/jni/KittyMemory/MemoryPatch.cpp b/app/src/main/jni/KittyMemory/MemoryPatch.cpp
new file mode 100644
index 0000000..74b6874
--- /dev/null
+++ b/app/src/main/jni/KittyMemory/MemoryPatch.cpp
@@ -0,0 +1,141 @@
+//
+// MemoryPatch.cpp
+//
+// Created by MJ (Ruit) on 1/1/19.
+//
+
+#include
+#include "MemoryPatch.h"
+#include "Includes/Logger.h"
+
+MemoryPatch::MemoryPatch() {
+ _address = 0;
+ _size = 0;
+ _orig_code.clear();
+ _patch_code.clear();
+}
+
+MemoryPatch::MemoryPatch(const char *libraryName, uintptr_t address,
+ const void *patch_code, size_t patch_size, bool useMapCache) {
+ MemoryPatch();
+
+ if (libraryName == NULL || address == 0 || patch_code == NULL || patch_size < 1)
+ return;
+
+ _address = KittyMemory::getAbsoluteAddress(libraryName, address, useMapCache);
+ if (_address == 0) return;
+
+ _size = patch_size;
+
+ _orig_code.resize(patch_size);
+ _patch_code.resize(patch_size);
+
+ // initialize patch & backup current content
+ KittyMemory::memRead(&_patch_code[0], patch_code, patch_size);
+ KittyMemory::memRead(&_orig_code[0], reinterpret_cast(_address), patch_size);
+}
+
+MemoryPatch::MemoryPatch(uintptr_t absolute_address,
+ const void *patch_code, size_t patch_size) {
+ MemoryPatch();
+
+ if (absolute_address == 0 || patch_code == NULL || patch_size < 1)
+ return;
+
+ _address = absolute_address;
+ _size = patch_size;
+
+ _orig_code.resize(patch_size);
+ _patch_code.resize(patch_size);
+
+ // initialize patch & backup current content
+ KittyMemory::memRead(&_patch_code[0], patch_code, patch_size);
+ KittyMemory::memRead(&_orig_code[0], reinterpret_cast(_address), patch_size);
+}
+
+MemoryPatch::~MemoryPatch() {
+ // clean up
+ _orig_code.clear();
+ _patch_code.clear();
+}
+
+MemoryPatch MemoryPatch::createWithHex(const char *libraryName, uintptr_t address,
+ std::string hex, bool useMapCache) {
+ MemoryPatch patch;
+
+ if (libraryName == NULL || address == 0 || !KittyUtils::validateHexString(hex))
+ return patch;
+
+ patch._address = KittyMemory::getAbsoluteAddress(libraryName, address, useMapCache);
+ if (patch._address == 0) return patch;
+
+ patch._size = hex.length() / 2;
+
+ patch._orig_code.resize(patch._size);
+ patch._patch_code.resize(patch._size);
+
+ // initialize patch
+ KittyUtils::fromHex(hex, &patch._patch_code[0]);
+
+ // backup current content
+ KittyMemory::memRead(&patch._orig_code[0], reinterpret_cast(patch._address),
+ patch._size);
+ return patch;
+}
+
+MemoryPatch MemoryPatch::createWithHex(uintptr_t absolute_address, std::string hex) {
+ MemoryPatch patch;
+
+ if (absolute_address == 0 || !KittyUtils::validateHexString(hex))
+ return patch;
+
+ patch._address = absolute_address;
+ patch._size = hex.length() / 2;
+
+ patch._orig_code.resize(patch._size);
+ patch._patch_code.resize(patch._size);
+
+ // initialize patch
+ KittyUtils::fromHex(hex, &patch._patch_code[0]);
+
+ // backup current content
+ KittyMemory::memRead(&patch._orig_code[0], reinterpret_cast(patch._address),
+ patch._size);
+ return patch;
+}
+
+bool MemoryPatch::isValid() const {
+ return (_address != 0 && _size > 0
+ && _orig_code.size() == _size && _patch_code.size() == _size);
+}
+
+size_t MemoryPatch::get_PatchSize() const {
+ return _size;
+}
+
+uintptr_t MemoryPatch::get_TargetAddress() const {
+ return _address;
+}
+
+bool MemoryPatch::Restore() {
+ if (!isValid()) return false;
+ //LOGI("Restore %i", isLeeched);
+ return KittyMemory::memWrite(reinterpret_cast(_address), &_orig_code[0], _size) ==
+ Memory_Status::SUCCESS;
+}
+
+bool MemoryPatch::Modify() {
+ if (!isValid()) return false;
+ //LOGI("Modify");
+ return (KittyMemory::memWrite(reinterpret_cast(_address), &_patch_code[0], _size) ==
+ Memory_Status::SUCCESS);
+}
+
+std::string MemoryPatch::get_CurrBytes() {
+ if (!isValid())
+ _hexString = std::string(OBFUSCATE("0xInvalid"));
+ else
+ _hexString = KittyMemory::read2HexStr(reinterpret_cast(_address), _size);
+
+ return _hexString;
+}
diff --git a/app/src/main/jni/KittyMemory/MemoryPatch.h b/app/src/main/jni/KittyMemory/MemoryPatch.h
new file mode 100644
index 0000000..f63d91c
--- /dev/null
+++ b/app/src/main/jni/KittyMemory/MemoryPatch.h
@@ -0,0 +1,80 @@
+//
+// MemoryPatch.h
+//
+// Created by MJ (Ruit) on 1/1/19.
+//
+
+#pragma once
+
+#include
+#include "KittyUtils.h"
+#include "KittyMemory.h"
+using KittyMemory::Memory_Status;
+using KittyMemory::ProcMap;
+
+class MemoryPatch {
+private:
+ uintptr_t _address;
+ size_t _size;
+
+ std::vector _orig_code;
+ std::vector _patch_code;
+
+ std::string _hexString;
+
+public:
+ MemoryPatch();
+
+ /*
+ * expects library name and relative address
+ */
+ MemoryPatch(const char *libraryName, uintptr_t address,
+ const void *patch_code, size_t patch_size, bool useMapCache=true);
+
+
+ /*
+ * expects absolute address
+ */
+ MemoryPatch(uintptr_t absolute_address,
+ const void *patch_code, size_t patch_size);
+
+
+ ~MemoryPatch();
+
+ /*
+ * compatible hex format (0xffff & ffff & ff ff)
+ */
+ static MemoryPatch createWithHex(const char *libraryName, uintptr_t address, std::string hex, bool useMapCache=true);
+ static MemoryPatch createWithHex(uintptr_t absolute_address, std::string hex);
+
+ /*
+ * Validate patch
+ */
+ bool isValid() const;
+
+
+ size_t get_PatchSize() const;
+
+ /*
+ * Returns pointer to the target address
+ */
+ uintptr_t get_TargetAddress() const;
+
+
+ /*
+ * Restores patch to original value
+ */
+ bool Restore();
+
+
+ /*
+ * Applies patch modifications to target address
+ */
+ bool Modify();
+
+
+ /*
+ * Returns current patch target address bytes as hex string
+ */
+ std::string get_CurrBytes();
+};
diff --git a/app/src/main/jni/Main.cpp b/app/src/main/jni/Main.cpp
new file mode 100644
index 0000000..b69c000
--- /dev/null
+++ b/app/src/main/jni/Main.cpp
@@ -0,0 +1,107 @@
+//
+// Created by aantik on 2/1/2026.
+//
+
+//Main.cpp
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "Includes/Logger.h"
+#include "Includes/obfuscate.h"
+#include "Includes/Utils.h"
+
+#include "KittyMemory/MemoryPatch.h"
+#include "Menu.h"
+
+
+#define targetLibName OBFUSCATE("libFileA.so")
+
+#include "Includes/Macros.h"
+
+#include "JavaGPP/Interface/Interface.h"
+
+
+struct My_Patches {
+ MemoryPatch xs;
+} hexPatches;
+
+
+
+void *hack_thread(void *) {
+ LOGI(OBFUSCATE("pthread created"));
+
+ do {
+ sleep(1);
+ } while (!isLibraryLoaded(targetLibName));
+
+
+ LOGI(OBFUSCATE("%s has been loaded"), (const char *) targetLibName);
+
+#if defined(__aarch64__)
+
+
+
+#else
+
+
+#endif
+
+ return NULL;
+}
+
+jobjectArray getFeatureList(JNIEnv *env, jobject context) {
+ jobjectArray ret;
+
+ const char *features[] = {
+ OBFUSCATE("Category_The Category"), //Not counted
+ OBFUSCATE("Toggle_The toggle"),
+ };
+
+
+ int Total_Feature = (sizeof features / sizeof features[0]);
+ ret = (jobjectArray) env->NewObjectArray(Total_Feature, env->FindClass(OBFUSCATE("java/lang/String")),env->NewStringUTF(""));
+ for (int i = 0; i < Total_Feature; i++) env->SetObjectArrayElement(ret, i, env->NewStringUTF(features[i]));
+ pthread_t ptid;
+
+ return (ret);
+}
+
+void Changes(JNIEnv *env, jclass clazz, jobject obj, jint featNum, jstring featName, jint value, jboolean boolean, jstring str) {
+
+ switch (featNum) {
+ case 1:
+
+ break;
+ }
+}
+
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
+ antik = vm;
+ InterfaceMethods::Icon = (void*) Icon;
+ InterfaceMethods::IconWebViewData = (void *) IconWebViewData;
+ InterfaceMethods::Changes = (void *) Changes;
+ InterfaceMethods::getFeatureList = (void *) getFeatureList;
+ InterfaceMethods::settingsList = (void *) settingsList;
+ InterfaceMethods::setTitleText = (void *) setTitleText;
+ InterfaceMethods::setHeadingText = (void *) setHeadingText;
+
+ binJava();
+ return JNI_VERSION_1_6;
+}
+
+
+
+__attribute__((constructor))
+void lib_main() {
+ pthread_t ptid;
+ pthread_create(&ptid, NULL, hack_thread, NULL);
+}
diff --git a/app/src/main/jni/Menu.h b/app/src/main/jni/Menu.h
new file mode 100644
index 0000000..4c762d2
--- /dev/null
+++ b/app/src/main/jni/Menu.h
@@ -0,0 +1,55 @@
+void setText(JNIEnv *env, jobject obj, const char* text){
+
+ jclass html = (*env).FindClass(OBFUSCATE("android/text/Html"));
+ jmethodID fromHtml = (*env).GetStaticMethodID(html, OBFUSCATE("fromHtml"), OBFUSCATE("(Ljava/lang/String;)Landroid/text/Spanned;"));
+
+
+ jclass textView = (*env).FindClass(OBFUSCATE("android/widget/TextView"));
+ jmethodID setText = (*env).GetMethodID(textView, OBFUSCATE("setText"), OBFUSCATE("(Ljava/lang/CharSequence;)V"));
+
+ jstring jstr = (*env).NewStringUTF(text);
+ (*env).CallVoidMethod(obj, setText, (*env).CallStaticObjectMethod(html, fromHtml, jstr));
+}
+
+void setTitleText(JNIEnv *env, jobject thiz, jobject obj) {
+ setText(env, obj, OBFUSCATE("Modded by (yourname)"));
+
+
+}
+
+void setHeadingText(JNIEnv *env, jobject thiz, jobject obj) {
+ setText(env, obj, OBFUSCATE(""));
+}
+
+jstring Icon(JNIEnv *env, jobject thiz) {
+ return env->NewStringUTF(
+ OBFUSCATE("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"));
+}
+
+jstring IconWebViewData(JNIEnv *env, jobject thiz) {
+ return NULL;
+}
+
+jobjectArray settingsList(JNIEnv *env, jobject activityObject) {
+ jobjectArray ret;
+
+ const char *features[] = {
+ OBFUSCATE("Category_Settings"),
+ OBFUSCATE("-1_Toggle_Save feature preferences"),
+ };
+
+ int Total_Feature = (sizeof features /
+ sizeof features[0]);
+ ret = (jobjectArray)
+ env->NewObjectArray(Total_Feature, env->FindClass(OBFUSCATE("java/lang/String")),
+ env->NewStringUTF(""));
+ int i;
+ for (i = 0; i < Total_Feature; i++)
+ env->SetObjectArrayElement(ret, i, env->NewStringUTF(features[i]));
+
+
+ return (ret);
+}
diff --git a/app/src/main/jni/Substrate/Buffer.hpp b/app/src/main/jni/Substrate/Buffer.hpp
new file mode 100644
index 0000000..34d9df3
--- /dev/null
+++ b/app/src/main/jni/Substrate/Buffer.hpp
@@ -0,0 +1,38 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011 Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate. If not, see .
+**/
+/* }}} */
+
+#ifndef SUBSTRATE_BUFFER_HPP
+#define SUBSTRATE_BUFFER_HPP
+
+#include
+
+template
+_disused static _finline void MSWrite(uint8_t *&buffer, Type_ value) {
+ *reinterpret_cast(buffer) = value;
+ buffer += sizeof(Type_);
+}
+
+_disused static _finline void MSWrite(uint8_t *&buffer, uint8_t *data, size_t size) {
+ memcpy(buffer, data, size);
+ buffer += size;
+}
+
+#endif//SUBSTRATE_BUFFER_HPP
diff --git a/app/src/main/jni/Substrate/CydiaSubstrate.h b/app/src/main/jni/Substrate/CydiaSubstrate.h
new file mode 100644
index 0000000..bb806aa
--- /dev/null
+++ b/app/src/main/jni/Substrate/CydiaSubstrate.h
@@ -0,0 +1,152 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011 Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate. If not, see .
+**/
+/* }}} */
+
+#ifndef SUBSTRATE_H_
+#define SUBSTRATE_H_
+
+#ifdef __APPLE__
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include
+#ifdef __cplusplus
+}
+#endif
+
+#include
+#include
+#endif
+
+#include
+#include
+
+#define _finline \
+ inline __attribute__((__always_inline__))
+#define _disused \
+ __attribute__((__unused__))
+
+#define _extern \
+ extern "C" __attribute__((__visibility__("default")))
+
+#ifdef __cplusplus
+#define _default(value) = value
+#else
+#define _default(value)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool MSHookProcess(pid_t pid, const char *library);
+
+typedef const void *MSImageRef;
+
+MSImageRef MSGetImageByName(const char *file);
+void *MSFindSymbol(MSImageRef image, const char *name);
+
+void MSHookFunction(void *symbol, void *replace, void **result);
+
+#ifdef __APPLE__
+#ifdef __arm__
+__attribute__((__deprecated__))
+IMP MSHookMessage(Class _class, SEL sel, IMP imp, const char *prefix _default(NULL));
+#endif
+void MSHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result);
+#endif
+
+#ifdef SubstrateInternal
+typedef void *SubstrateAllocatorRef;
+typedef struct __SubstrateProcess *SubstrateProcessRef;
+typedef struct __SubstrateMemory *SubstrateMemoryRef;
+
+SubstrateProcessRef SubstrateProcessCreate(SubstrateAllocatorRef allocator, pid_t pid);
+void SubstrateProcessRelease(SubstrateProcessRef process);
+
+SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size);
+void SubstrateMemoryRelease(SubstrateMemoryRef memory);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+
+#ifdef SubstrateInternal
+struct SubstrateHookMemory {
+ SubstrateMemoryRef handle_;
+
+ SubstrateHookMemory(SubstrateProcessRef process, void *data, size_t size) :
+ handle_(SubstrateMemoryCreate(NULL, NULL, data, size))
+ {
+ }
+
+ ~SubstrateHookMemory() {
+ if (handle_ != NULL)
+ SubstrateMemoryRelease(handle_);
+ }
+};
+#endif
+
+
+template
+static inline void MSHookFunction(Type_ *symbol, Type_ *replace, Type_ **result) {
+ MSHookFunction(
+ reinterpret_cast(symbol),
+ reinterpret_cast(replace),
+ reinterpret_cast(result)
+ );
+}
+
+template
+static inline void MSHookFunction(Type_ *symbol, Type_ *replace) {
+ return MSHookFunction(symbol, replace, reinterpret_cast(NULL));
+}
+
+template
+static inline void MSHookSymbol(Type_ *&value, const char *name, MSImageRef image = NULL) {
+ value = reinterpret_cast(MSFindSymbol(image, name));
+}
+
+template
+static inline void MSHookFunction(const char *name, Type_ *replace, Type_ **result = NULL) {
+ Type_ *symbol;
+ MSHookSymbol(symbol, name);
+ return MSHookFunction(symbol, replace, result);
+}
+
+#endif
+
+#define MSHook(type, name, args...) \
+ _disused static type (*_ ## name)(args); \
+ static type $ ## name(args)
+
+#ifdef __cplusplus
+#define MSHake(name) \
+ &$ ## name, &_ ## name
+#else
+#define MSHake(name) \
+ &$ ## name, (void **) &_ ## name
+#endif
+
+
+#endif//SUBSTRATE_H_
diff --git a/app/src/main/jni/Substrate/SubstrateARM.hpp b/app/src/main/jni/Substrate/SubstrateARM.hpp
new file mode 100644
index 0000000..02b3028
--- /dev/null
+++ b/app/src/main/jni/Substrate/SubstrateARM.hpp
@@ -0,0 +1,65 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011 Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate. If not, see .
+**/
+/* }}} */
+
+#ifndef SUBSTRATE_ARM_HPP
+#define SUBSTRATE_ARM_HPP
+
+enum A$r {
+ A$r0, A$r1, A$r2, A$r3,
+ A$r4, A$r5, A$r6, A$r7,
+ A$r8, A$r9, A$r10, A$r11,
+ A$r12, A$r13, A$r14, A$r15,
+ A$sp = A$r13,
+ A$lr = A$r14,
+ A$pc = A$r15
+};
+
+enum A$c {
+ A$eq, A$ne, A$cs, A$cc,
+ A$mi, A$pl, A$vs, A$vc,
+ A$hi, A$ls, A$ge, A$lt,
+ A$gt, A$le, A$al,
+ A$hs = A$cs,
+ A$lo = A$cc
+};
+
+#define A$mrs_rm_cpsr(rd) /* mrs rd, cpsr */ \
+ (0xe10f0000 | ((rd) << 12))
+#define A$msr_cpsr_f_rm(rm) /* msr cpsr_f, rm */ \
+ (0xe128f000 | (rm))
+#define A$ldr_rd_$rn_im$(rd, rn, im) /* ldr rd, [rn, #im] */ \
+ (0xe5100000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs((int)(im)))
+#define A$str_rd_$rn_im$(rd, rn, im) /* sr rd, [rn, #im] */ \
+ (0xe5000000 | ((im) < 0 ? 0 : 1 << 23) | ((rn) << 16) | ((rd) << 12) | abs(im))
+#define A$sub_rd_rn_$im(rd, rn, im) /* sub, rd, rn, #im */ \
+ (0xe2400000 | ((rn) << 16) | ((rd) << 12) | (im & 0xff))
+#define A$blx_rm(rm) /* blx rm */ \
+ (0xe12fff30 | (rm))
+#define A$mov_rd_rm(rd, rm) /* mov rd, rm */ \
+ (0xe1a00000 | ((rd) << 12) | (rm))
+#define A$ldmia_sp$_$rs$(rs) /* ldmia sp!, {rs} */ \
+ (0xe8b00000 | (A$sp << 16) | (rs))
+#define A$stmdb_sp$_$rs$(rs) /* stmdb sp!, {rs} */ \
+ (0xe9200000 | (A$sp << 16) | (rs))
+#define A$stmia_sp$_$r0$ 0xe8ad0001 /* stmia sp!, {r0} */
+#define A$bx_r0 0xe12fff10 /* bx r0 */
+
+#endif//SUBSTRATE_ARM_HPP
diff --git a/app/src/main/jni/Substrate/SubstrateDebug.cpp b/app/src/main/jni/Substrate/SubstrateDebug.cpp
new file mode 100644
index 0000000..3f5a168
--- /dev/null
+++ b/app/src/main/jni/Substrate/SubstrateDebug.cpp
@@ -0,0 +1,97 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011 Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate. If not, see .
+**/
+/* }}} */
+
+#include "SubstrateHook.h"
+#include "SubstrateDebug.hpp"
+
+#include
+#include
+#include
+#include
+
+_extern bool MSDebug;
+bool MSDebug = false;
+
+static char _MSHexChar(uint8_t value) {
+ return value < 0x20 || value >= 0x80 ? '.' : value;
+}
+
+#define HexWidth_ 16
+#define HexDepth_ 4
+
+void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark) {
+ const uint8_t *data((const uint8_t *) vdata);
+
+ size_t i(0), j;
+
+ char d[256];
+ size_t b(0);
+ d[0] = '\0';
+
+ while (i != size) {
+ if (i % HexWidth_ == 0) {
+ if (mark != NULL)
+ b += sprintf(d + b, OBFUSCATE("\n[%s] "), mark);
+ b += sprintf(d + b, OBFUSCATE("0x%.3zx:"), i);
+ }
+
+ b += sprintf(d + b, " ");
+
+ for (size_t q(0); q != stride; ++q)
+ b += sprintf(d + b, OBFUSCATE("%.2x"), data[i + stride - q - 1]);
+
+ i += stride;
+
+ for (size_t q(1); q != stride; ++q)
+ b += sprintf(d + b, " ");
+
+ if (i % HexDepth_ == 0)
+ b += sprintf(d + b, " ");
+
+ if (i % HexWidth_ == 0) {
+ b += sprintf(d + b, " ");
+ for (j = i - HexWidth_; j != i; ++j)
+ b += sprintf(d + b, "%c", _MSHexChar(data[j]));
+
+ lprintf("%s", d);
+ b = 0;
+ d[0] = '\0';
+ }
+ }
+
+ if (i % HexWidth_ != 0) {
+ for (j = i % HexWidth_; j != HexWidth_; ++j)
+ b += sprintf(d + b, " ");
+ for (j = 0; j != (HexWidth_ - i % HexWidth_ + HexDepth_ - 1) / HexDepth_; ++j)
+ b += sprintf(d + b, " ");
+ b += sprintf(d + b, " ");
+ for (j = i / HexWidth_ * HexWidth_; j != i; ++j)
+ b += sprintf(d + b, OBFUSCATE("%c"), _MSHexChar(data[j]));
+
+ // lprintf("%s", d);
+ b = 0;
+ d[0] = '\0';
+ }
+}
+
+void MSLogHex(const void *vdata, size_t size, const char *mark) {
+ return MSLogHexEx(vdata, size, 1, mark);
+}
diff --git a/app/src/main/jni/Substrate/SubstrateDebug.hpp b/app/src/main/jni/Substrate/SubstrateDebug.hpp
new file mode 100644
index 0000000..9c554c8
--- /dev/null
+++ b/app/src/main/jni/Substrate/SubstrateDebug.hpp
@@ -0,0 +1,33 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011 Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate. If not, see .
+**/
+/* }}} */
+
+#ifndef SUBSTRATE_DEBUG_HPP
+#define SUBSTRATE_DEBUG_HPP
+
+#include "SubstrateLog.hpp"
+#define lprintf(format, ...) \
+ MSLog(MSLogLevelNotice, format, ## __VA_ARGS__)
+
+extern "C" bool MSDebug;
+void MSLogHexEx(const void *vdata, size_t size, size_t stride, const char *mark = 0);
+void MSLogHex(const void *vdata, size_t size, const char *mark = 0);
+
+#endif//SUBSTRATE_DEBUG_HPP
diff --git a/app/src/main/jni/Substrate/SubstrateHook.cpp b/app/src/main/jni/Substrate/SubstrateHook.cpp
new file mode 100644
index 0000000..a815f47
--- /dev/null
+++ b/app/src/main/jni/Substrate/SubstrateHook.cpp
@@ -0,0 +1,951 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011 Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate. If not, see .
+**/
+/* }}} */
+
+#define SubstrateInternal
+
+#include "CydiaSubstrate.h"
+
+#include
+
+#define _trace() do { \
+ MSLog(MSLogLevelNotice, "_trace(%u)", __LINE__); \
+} while (false)
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include "hde64.h"
+
+#endif
+
+#include "SubstrateDebug.hpp"
+
+#include
+#include
+#include
+#include
+
+#ifdef __arm__
+/* WebCore (ARM) PC-Relative:
+X 1 ldr r*,[pc,r*] !=
+ 2 fldd d*,[pc,#*]
+X 5 str r*,[pc,r*] !=
+ 8 flds s*,[pc,#*]
+ 400 ldr r*,[pc,r*] ==
+ 515 add r*, pc,r* ==
+X 4790 ldr r*,[pc,#*] */
+
+// x=0; while IFS= read -r line; do if [[ ${#line} -ne 0 && $line == +([^\;]): ]]; then x=2; elif [[ $line == ' +'* && $x -ne 0 ]]; then ((--x)); echo "$x${line}"; fi; done WebCore.pc
+// grep pc WebCore.pc | cut -c 40- | sed -Ee 's/^ldr *(ip|r[0-9]*),\[pc,\#0x[0-9a-f]*\].*/ ldr r*,[pc,#*]/;s/^add *r[0-9]*,pc,r[0-9]*.*/ add r*, pc,r*/;s/^(st|ld)r *r([0-9]*),\[pc,r([0-9]*)\].*/ \1r r\2,[pc,r\3]/;s/^fld(s|d) *(s|d)[0-9]*,\[pc,#0x[0-9a-f]*].*/fld\1 \2*,[pc,#*]/' | sort | uniq -c | sort -n
+
+#include "SubstrateARM.hpp"
+
+#define T$Label(l, r) \
+ (((r) - (l)) * 2 - 4 + ((l) % 2 == 0 ? 0 : 2))
+
+#define T$pop_$r0$ 0xbc01 // pop {r0}
+#define T$b(im) /* b im */ \
+ (0xde00 | (im & 0xff))
+#define T$blx(rm) /* blx rm */ \
+ (0x4780 | (rm << 3))
+#define T$bx(rm) /* bx rm */ \
+ (0x4700 | (rm << 3))
+#define T$nop /* nop */ \
+ (0x46c0)
+
+#define T$add_rd_rm(rd, rm) /* add rd, rm */ \
+ (0x4400 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7))
+#define T$push_r(r) /* push r... */ \
+ (0xb400 | (((r) & (1 << A$lr)) >> A$lr << 8) | ((r) & 0xff))
+#define T$pop_r(r) /* pop r... */ \
+ (0xbc00 | (((r) & (1 << A$pc)) >> A$pc << 8) | ((r) & 0xff))
+#define T$mov_rd_rm(rd, rm) /* mov rd, rm */ \
+ (0x4600 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7))
+#define T$ldr_rd_$rn_im_4$(rd, rn, im) /* ldr rd, [rn, #im * 4] */ \
+ (0x6800 | (((im) & 0x1f) << 6) | ((rn) << 3) | (rd))
+#define T$ldr_rd_$pc_im_4$(rd, im) /* ldr rd, [PC, #im * 4] */ \
+ (0x4800 | ((rd) << 8) | ((im) & 0xff))
+#define T$cmp_rn_$im(rn, im) /* cmp rn, #im */ \
+ (0x2000 | ((rn) << 8) | ((im) & 0xff))
+#define T$it$_cd(cd, ms) /* it, cd */ \
+ (0xbf00 | ((cd) << 4) | (ms))
+#define T$cbz$_rn_$im(op,rn,im) /* cbz rn, #im */ \
+ (0xb100 | ((op) << 11) | (((im) & 0x40) >> 6 << 9) | (((im) & 0x3e) >> 1 << 3) | (rn))
+#define T$b$_$im(cond,im) /* b #im */ \
+ (cond == A$al ? 0xe000 | (((im) >> 1) & 0x7ff) : 0xd000 | ((cond) << 8) | (((im) >> 1) & 0xff))
+
+#define T1$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \
+ (0xf850 | ((im < 0 ? 0 : 1) << 7) | (rn))
+#define T2$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \
+ (((rt) << 12) | abs((int)(im)))
+
+#define T1$mrs_rd_apsr(rd) /* mrs rd, apsr */ \
+ (0xf3ef)
+#define T2$mrs_rd_apsr(rd) /* mrs rd, apsr */ \
+ (0x8000 | ((rd) << 8))
+
+#define T1$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \
+ (0xf380 | (rn))
+#define T2$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \
+ (0x8c00)
+#define T$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \
+ (T2$msr_apsr_nzcvqg_rn(rn) << 16 | T1$msr_apsr_nzcvqg_rn(rn))
+
+static inline bool A$pcrel$r(uint32_t ic) {
+ return (ic & 0x0c000000) == 0x04000000 && (ic & 0xf0000000) != 0xf0000000 && (ic & 0x000f0000) == 0x000f0000;
+}
+
+static inline bool T$32bit$i(uint16_t ic) {
+ return ((ic & 0xe000) == 0xe000 && (ic & 0x1800) != 0x0000);
+}
+
+static inline bool T$pcrel$cbz(uint16_t ic) {
+ return (ic & 0xf500) == 0xb100;
+}
+
+static inline bool T$pcrel$b(uint16_t ic) {
+ return (ic & 0xf000) == 0xd000 && (ic & 0x0e00) != 0x0e00;
+}
+
+static inline bool T2$pcrel$b(uint16_t *ic) {
+ return (ic[0] & 0xf800) == 0xf000 && (((ic[1] & 0xd000) == 0x9000 || (ic[1] & 0xd000) == 0x8000) && (ic[0] & 0x0380) != 0x0380);
+}
+
+static inline bool T$pcrel$bl(uint16_t *ic) {
+ return (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0xd000 || (ic[1] & 0xd001) == 0xc000);
+}
+
+static inline bool T$pcrel$ldr(uint16_t ic) {
+ return (ic & 0xf800) == 0x4800;
+}
+
+static inline bool T$pcrel$add(uint16_t ic) {
+ return (ic & 0xff78) == 0x4478;
+}
+
+static inline bool T$pcrel$ldrw(uint16_t ic) {
+ return (ic & 0xff7f) == 0xf85f;
+}
+
+static size_t MSGetInstructionWidthThumb(void *start) {
+ uint16_t *thumb(reinterpret_cast(start));
+ return T$32bit$i(thumb[0]) ? 4 : 2;
+}
+
+static size_t MSGetInstructionWidthARM(void *start) {
+ return 4;
+}
+
+extern "C" size_t MSGetInstructionWidth(void *start) {
+ if ((reinterpret_cast(start) & 0x1) == 0)
+ return MSGetInstructionWidthARM(start);
+ else
+ return MSGetInstructionWidthThumb(reinterpret_cast(reinterpret_cast(start) & ~0x1));
+}
+
+static size_t SubstrateHookFunctionThumb(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
+ if (symbol == NULL)
+ return 0;
+printf(OBFUSCATE("SubstrateHookFunctionThumb\n"));
+ uint16_t *area(reinterpret_cast(symbol));
+
+ unsigned align((reinterpret_cast(area) & 0x2) == 0 ? 0 : 1);
+ uint16_t *thumb(area + align);
+
+ uint32_t *arm(reinterpret_cast(thumb + 2));
+ uint16_t *trail(reinterpret_cast(arm + 2));
+
+ if (
+ (align == 0 || area[0] == T$nop) &&
+ thumb[0] == T$bx(A$pc) &&
+ thumb[1] == T$nop &&
+ arm[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)
+ ) {
+ if (result != NULL)
+ *result = reinterpret_cast(arm[1]);
+
+ SubstrateHookMemory code(process, arm + 1, sizeof(uint32_t) * 1);
+
+ arm[1] = reinterpret_cast(replace);
+
+ return sizeof(arm[0]);
+ }
+
+ size_t required((trail - area) * sizeof(uint16_t));
+
+ size_t used(0);
+ while (used < required)
+ used += MSGetInstructionWidthThumb(reinterpret_cast(area) + used);
+ used = (used + sizeof(uint16_t) - 1) / sizeof(uint16_t) * sizeof(uint16_t);
+
+ size_t blank((used - required) / sizeof(uint16_t));
+
+ uint16_t backup[used / sizeof(uint16_t)];
+ memcpy(backup, area, used);
+
+ if (MSDebug) {
+ char name[16];
+ sprintf(name, "%p", area);
+ MSLogHexEx(area, used + sizeof(uint16_t), 2, name);
+ }
+
+ if (result != NULL) {
+
+ size_t length(used);
+ for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset)
+ if (T$pcrel$ldr(backup[offset]))
+ length += 3 * sizeof(uint16_t);
+ else if (T$pcrel$b(backup[offset]))
+ length += 6 * sizeof(uint16_t);
+ else if (T2$pcrel$b(backup + offset)) {
+ length += 5 * sizeof(uint16_t);
+ ++offset;
+ } else if (T$pcrel$bl(backup + offset)) {
+ length += 5 * sizeof(uint16_t);
+ ++offset;
+ } else if (T$pcrel$cbz(backup[offset])) {
+ length += 16 * sizeof(uint16_t);
+ } else if (T$pcrel$ldrw(backup[offset])) {
+ length += 4 * sizeof(uint16_t);
+ ++offset;
+ } else if (T$pcrel$add(backup[offset]))
+ length += 6 * sizeof(uint16_t);
+ else if (T$32bit$i(backup[offset]))
+ ++offset;
+
+ unsigned pad((length & 0x2) == 0 ? 0 : 1);
+ length += (pad + 2) * sizeof(uint16_t) + 2 * sizeof(uint32_t);
+
+ uint16_t *buffer(reinterpret_cast(mmap(
+ NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0
+ )));
+
+ if (buffer == MAP_FAILED) {
+ MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno);
+ *result = NULL;
+ return 0;
+ }
+
+ if (false) fail: {
+ munmap(buffer, length);
+ *result = NULL;
+ return 0;
+ }
+
+ size_t start(pad), end(length / sizeof(uint16_t));
+ uint32_t *trailer(reinterpret_cast(buffer + end));
+ for (unsigned offset(0); offset != used / sizeof(uint16_t); ++offset) {
+ if (T$pcrel$ldr(backup[offset])) {
+ union {
+ uint16_t value;
+
+ struct {
+ uint16_t immediate : 8;
+ uint16_t rd : 3;
+ uint16_t : 5;
+ };
+ } bits = {backup[offset+0]};
+
+ buffer[start+0] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+0, end-2) / 4);
+ buffer[start+1] = T$ldr_rd_$rn_im_4$(bits.rd, bits.rd, 0);
+
+ // XXX: this code "works", but is "wrong": the mechanism is more complex than this
+ *--trailer = ((reinterpret_cast(area + offset) + 4) & ~0x2) + bits.immediate * 4;
+
+ start += 2;
+ end -= 2;
+ } else if (T$pcrel$b(backup[offset])) {
+ union {
+ uint16_t value;
+
+ struct {
+ uint16_t imm8 : 8;
+ uint16_t cond : 4;
+ uint16_t /*1101*/ : 4;
+ };
+ } bits = {backup[offset+0]};
+
+ intptr_t jump(bits.imm8 << 1);
+ jump |= 1;
+ jump <<= 23;
+ jump >>= 23;
+
+ buffer[start+0] = T$b$_$im(bits.cond, (end-6 - (start+0)) * 2 - 4);
+
+ *--trailer = reinterpret_cast(area + offset) + 4 + jump;
+ *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+ *--trailer = T$nop << 16 | T$bx(A$pc);
+
+ start += 1;
+ end -= 6;
+ } else if (T2$pcrel$b(backup + offset)) {
+ union {
+ uint16_t value;
+
+ struct {
+ uint16_t imm6 : 6;
+ uint16_t cond : 4;
+ uint16_t s : 1;
+ uint16_t : 5;
+ };
+ } bits = {backup[offset+0]};
+
+ union {
+ uint16_t value;
+
+ struct {
+ uint16_t imm11 : 11;
+ uint16_t j2 : 1;
+ uint16_t a : 1;
+ uint16_t j1 : 1;
+ uint16_t : 2;
+ };
+ } exts = {backup[offset+1]};
+
+ intptr_t jump(1);
+ jump |= exts.imm11 << 1;
+ jump |= bits.imm6 << 12;
+
+ if (exts.a) {
+ jump |= bits.s << 24;
+ jump |= (~(bits.s ^ exts.j1) & 0x1) << 23;
+ jump |= (~(bits.s ^ exts.j2) & 0x1) << 22;
+ jump |= bits.cond << 18;
+ jump <<= 7;
+ jump >>= 7;
+ } else {
+ jump |= bits.s << 20;
+ jump |= exts.j2 << 19;
+ jump |= exts.j1 << 18;
+ jump <<= 11;
+ jump >>= 11;
+ }
+
+ buffer[start+0] = T$b$_$im(exts.a ? A$al : bits.cond, (end-6 - (start+0)) * 2 - 4);
+
+ *--trailer = reinterpret_cast(area + offset) + 4 + jump;
+ *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+ *--trailer = T$nop << 16 | T$bx(A$pc);
+
+ ++offset;
+ start += 1;
+ end -= 6;
+ } else if (T$pcrel$bl(backup + offset)) {
+ union {
+ uint16_t value;
+
+ struct {
+ uint16_t immediate : 10;
+ uint16_t s : 1;
+ uint16_t : 5;
+ };
+ } bits = {backup[offset+0]};
+
+ union {
+ uint16_t value;
+
+ struct {
+ uint16_t immediate : 11;
+ uint16_t j2 : 1;
+ uint16_t x : 1;
+ uint16_t j1 : 1;
+ uint16_t : 2;
+ };
+ } exts = {backup[offset+1]};
+
+ int32_t jump(0);
+ jump |= bits.s << 24;
+ jump |= (~(bits.s ^ exts.j1) & 0x1) << 23;
+ jump |= (~(bits.s ^ exts.j2) & 0x1) << 22;
+ jump |= bits.immediate << 12;
+ jump |= exts.immediate << 1;
+ jump |= exts.x;
+ jump <<= 7;
+ jump >>= 7;
+
+ buffer[start+0] = T$push_r(1 << A$r7);
+ buffer[start+1] = T$ldr_rd_$pc_im_4$(A$r7, ((end-2 - (start+1)) * 2 - 4 + 2) / 4);
+ buffer[start+2] = T$mov_rd_rm(A$lr, A$r7);
+ buffer[start+3] = T$pop_r(1 << A$r7);
+ buffer[start+4] = T$blx(A$lr);
+
+ *--trailer = reinterpret_cast(area + offset) + 4 + jump;
+
+ ++offset;
+ start += 5;
+ end -= 2;
+ } else if (T$pcrel$cbz(backup[offset])) {
+ union {
+ uint16_t value;
+
+ struct {
+ uint16_t rn : 3;
+ uint16_t immediate : 5;
+ uint16_t : 1;
+ uint16_t i : 1;
+ uint16_t : 1;
+ uint16_t op : 1;
+ uint16_t : 4;
+ };
+ } bits = {backup[offset+0]};
+
+ intptr_t jump(1);
+ jump |= bits.i << 6;
+ jump |= bits.immediate << 1;
+
+ //jump <<= 24;
+ //jump >>= 24;
+
+ unsigned rn(bits.rn);
+ unsigned rt(rn == A$r7 ? A$r6 : A$r7);
+
+ buffer[start+0] = T$push_r(1 << rt);
+ buffer[start+1] = T1$mrs_rd_apsr(rt);
+ buffer[start+2] = T2$mrs_rd_apsr(rt);
+ buffer[start+3] = T$cbz$_rn_$im(bits.op, rn, (end-10 - (start+3)) * 2 - 4);
+ buffer[start+4] = T1$msr_apsr_nzcvqg_rn(rt);
+ buffer[start+5] = T2$msr_apsr_nzcvqg_rn(rt);
+ buffer[start+6] = T$pop_r(1 << rt);
+
+ *--trailer = reinterpret_cast(area + offset) + 4 + jump;
+ *--trailer = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+ *--trailer = T$nop << 16 | T$bx(A$pc);
+ *--trailer = T$nop << 16 | T$pop_r(1 << rt);
+ *--trailer = T$msr_apsr_nzcvqg_rn(rt);
+
+#if 0
+ if ((start & 0x1) == 0)
+ buffer[start++] = T$nop;
+ buffer[start++] = T$bx(A$pc);
+ buffer[start++] = T$nop;
+
+ uint32_t *arm(reinterpret_cast(buffer + start));
+ arm[0] = A$add(A$lr, A$pc, 1);
+ arm[1] = A$ldr_rd_$rn_im$(A$pc, A$pc, (trailer - arm) * sizeof(uint32_t) - 8);
+#endif
+
+ start += 7;
+ end -= 10;
+ } else if (T$pcrel$ldrw(backup[offset])) {
+ union {
+ uint16_t value;
+
+ struct {
+ uint16_t : 7;
+ uint16_t u : 1;
+ uint16_t : 8;
+ };
+ } bits = {backup[offset+0]};
+
+ union {
+ uint16_t value;
+
+ struct {
+ uint16_t immediate : 12;
+ uint16_t rt : 4;
+ };
+ } exts = {backup[offset+1]};
+
+ buffer[start+0] = T1$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2));
+ buffer[start+1] = T2$ldr_rt_$rn_im$(exts.rt, A$pc, T$Label(start+0, end-2));
+
+ buffer[start+2] = T1$ldr_rt_$rn_im$(exts.rt, exts.rt, 0);
+ buffer[start+3] = T2$ldr_rt_$rn_im$(exts.rt, exts.rt, 0);
+
+ // XXX: this code "works", but is "wrong": the mechanism is more complex than this
+ *--trailer = ((reinterpret_cast(area + offset) + 4) & ~0x2) + (bits.u == 0 ? -exts.immediate : exts.immediate);
+
+ ++offset;
+ start += 4;
+ end -= 2;
+ } else if (T$pcrel$add(backup[offset])) {
+ union {
+ uint16_t value;
+
+ struct {
+ uint16_t rd : 3;
+ uint16_t rm : 3;
+ uint16_t h2 : 1;
+ uint16_t h1 : 1;
+ uint16_t : 8;
+ };
+ } bits = {backup[offset+0]};
+
+ if (bits.h1) {
+ MSLog(MSLogLevelError, "MS:Error:pcrel(%u):add (rd > r7)", offset);
+ goto fail;
+ }
+
+ unsigned rt(bits.rd == A$r7 ? A$r6 : A$r7);
+
+ buffer[start+0] = T$push_r(1 << rt);
+ buffer[start+1] = T$mov_rd_rm(rt, (bits.h1 << 3) | bits.rd);
+ buffer[start+2] = T$ldr_rd_$pc_im_4$(bits.rd, T$Label(start+2, end-2) / 4);
+ buffer[start+3] = T$add_rd_rm((bits.h1 << 3) | bits.rd, rt);
+ buffer[start+4] = T$pop_r(1 << rt);
+ *--trailer = reinterpret_cast(area + offset) + 4;
+
+ start += 5;
+ end -= 2;
+ } else if (T$32bit$i(backup[offset])) {
+ buffer[start++] = backup[offset];
+ buffer[start++] = backup[++offset];
+ } else {
+ buffer[start++] = backup[offset];
+ }
+ }
+
+ buffer[start++] = T$bx(A$pc);
+ buffer[start++] = T$nop;
+
+ uint32_t *transfer = reinterpret_cast(buffer + start);
+ transfer[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+ transfer[1] = reinterpret_cast(area + used / sizeof(uint16_t)) + 1;
+
+ if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {
+ MSLog(MSLogLevelError, "MS:Error:mprotect():%d", errno);
+ return 0;
+ }
+
+ *result = reinterpret_cast(buffer + pad) + 1;
+
+ if (MSDebug) {
+ char name[16];
+ sprintf(name, "%p", *result);
+ MSLogHexEx(buffer, length, 2, name);
+ }
+
+ }
+
+ {
+ SubstrateHookMemory code(process, area, used);
+
+ if (align != 0)
+ area[0] = T$nop;
+
+ thumb[0] = T$bx(A$pc);
+ thumb[1] = T$nop;
+
+ arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+ arm[1] = reinterpret_cast(replace);
+
+ for (unsigned offset(0); offset != blank; ++offset)
+ trail[offset] = T$nop;
+ }
+
+ if (MSDebug) {
+ char name[16];
+ sprintf(name, "%p", area);
+ MSLogHexEx(area, used + sizeof(uint16_t), 2, name);
+ }
+
+ return used;
+}
+
+static size_t SubstrateHookFunctionARM(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
+ if (symbol == NULL)
+ return 0;
+ printf(OBFUSCATE("SubstrateHookFunctionARM\n"));
+ uint32_t *area(reinterpret_cast(symbol));
+ uint32_t *arm(area);
+
+ const size_t used(8);
+
+ uint32_t backup[used / sizeof(uint32_t)] = {arm[0], arm[1]};
+
+ if (MSDebug) {
+ char name[16];
+ sprintf(name, "%p", area);
+ MSLogHexEx(area, used + sizeof(uint32_t), 4, name);
+ }
+
+ if (result != NULL) {
+
+ if (backup[0] == A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8)) {
+ *result = reinterpret_cast(backup[1]);
+
+ return sizeof(backup[0]);
+ }
+
+ size_t length(used);
+ for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset)
+ if (A$pcrel$r(backup[offset])) {
+ if ((backup[offset] & 0x02000000) == 0 || (backup[offset] & 0x0000f000 >> 12) != (backup[offset] & 0x0000000f))
+ length += 2 * sizeof(uint32_t);
+ else
+ length += 4 * sizeof(uint32_t);
+ }
+
+ length += 2 * sizeof(uint32_t);
+
+ uint32_t *buffer(reinterpret_cast(mmap(
+ NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0
+ )));
+
+ if (buffer == MAP_FAILED) {
+ MSLog(MSLogLevelError, "MS:Error:mmap() = %d", errno);
+ *result = NULL;
+ return 0;
+ }
+
+ if (false) fail: {
+ munmap(buffer, length);
+ *result = NULL;
+ return 0;
+ }
+
+ size_t start(0), end(length / sizeof(uint32_t));
+ uint32_t *trailer(reinterpret_cast(buffer + end));
+ for (unsigned offset(0); offset != used / sizeof(uint32_t); ++offset)
+ if (A$pcrel$r(backup[offset])) {
+ union {
+ uint32_t value;
+
+ struct {
+ uint32_t rm : 4;
+ uint32_t : 1;
+ uint32_t shift : 2;
+ uint32_t shiftamount : 5;
+ uint32_t rd : 4;
+ uint32_t rn : 4;
+ uint32_t l : 1;
+ uint32_t w : 1;
+ uint32_t b : 1;
+ uint32_t u : 1;
+ uint32_t p : 1;
+ uint32_t mode : 1;
+ uint32_t type : 2;
+ uint32_t cond : 4;
+ };
+ } bits = {backup[offset+0]}, copy(bits);
+
+ bool guard;
+ if (bits.mode == 0 || bits.rd != bits.rm) {
+ copy.rn = bits.rd;
+ guard = false;
+ } else {
+ copy.rn = bits.rm != A$r0 ? A$r0 : A$r1;
+ guard = true;
+ }
+
+ if (guard)
+ buffer[start++] = A$stmdb_sp$_$rs$((1 << copy.rn));
+
+ buffer[start+0] = A$ldr_rd_$rn_im$(copy.rn, A$pc, (end-1 - (start+0)) * 4 - 8);
+ buffer[start+1] = copy.value;
+
+ start += 2;
+
+ if (guard)
+ buffer[start++] = A$ldmia_sp$_$rs$((1 << copy.rn));
+
+ *--trailer = reinterpret_cast(area + offset) + 8;
+ end -= 1;
+ } else
+ buffer[start++] = backup[offset];
+
+ buffer[start+0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+ buffer[start+1] = reinterpret_cast(area + used / sizeof(uint32_t));
+
+ if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {
+ MSLog(MSLogLevelError, OBFUSCATE("MS:Error:mprotect():%d"), errno);
+ goto fail;
+ }
+
+ *result = buffer;
+
+ if (MSDebug) {
+ char name[16];
+ sprintf(name, "%p", *result);
+ MSLogHexEx(buffer, length, 4, name);
+ }
+
+ }
+
+ {
+ SubstrateHookMemory code(process, symbol, used);
+
+ arm[0] = A$ldr_rd_$rn_im$(A$pc, A$pc, 4 - 8);
+ arm[1] = reinterpret_cast(replace);
+ }
+
+ if (MSDebug) {
+ char name[16];
+ sprintf(name, "%p", area);
+ MSLogHexEx(area, used + sizeof(uint32_t), 4, name);
+ }
+
+ return used;
+}
+
+static size_t SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
+ if (MSDebug)
+ MSLog(MSLogLevelNotice, "SubstrateHookFunction(%p, %p, %p, %p)\n", process, symbol, replace, result);
+ if ((reinterpret_cast(symbol) & 0x1) == 0)
+ return SubstrateHookFunctionARM(process, symbol, replace, result);
+ else
+ return SubstrateHookFunctionThumb(process, reinterpret_cast(reinterpret_cast(symbol) & ~0x1), replace, result);
+}
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include "SubstrateX86.hpp"
+
+static size_t MSGetInstructionWidthIntel(void *start) {
+ hde64s decode;
+ return hde64_disasm(start, &decode);
+}
+
+static void
+SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) {
+ if (MSDebug)
+ MSLog(MSLogLevelNotice, OBFUSCATE("MSHookFunction(%p, %p, %p)\n"), symbol, replace, result);
+ if (symbol == NULL)
+ return;
+
+ uintptr_t source(reinterpret_cast(symbol));
+ uintptr_t target(reinterpret_cast(replace));
+
+ uint8_t *area(reinterpret_cast(symbol));
+
+ size_t required(MSSizeOfJump(target, source));
+
+ if (MSDebug) {
+ char name[16];
+ sprintf(name, OBFUSCATE("%p"), area);
+ MSLogHex(area, 32, name);
+ }
+
+ size_t used(0);
+ while (used < required) {
+ size_t width(MSGetInstructionWidthIntel(area + used));
+ if (width == 0) {
+ //MSLog(MSLogLevelError, "MS:Error:MSGetInstructionWidthIntel(%p) == 0", area + used);
+ return;
+ }
+
+ used += width;
+ }
+
+ size_t blank(used - required);
+
+ if (MSDebug) {
+ char name[16];
+ sprintf(name, OBFUSCATE("%p"), area);
+ MSLogHex(area, used + sizeof(uint16_t), name);
+ }
+
+ uint8_t backup[used];
+ memcpy(backup, area, used);
+
+ if (result != NULL) {
+
+ if (backup[0] == 0xe9) {
+ *result = reinterpret_cast(source + 5 +
+ *reinterpret_cast(backup + 1));
+ return;
+ }
+
+ if (!ia32 && backup[0] == 0xff && backup[1] == 0x25) {
+ *result = *reinterpret_cast(source + 6 +
+ *reinterpret_cast(backup + 2));
+ return;
+ }
+
+ size_t length(used + MSSizeOfJump(source + used));
+
+ for (size_t offset(0), width; offset != used; offset += width) {
+ hde64s decode;
+ hde64_disasm(backup + offset, &decode);
+ width = decode.len;
+ //_assert(width != 0 && offset + width <= used);
+
+#ifdef __LP64__
+ if ((decode.modrm & 0xc7) == 0x05) {
+ if (decode.opcode == 0x8b) {
+ void *destiny(area + offset + width + int32_t(decode.disp.disp32));
+ uint8_t reg(decode.rex_r << 3 | decode.modrm_reg);
+ length -= decode.len;
+ length += MSSizeOfPushPointer(destiny);
+ length += MSSizeOfPop(reg);
+ length += MSSizeOfMove64();
+ } else {
+ MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2);
+ continue;
+ }
+ } else
+#endif
+
+ if (backup[offset] == 0xe8) {
+ int32_t relative(*reinterpret_cast(backup + offset + 1));
+ void *destiny(area + offset + decode.len + relative);
+
+ if (relative == 0) {
+ length -= decode.len;
+ length += MSSizeOfPushPointer(destiny);
+ } else {
+ length += MSSizeOfSkip();
+ length += MSSizeOfJump(destiny);
+ }
+ } else if (backup[offset] == 0xeb) {
+ length -= decode.len;
+ length += MSSizeOfJump(area + offset + decode.len +
+ *reinterpret_cast(backup + offset + 1));
+ } else if (backup[offset] == 0xe9) {
+ length -= decode.len;
+ length += MSSizeOfJump(area + offset + decode.len +
+ *reinterpret_cast(backup + offset + 1));
+ } else if (
+ backup[offset] == 0xe3 ||
+ (backup[offset] & 0xf0) == 0x70
+ // XXX: opcode2 & 0xf0 is 0x80?
+ ) {
+ length += decode.len;
+ length += MSSizeOfJump(area + offset + decode.len +
+ *reinterpret_cast(backup + offset + 1));
+ }
+ }
+
+ uint8_t *buffer(reinterpret_cast(mmap(
+ NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0
+ )));
+
+ if (buffer == MAP_FAILED) {
+ MSLog(MSLogLevelError, OBFUSCATE("MS:Error:mmap() = %d"), errno);
+ *result = NULL;
+ return;
+ }
+
+ if (false)
+ fail:
+ {
+ munmap(buffer, length);
+ *result = NULL;
+ return;
+ }
+
+ {
+ uint8_t *current(buffer);
+
+ for (size_t offset(0), width; offset != used; offset += width) {
+ hde64s decode;
+ hde64_disasm(backup + offset, &decode);
+ width = decode.len;
+ //_assert(width != 0 && offset + width <= used);
+
+#ifdef __LP64__
+ if ((decode.modrm & 0xc7) == 0x05) {
+ if (decode.opcode == 0x8b) {
+ void *destiny(area + offset + width + int32_t(decode.disp.disp32));
+ uint8_t reg(decode.rex_r << 3 | decode.modrm_reg);
+ MSPushPointer(current, destiny);
+ MSWritePop(current, reg);
+ MSWriteMove64(current, reg, reg);
+ } else {
+ MSLog(MSLogLevelError, "MS:Error: Unknown RIP-Relative (%.2x %.2x)", decode.opcode, decode.opcode2);
+ goto copy;
+ }
+ } else
+#endif
+
+ if (backup[offset] == 0xe8) {
+ int32_t relative(*reinterpret_cast(backup + offset + 1));
+ if (relative == 0)
+ MSPushPointer(current, area + offset + decode.len);
+ else {
+ MSWrite(current, 0xe8);
+ MSWrite(current, MSSizeOfSkip());
+ void *destiny(area + offset + decode.len + relative);
+ MSWriteSkip(current, MSSizeOfJump(destiny, current + MSSizeOfSkip()));
+ MSWriteJump(current, destiny);
+ }
+ } else if (backup[offset] == 0xeb)
+ MSWriteJump(current, area + offset + decode.len +
+ *reinterpret_cast(backup + offset + 1));
+ else if (backup[offset] == 0xe9)
+ MSWriteJump(current, area + offset + decode.len +
+ *reinterpret_cast(backup + offset + 1));
+ else if (
+ backup[offset] == 0xe3 ||
+ (backup[offset] & 0xf0) == 0x70
+ ) {
+ MSWrite(current, backup[offset]);
+ MSWrite(current, 2);
+ MSWrite(current, 0xeb);
+ void *destiny(area + offset + decode.len +
+ *reinterpret_cast(backup + offset + 1));
+ MSWrite(current, MSSizeOfJump(destiny, current + 1));
+ MSWriteJump(current, destiny);
+ } else
+#ifdef __LP64__
+ copy:
+#endif
+ {
+ MSWrite(current, backup + offset, width);
+ }
+ }
+
+ MSWriteJump(current, area + used);
+ }
+
+ if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {
+ MSLog(MSLogLevelError, OBFUSCATE("MS:Error:mprotect():%d"), errno);
+ goto fail;
+ }
+
+ *result = buffer;
+
+ if (MSDebug) {
+ char name[16];
+ sprintf(name, OBFUSCATE("%p"), *result);
+ MSLogHex(buffer, length, name);
+ }
+
+ }
+
+ {
+ SubstrateHookMemory code(process, area, used);
+ uint8_t *current(area);
+ MSWriteJump(current, target);
+ for (unsigned offset(0); offset != blank; ++offset)
+ MSWrite(current, 0x90);
+ }
+
+ if (MSDebug) {
+ char name[16];
+ sprintf(name, OBFUSCATE("%p"), area);
+ MSLogHex(area, used + sizeof(uint16_t), name);
+ }
+}
+
+#endif
+
+void MSHookFunction(void *symbol, void *replace, void **result) {
+#if defined(__i386__) || defined(__arm__)
+ SubstrateHookFunction(NULL, symbol, replace, result);
+#endif
+}
+
+#if defined(__APPLE__) && defined(__arm__)
+_extern void _Z14MSHookFunctionPvS_PS_(void *symbol, void *replace, void **result) {
+ return MSHookFunction(symbol, replace, result);
+}
+#endif
diff --git a/app/src/main/jni/Substrate/SubstrateHook.h b/app/src/main/jni/Substrate/SubstrateHook.h
new file mode 100644
index 0000000..ab68a60
--- /dev/null
+++ b/app/src/main/jni/Substrate/SubstrateHook.h
@@ -0,0 +1,19 @@
+#ifndef __SUBSTRATEHOOK_H__
+#define __SUBSTRATEHOOK_H__
+
+
+#include
+
+#define _extern extern "C" __attribute__((__visibility__("hidden")))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void MSHookFunction(void *symbol, void *replace, void **result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/app/src/main/jni/Substrate/SubstrateLog.hpp b/app/src/main/jni/Substrate/SubstrateLog.hpp
new file mode 100644
index 0000000..3e57280
--- /dev/null
+++ b/app/src/main/jni/Substrate/SubstrateLog.hpp
@@ -0,0 +1,40 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011 Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate. If not, see .
+**/
+/* }}} */
+
+#ifndef SUBSTRATE_LOG_HPP
+#define SUBSTRATE_LOG_HPP
+
+#if 0
+#include
+
+#define MSLog(level, format, ...) ((void)__android_log_print(level, "NNNN", format, __VA_ARGS__))
+
+#define MSLogLevelNotice ANDROID_LOG_INFO
+#define MSLogLevelWarning ANDROID_LOG_WARN
+#define MSLogLevelError ANDROID_LOG_ERROR
+
+#else
+
+#define MSLog(level, format, ...) printf(format, __VA_ARGS__)
+
+#endif
+
+#endif//SUBSTRATE_LOG_HPP
diff --git a/app/src/main/jni/Substrate/SubstratePosixMemory.cpp b/app/src/main/jni/Substrate/SubstratePosixMemory.cpp
new file mode 100644
index 0000000..b25348b
--- /dev/null
+++ b/app/src/main/jni/Substrate/SubstratePosixMemory.cpp
@@ -0,0 +1,76 @@
+/* Cydia Substrate - Powerful Code Insertion Platform
+ * Copyright (C) 2008-2011 Jay Freeman (saurik)
+*/
+
+/* GNU Lesser General Public License, Version 3 {{{ */
+/*
+ * Substrate is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Substrate is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Substrate. If not, see .
+**/
+/* }}} */
+
+#define SubstrateInternal
+#include "CydiaSubstrate.h"
+#include "SubstrateLog.hpp"
+
+#include
+
+#include
+#include
+#include
+#include
+
+extern "C" void __clear_cache (void *beg, void *end);
+
+struct __SubstrateMemory {
+ void *address_;
+ size_t width_;
+
+ __SubstrateMemory(void *address, size_t width) :
+ address_(address),
+ width_(width)
+ {
+ }
+};
+
+extern "C" SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size) {
+ if (allocator != NULL) {
+ MSLog(MSLogLevelError, OBFUSCATE("MS:Error:allocator != %d"), 0);
+ return NULL;
+ }
+
+ if (size == 0)
+ return NULL;
+
+ int page(getpagesize());
+
+ uintptr_t base(reinterpret_cast(data) / page * page);
+ size_t width(((reinterpret_cast(data) + size - 1) / page + 1) * page - base);
+ void *address(reinterpret_cast(base));
+
+ if (mprotect(address, width, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
+ MSLog(MSLogLevelError, OBFUSCATE("MS:Error:mprotect() = %d"), errno);
+ return NULL;
+ }
+
+ return new __SubstrateMemory(address, width);
+}
+
+extern "C" void SubstrateMemoryRelease(SubstrateMemoryRef memory) {
+ if (mprotect(memory->address_, memory->width_, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
+ MSLog(MSLogLevelError, OBFUSCATE("MS:Error:mprotect() = %d"), errno);
+
+ __clear_cache(reinterpret_cast