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("

" + "

Modded by LGL

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

" + "
")); +} + +jstring Icon(JNIEnv *env, jobject thiz) { + return env->NewStringUTF( + OBFUSCATE("iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAMAAAC3Ycb+AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAADeUExURUdwTAC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwC8DwAAAAC8D2XRAEiaABIyADl6AGDJACNSAD6FACthAAYcAE6kAAEQADNtAFi3AACyDVzAAAglAABCAQC3DQAIAACgClKtAACTCAAwAWPNAABLAgBnBBxCAABzBQCoCwBbAwB8BgBSAl/EAAAZAACZCUyfAAA6AQCOCECJAACGB0aVAEOQADVzABg7AC5nAChbACleAGmQvrQAAAAZdFJOUwDwCxYgcZUr3/nosNDHBKSHVP1jM7lIe0FRwZXpAAAbvElEQVR42u2deX/iqhrH475vUdtJWp06NlXjrlat3Zdpz3n/b+je9qgGAgmJBFD5/XPu59apwLfwLDyAohyg8pHEWTlWjBcy1Ww2mQ6FQvq3QqFQOpnNVjOFeDFWPktE8opUoApXSrF4Jpv+GX5dVXWcNj9K5zLxWKkSlkNHW5FfsXgurfuQ+h+YeOwsKoeRyvKUKBWrSZ2GQrl4uSKXsX3mRSmeS+l0lcrFSxE5tN6VKBeSeiBSdT1ZKCfkEHuAEcuE9KAVysQkFKJlqpDWWSldkMuX89Q4z6k6W6m5czlR0KoUk2svlS0RXU8WK3L4YRrxpM5TybhkYlmpinxprJkU5dr1Y8Vj2T1Hsv1/LQaL7//s+ZuysVO38fmzTMq73egNRm/z2ar7bD4YGizjwXzurmbzt9Gg59meqKnM2QnH8pFzb2a8N3ibL7tmXyNV3+wu52+ewKh68vxEp8mvDHlapPc4X40Nza+M8Wr+SI4llfl1cjTCZVLL0X6ZdR9+/ty1PfXQnb2QWpls+aRy9pEiWTS+mE+bGl01p/MFWRRfPJmVKxonWasG866hBSOjOx+QrFzxk9hCqRTczXj7LjAYOyh37suXWjj6cLGSccXxODM1NjJnj65IMpXTxvGyMjSWMqYvp4sk4YbjbdrX2Ks/fXNDcpQ5laiL7RiteNBYM1mNXGzJ0Zn3cNHRs1osDY2vjKWjN5wqHlVcko85bcn27saaCBrfOQXzodjxJLlKTtn1Acelyr50OcUnydKR2PKqQwJRkMlhnSYOecdq4riNR3tmaOLJmLWP2JSU8EmrxUoTVSu8gU8f9LoVzeDj8a4msrr4GD5zsC6wg281Gmuiazw6Nn8rkTtgHM5Icgdo3PPnqcPG4YgkdX5okySB2xAcPGuHpGdcYJI9rEkSw0yPRVc7NHXRHpeaih2Qc1XFxB1L7RC1xMQl1UNxt0oY52re1w5T/TnG3TqImCQcx9jypna4amKsezx8qNb8AI0HkSkR3raX0dZ8ph2+ZmgHuCz4coVK7D6a2jHIfESmgAVetqLI5aq30o5FK+QOVlZUb+sM6V29PGjHIwNZoxI6EzMYRK1W7ZV2XFqhghJVwCAxXED6uoZ2bDKQHnBBNEMSQaV2e0vtGLVEWZKcWKXZCVQZw6CpHaeaqIxjMiG6OZ9rx6u52Ka9jDDn7a52zOoibLsqSoxYRMWChnbcMlBRYlGIrUGUe3WnHb9QBVwF/huJ4epRx+Ze4/Yqb/c3jHB3F6Z2GjIXiLts+BKJILJXL33tVNRHZFKyPAOSaPI4M+375OTVZFQkHr2pdlqa9sQhguDRHmunprE9IuFEJGGvox48aKenB3siJZ0QY36M+topqj8SYY4geLxpp6o3/kQQPOba6WrOm0gkeeLurrv7m2QZj4Tt8eBSO20t7REiu5gdkS+ZaqeuqX0TkRWRvD2f2NWkurYIscoo91uQPMiI6AU++1G9ZwnjR889HjtWZdvfgeSxJWIbm+B3dUuqXK+82JGgj5AkQpKHJyKhBOOAUPq7Lt5voAGiPQBZSgRuEWKQ4UhB5kt8ZFGCc35jMp/oK9MYVG38mSrz7b6y8WowRaZR2MEayaHHCN6xCgWRi7dleAd9OfIY9QcMMr/w8fP2gxx4rB7gyod44BmT3lgOu4PGvYBzKImUDAj3ChBTiWANiAxAvIYjdM0IbEBe5IB7dn5pmpESXN8uHSwfrlYpsAikJx0sIlerF1Q0UpUZd1+Cc/HVgFJYMoPlN6sVC8TjfZQDTazHAHzfPOTxtg05zsQyoIg9S6Ew6FwaEIpm5Jz6giUNyF5mZO9FKw9t2g7kEHsUFI3k8lQ9rF5TjrBHNXs0PS04JJQ1Dd61pBkeZuQe4f6C9g8z9HJY0uOl4vv6z2mFoXO2wV1h0qxZhTBUZg0l0yD62H+yf8ZANoH+X90KOqPrOxFfZJJzv3pvXUCatF4/wQ9dXqB1/3RjEHzsW/bPNKzfUN/8v1f0u/hCpyYeCkF6gSxYtSFm/N4NjWyk7//xC6TFCojRoxKMVBksWLV77AC2SP/0L758ArkwGQGBF60qDYseTE6x5TCCH6RAJjWfQG5YAYGzjH7seh4qdA/kEqw/ZOPsAuTi0ieQJ2ZATOh+mvzeMXowVQ3XjuNcJwXy2yeQ331WQOCaB+/xegSM0RfBeFj3juP8RArk4tYfEOvgBwykD94+F/J8aiTOIuluWEz4+2vjW6+XHZQXtB3F+8ZGH9d2s7792KRhEwrIKzMgcCLeaw1KNMUiZ2JuR2ZoobQl0kEAsbqqX9t//hf+2G/8l1qBtNgBgTIoKY8pLehoTkBJ3hoyRntHjCoSSNNmbbwCuaixA9Lc5xhPQmWyK1Wz/Yl/65UUiLY/kL/sgEB7VWrCf5a33Q8ciDUiaDAEcs0QSL/tO+tbUdnsgvAHMjHYAYF2RtSK3wmy0I4XyMU/DIFoC59TBJog3WMG8sESSNfnFMmwqmsQAEiHJRCo4iHjb4I8HzWQi0+WQJ59TZECs310EYDUWQKBosOCnyB9fLRA7oEcARsgY3CKRL1nsQItNOELZP0/miyBQFOEJKMVYTdBOANZb8b8YQoEnCKpiNfKhmArsfgCMX9bd7cYAYGmSNFr6c/4iIE0/8vf37MFMvZYElRmeTiHM5Ab6+4WKyDQ9rrrhQJZlodBOAMxral/ZkDAcD3rwuMXoyyWEEDWNS8ttkCgjNYvL1mT1ZEDaVjKs9gBAYq01IwHn7etHSyQSR3QDRrIleXL2QHR2uSe7znbC02CAwKpgwayrnp5YgxkRnzsECyO6xlHD+RyV57FEAhY6utUNHfG+EHbPYHU9gbytWPAEAj0jO4ZaZ53LDqQV9s/9wqkuSvPYglkTLgtApp0BgdufQD5fb3RcFdRt8lHeQaiDbeUWQIBN6rwZj3G1Of1BwStml8g9e2/ZwpkRVboC0TpPe1wgHQ0v0But+VZTIFoPZJoPcH8jXpaQOx1u5NbQJ84IFpnU57FFgho1hMkiffx4QDpPGj+InVtU7c6MTS2QMYkSfgkyzQWRSD3n5p/IP9uyrPYAgETWklkUFhhf2cDFSBPprYHEGOyLs9iDASsYqy476UbggKZdCxqDS//1pCDTQxk7fh2WAMx3PfWk+yvjds3dYIfbHIgaxCfjIGAW7moNavCOggRBMi6DXXWQKZua1aR/YolBJD1LtWQNZB+z8XPSnK49kcIIB/rWIYxEPDm66RLVLg6ISDrXaohayBT59jwnMOKJQaQ/m/QjWYFpO+8TZXjcde+EEC0Jz5AwDuCcnDmXeWwYgkC5IYTEGDNUiNON80YJwXE5AQEiA3VssNeIbPLxMUAAl1JxAwIWMMInRVJc3k+RxAgDU5AgOqTtIPTa54YkCtOQEy84xtjWh8nGhDwUiJ2QMCKuRj2Nr+7kwNyyQnIHe7ev3yIz4t4+951QgvIFycggOMbyuMyvQYHIA2XUQ0WSJMTEAOX8Y1xegBhFwG0dvfbNO9J78uiBkQb8gEC1mfFMIcQGL4Q0rdsjA8vf/Q0/O14oxwhkMmlTXUHIHVOQOaYCsY0ryd0vN+5SAgEoaEDkFtOQLroSCTByYTY8nqQ/jIDonX4ADHQkUiZ2xs6V4739prsgLzzAQIakTKy3oTtI1NDknpEBkD+5QRkjqw9yfJ7l9vE3zV+3WcIZF2exRzIFFXjm08xuIAUpyZmDCcN9EgHBMQyVZkCAa4qTeURYWFPY63PxvB+AsLoPNVNjS2QOh8gYBl8BWHT5UvpbPWCsOpxLnshUj+aIax6Tr6syk9dRKUDkOqVb9ez1QOQ8P3vSj++Nv3UBVj1qO1wunwsnbVGtiPrMfkYNE/NbRn4OI8SOamNrCek1YLNyRrLEWKssc3NSvPKvUt9y5qBV7+3RMLSyRLIzQpDu1MDOT7MNYCyWUCZ9ZscH+Z6g54AjclMFl/NIL83zm93SupbUyi9mJFerzh+r5pRlJxMLfLVAxSIAGFIX44Pe4G1WXmVy0EEqZ2shxJUJSLDEKECEbDCYSRHh4OAbXVwN0TGhTwEHNsBS07kbgj3yFAG6vwFXC4H3sokt6e4h+pg5kTWAPEQ8PwneIXDsxwdzrkTHUxlmXJ0OAi4P0CmsvjrAQ9EprJ4CDjXBh7WkYPDRQCQpAQiFpC0BCIWkJDMvnNXWwKRQKRIgaQkEO6yviWiMH/ERUoCkUCk/AORNkQwINLLkm6vlARyQEBkLou/ZLZXZCByP0QwIDl5Bpe3DLmnLpYeZNWJWDLxdVnyRBsPjfGVi7JQjoee8bW98hAuD03x1e9LOToctMSfD5HHEXhohj9BdSdHh4Pu8GcM5aW9PASeMZSncLkLPIUrz6lzF3hOnfwmB7O2ESag3/68hsiJNWtWNd0+4PhRGg0za3jZP2MgW2ogvsLaBMNpQHaCXtkhTmbtLrq/R/7c8jjLH+gn77ZHKSat10/wQ3XsLfqtj1tHIL4a5nRtv/0zDWRLLdf2bx/OurR88A/RBf/wXSfEtwFZuoAcoFcMkBru0ZZ3gwzI92tUTdoN8wakFSCQMfQwGPF9WZYu1FE/b6GB1PAPf7UMUiAXrSblhnkDcmEGBwS+L4v4oPql/SkOYCW/QANpOfT8gxiI9cU2Kg3zCOQmOCDwjXLEdy5amjdBGKkvNJA/jsNcIwZy8Um3YR6BPAUHBL5zsUIaiFi78I/zjy1Arh1HuU4OpEG3YR6B/O4HBgS+lZT43t5LzFqz1j0aCPHTkW5AhnQb5hGIdUgpA4Hv7SW+2draPPtLUNa3Mi39Niz/5v218a3Xyw7q9+yA/P2z0dff3QTr0G3Y7pHWhk2oX/waFBDD9tgn6d3vl5jFH/EH/gdhUS1/4UYHMcp19O/e+gQTug3z9Iwx5k+HChD73e+kryNcYl5F/U9DdL/RD3S/I0YDA+TdtpDQaZhXIJZW0QVifx2B9P2QSycv1Ji4AXF7wh4D5NUjENKGeQbyNyAg9vdDzggv+QOaB3fjnws3IFZPvkEOpOERCGnDPAO5DgiI/YUd0jeoQL8E+ooPUYCQNswzkF2IQxeI/Q0q0lfaLp3CgpYwQAgb5hnILsShCgTxShvpO4aXDmFB7UIYIIQN8w7kIxAgqHcMCV/6hEIpINt3Iw4QwoZ5B9IJBAjqpU/Ct3AvcX37v54EAkLWMO9Attk0qkBQb+ESvhYNNe/d+rPfAgEha5gPIPUggKBeiyZ8T33TvJY9lXEF/IQXEE8N8wTkHrBONIEg31MHD+1M3Zp33bHlw9ej9soXiKeGeQJyCVgnmkCsu1Nqds0DtOpz136/21Ln6/TEFWcgXhrmCcgf4F/TBDJH2HTIqg9c+/0Fh67NdejU5wzES8M8ATF/WwebJpABwqZDL+dhM/DbfpubbkJfPNQ4A/HSME9AmtfWohaKQPrAyCc2QMAtka5bvzfR7xWUka3zBuKlYd6A3FiLWigC6SLi9G9lSIzIrt+v0J5NZ9Nc3kA8NMwbENOalqEIZA6VAG0UIzEiu35fgXs2n9sqNd5APDTMG5D13GvRBjKw5d4RoSHOiOz6vdljMIGhfOIPxEPDPAJpWH4xPSDA9u0mLPwJDUMEkciu3xtv8gsoLLnhD8RDw3Z76nVAN2ggV5ZO0AMC1MiF8jsgSpXg2I6l33Xrl/cn23HkDoS8Ybiqkw4ayLp25YkuEOCoTtXCAzQibdd+31prm692PeEOhLxhXoFc7sqz6AFpY0wIHImYbv3e1DrdWna9P0QAQt4wr0C+diNLDYiJiUJskcjMtd9PliRFa9dX/kCIG+YVSHPnUVMDsrSVZO0EXOjw6Nrvv7vdOdOyL8QfCHHDvAJZ+wstmkAerYNeAIGU3B1fa79ru43/L8vOKX8gxA3zDKS+bRwtIIDTq5ZBIBHVtVzO2u9NDPzP9v9uiAGEuGGegdxuy7NoAQGcXjUCAgErHV5c+/2+NZj3lq8UAAhpw3ZxyC2gTxyQNeprekBeEPUNO527rllAv/9skhS31gyrAEBIG+YxUt+inhgaJSBgpvccBgI6viu3fjc3g1e37kEIAIS0Yd6B/LtZDCkBmTo4vd9KulWUAv3e+JQ3Q+sunQBASBvmHcg6UfZBCwhwcipp4wHe04Ras8B+r6Ou64l1p0AEIIQN8w5k7fh2KAHpA/UmRTuQituaBfb7CvBN1ntpIgAhbJgPIOsWftIBMsVletFr1qNbv/sTxCEAEYAQNswHkHVf6nSAjFxWLKj2BLFmgf0GD8LciAOEsGE+gKzN05AKEHArJI4CAq5ZS7d+A0fFTIGAkDXMD5D10YYGDSBL1xULWrMWbv2+RRzAEwIIWcP8AFmbpyENIAvXFQv2s8Yu/bYeN/4QCQhZw/wA6UOVwnsAAY56onwse2x459ZvS2H5v0IBIWqYHyBwLf0eQO5cokJEjW/Prd+WoxcPQgEhapgvIDfUgABBSBbDA9zItYUicL9r9kNLYgAhapgvICYtICsdu3kL5OBTTvVZcL93x/caYgEhapgvINCJRf9AgHqsVAQHBKxghM26rd+vnbWuBANC0jB/QBp0gIAmPYPlAR5Zh826rd92CQKEpGH+gFzRAQKa9DM8kDwQivQMCQQ+R3pPA4gBmPRkHg8E2qaaSSAwkEsaQGbOW1N4s96mCST4u05YAPmiAaRNaNLtZn1FEUgD1VOqtwGxANJ0A2JtxF80EMDnVTOOPJRfOtbz9dXvnefe2l2S17xH1HiggfRb7gPHEgiQS7YM87ZHvy1ln7vb126xPq/+yxkIGK0DT+746nffslc0vPzR0/C3LfkHAGnsatFfW8j6HJpAJpc21R2A1NFAdu2cDJ/++y3Dji3p/CPgQR18lI48AArsU/nqN8s7F/0BcfgqFJBbNBDnuz4n2HpFvewGJJzGBYf+gDw5tvQvOZC6GEC0DhJInfiPCQwK02E3IFASfrQvkCvHvxyTHEhNECDvSCDmxKntX7itW1ziHe/5WqaIPyDa0KGhVs/LBciHJgiQf5FArBfM29TCThAXnxe1tz7aF4iJv2v8uk8MZGiIAsRyiaMVSB9vRTo17ASJE/BQoip6ivgEojUxfZ+Ad785AZl8OD1swhaIZcqD4V4Ds2o9mdgJokZJgIBnRXZTxC8QTftsDO/B1k46T3XonBYOyKRzXa9pmjhA6riUoVl/6kDdvB82PvHFP/CZEJwqqi6f/wxGYAyiVsiAQPkT+VIYPQ1IN0Icp0hXDiQldf1NEHiKLORIUtLC3wSxTRH5RC4dLf1OEHiKtPtyMCmo3/Y7QRQlAU6RuRxNCpqDEyThBQgUi+hNOZx7q6n7iUG24XpKJ3w0QYpQYEyYinoDAmW0pOtL2eUly2IBSd+QdH0DdHlDEa9AoEJfxycnpdwFlv7gy3lJi+ZwdzZJkQm8icm5OA4n8D4azBVBUmQCN9L1kqL4UFV3vd1Bikzg8QPwNj8P0SHo+vYMObA+BRbz6qmEPyBQvYPDWy9SznrRvVY2EJUEyUXLp8BLG0hKfwjtelsuWr4WrDYNi47K+soMCoWcibcsry2lBcbrcmfEh8BdED0U3QcIHK/3ZNrXq5q9vWN0IF7P6bLiYS+BdQ16Lr8fEDgYkXtVHgXuSvkPQXDHDmUi3pueodE735uHks9K35eax5vN7w/EtmjJLKPvnCKFBQvhaUkz4teA7Oth4dK+0owQCtq19ZvkdQ8Pew9ysAn00KMaEjrktPSFrJxzVx+KQPbKYbnUoMhMPIHe9D3rTBwT8ZDvK2seXAVVNejZME0gNt8X/8q31I+gPRBaHi/uQgFd743loDtoDBl09wsC9jYjbelqkUfodA0IxowMpKtF7GBRNiDoaETuH2IF7RHSjECAGxlV6Hve5NATObzqmRKM4KSWzGoRZbCopbBcj/HIcIQkAPF6NMeTYc/BXyarHmAt4SHKhYMDokSSugwQPQWEejKiBKkE7GrJXDwgOOOuhxJKsCrBrpa8C8UieAtdV0tK0IJzKJKIA48AMiauNfG63pNE1jzgBJb/Ovf9nN+etCM/9qPH0OEFCoOqurTsBPZcr+bZAEGEI9L7tfu7wQYgLplfeZhnZRuRLDseqADxxLMotnxJ0AGhLRdvJ3LKmca5nUdUUXgTOd1s/Bt/HkoeQWR0mnuI/ZEtPk8z5/Gd1krbiAxOcZ/9YWAbh3RC4SHEHGmfXi3KuC3AeoUn0ju1gGTaE4cHksiJub92d1flyENRIvYIUX85HdPef7F3PxtReAqRRdEXp2LaHxY6z3wJhog903gq2V97dlfXq7x5KEq+YG/WSUTtc0S/C3lFABURLXs89rO6xiOi10VFDJVVe9vax71sPdujD10tK6LoLHRiyxZquQqdKeKogghI9MGx3lTTHCB6m6woIimCcH/13nHWNS4R3pWeiyhiKYxytvTR8dl2Y4TqaCGsCKcYwrTr7WPLbU0R1lxXY4qIQpp2/eWYJonxootuzoFcYxbV3N7x1D+sUNZDz0YVURWOoxqsPx7HvfHmI7J38bAisMopZKOPISc/Q/YsVVbEVgK5bOmLQw/cuwtkv7IJRXRhli19dMhhYnOkH+BytT1CEkK3fn6oO1f9ObpDoZJyGIpW0R1oH2bkvmyju1ONKgejGNq2H6IpwRgPPRVTDkkY267rg8M63fM8wPTjAKw5tJF4jpkk+uhwarfGGFuup87zysEpkdMPGwkWh55LKIeofCx0wEiwONRULK8cqKIZHBH9UWzz3n3EtjwTVQ5YpTS2YwtxM/PTAbbV6ZJy2AoXU9jOtWcipuaNWRvb4lQxrBy8ElUdrzvRjMn4zqG11YRyFColHTo5EGm7ZDVwaGmypByLHPyt7x0sQabJ+K7n0MrQ4fpWyKoUB1PybeCXvK2JsVw4NTBVjChHpmjcEYk+mvJLBvenI8e2peJR5QgVLaiO3dbfuDDpT996js1SC0eJ48fhckOiv0zZrl3G9MWlRWohoRyxEhk3JPrjjFVNhDl7dGuMmjlqHN+quM4SXW/fdYOeKEb3ru3aDrVQUU5AbuZ9HZ/MA4PS784HBC04UlOOdoLTuk4EZUq7NKI5JYKh6+njc3QdU1zlrE6m9susS+cU6UN39tIm/NZsOaycmn5lUjqpeqP5aux/BTPGq/moR/xtqcwv5SQVOU/qXtQbvM2XXZM8WOmb3eX8bdDz9C3J84hyssqfeZgmFjCjt/ls1X02H+xw+g/mc3c1m7+NPIJYT46zvHLaisSy+n5q/1+LweL7P3v+pmwsokgpSqKY1PkrWUxIFLtwMc6XSTJekRAgc1LhNk+SRUkDs3ad51TGMNTcuVypHG18qZBmRiNdKEkrTjJRYtVQ4DBC1ZicGl6glAvBmZRkoSxh+Fq+4rkUZRapXFwuU3s5X4lSsUpnroRy8XIlL4d0byTfc+VXLJ7zb+zTuXjsLCqHkrbClVIsnsmmPYDIxGOlyiFl0v8HKtOuZ5Ocqs4AAAAASUVORK5CYII=")); +} + +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(memory->address_), reinterpret_cast(memory->address_) + memory->width_); + + delete memory; +} diff --git a/app/src/main/jni/Substrate/SubstrateX86.hpp b/app/src/main/jni/Substrate/SubstrateX86.hpp new file mode 100644 index 0000000..ffe2b06 --- /dev/null +++ b/app/src/main/jni/Substrate/SubstrateX86.hpp @@ -0,0 +1,200 @@ +/* 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_X86_HPP +#define SUBSTRATE_X86_HPP + +#include "Buffer.hpp" + +#ifdef __LP64__ +static const bool ia32 = false; +#else +static const bool ia32 = true; +#endif + +enum I$r { + I$rax, I$rcx, I$rdx, I$rbx, + I$rsp, I$rbp, I$rsi, I$rdi, + I$r8, I$r9, I$r10, I$r11, + I$r12, I$r13, I$r14, I$r15, +}; + +_disused static bool MSIs32BitOffset(uintptr_t target, uintptr_t source) { + intptr_t offset(target - source); + return int32_t(offset) == offset; +} + +_disused static size_t MSSizeOfSkip() { + return 5; +} + +_disused static size_t MSSizeOfPushPointer(uintptr_t target) { + return uint64_t(target) >> 32 == 0 ? 5 : 13; +} + +_disused static size_t MSSizeOfPushPointer(void *target) { + return MSSizeOfPushPointer(reinterpret_cast(target)); +} + +_disused static size_t MSSizeOfJump(bool blind, uintptr_t target, uintptr_t source = 0) { + if (ia32 || !blind && MSIs32BitOffset(target, source + 5)) + return MSSizeOfSkip(); + else + return MSSizeOfPushPointer(target) + 1; +} + +_disused static size_t MSSizeOfJump(uintptr_t target, uintptr_t source) { + return MSSizeOfJump(false, target, source); +} + +_disused static size_t MSSizeOfJump(uintptr_t target) { + return MSSizeOfJump(true, target); +} + +_disused static size_t MSSizeOfJump(void *target, void *source) { + return MSSizeOfJump(reinterpret_cast(target), reinterpret_cast(source)); +} + +_disused static size_t MSSizeOfJump(void *target) { + return MSSizeOfJump(reinterpret_cast(target)); +} + +_disused static void MSWriteSkip(uint8_t *¤t, ssize_t size) { + MSWrite(current, 0xe9); + MSWrite(current, size); +} + +_disused static void MSPushPointer(uint8_t *¤t, uintptr_t target) { + MSWrite(current, 0x68); + MSWrite(current, target); + + if (uint32_t high = uint64_t(target) >> 32) { + MSWrite(current, 0xc7); + MSWrite(current, 0x44); + MSWrite(current, 0x24); + MSWrite(current, 0x04); + MSWrite(current, high); + } +} + +_disused static void MSPushPointer(uint8_t *¤t, void *target) { + return MSPushPointer(current, reinterpret_cast(target)); +} + +_disused static void MSWriteCall(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0xff); + MSWrite(current, 0xd0 | target & 0x07); +} + +_disused static void MSWriteCall(uint8_t *¤t, uintptr_t target) { + uintptr_t source(reinterpret_cast(current)); + + if (ia32 || MSIs32BitOffset(target, source + 5)) { + MSWrite(current, 0xe8); + MSWrite(current, target - (source + 5)); + } else { + MSPushPointer(current, target); + + MSWrite(current, 0x83); + MSWrite(current, 0xc4); + MSWrite(current, 0x08); + + MSWrite(current, 0x67); + MSWrite(current, 0xff); + MSWrite(current, 0x54); + MSWrite(current, 0x24); + MSWrite(current, 0xf8); + } +} + +template +_disused static void MSWriteCall(uint8_t *¤t, Type_ *target) { + return MSWriteCall(current, reinterpret_cast(target)); +} + +_disused static void MSWriteJump(uint8_t *¤t, uintptr_t target) { + uintptr_t source(reinterpret_cast(current)); + + if (ia32 || MSIs32BitOffset(target, source + 5)) + MSWriteSkip(current, target - (source + 5)); + else { + MSPushPointer(current, target); + MSWrite(current, 0xc3); + } +} + +_disused static void MSWriteJump(uint8_t *¤t, void *target) { + return MSWriteJump(current, reinterpret_cast(target)); +} + +_disused static void MSWriteJump(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0xff); + MSWrite(current, 0xe0 | target & 0x07); +} + +_disused static void MSWritePop(uint8_t *¤t, uint8_t target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0x58 | target & 0x07); +} + +_disused static size_t MSSizeOfPop(uint8_t target) { + return target >> 3 != 0 ? 2 : 1; +} + +_disused static void MSWritePush(uint8_t *¤t, I$r target) { + if (target >> 3 != 0) + MSWrite(current, 0x40 | (target & 0x08) >> 3); + MSWrite(current, 0x50 | target & 0x07); +} + +_disused static void MSWriteAdd(uint8_t *¤t, I$r target, uint8_t source) { + MSWrite(current, 0x83); + MSWrite(current, 0xc4 | target & 0x07); + MSWrite(current, source); +} + +_disused static void MSWriteSet64(uint8_t *¤t, I$r target, uintptr_t source) { + MSWrite(current, 0x48 | (target & 0x08) >> 3 << 2); + MSWrite(current, 0xb8 | target & 0x7); + MSWrite(current, source); +} + +template +_disused static void MSWriteSet64(uint8_t *¤t, I$r target, Type_ *source) { + return MSWriteSet64(current, target, reinterpret_cast(source)); +} + +_disused static void MSWriteMove64(uint8_t *¤t, uint8_t source, uint8_t target) { + MSWrite(current, 0x48 | (target & 0x08) >> 3 << 2 | (source & 0x08) >> 3); + MSWrite(current, 0x8b); + MSWrite(current, (target & 0x07) << 3 | source & 0x07); +} + +_disused static size_t MSSizeOfMove64() { + return 3; +} + +#endif//SUBSTRATE_X86_HPP diff --git a/app/src/main/jni/Substrate/SymbolFinder.cpp b/app/src/main/jni/Substrate/SymbolFinder.cpp new file mode 100644 index 0000000..07c7607 --- /dev/null +++ b/app/src/main/jni/Substrate/SymbolFinder.cpp @@ -0,0 +1,433 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "SymbolFinder.h" + +#define TAG "MSHook" +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) +/* memory map for libraries */ +#define MAX_NAME_LEN 256 +#define MEMORY_ONLY "[memory]" +struct mm { + char name[MAX_NAME_LEN]; + unsigned long start, end; +}; + +typedef struct symtab *symtab_t; +struct symlist { + Elf32_Sym *sym; /* symbols */ + char *str; /* symbol strings */ + unsigned num; /* number of symbols */ +}; +struct symtab { + struct symlist *st; /* "static" symbols */ + struct symlist *dyn; /* dynamic symbols */ +}; + +static void *xmalloc(size_t size) { + void *p; + p = malloc(size); + if (!p) { + printf(OBFUSCATE("Out of memory\n")); + exit(1); + } + return p; +} + +static int my_pread(int fd, void *buf, size_t count, off_t offset) { + lseek(fd, offset, SEEK_SET); + return read(fd, buf, count); +} + +static struct symlist *get_syms(int fd, Elf32_Shdr *symh, Elf32_Shdr *strh) { + struct symlist *sl, *ret; + int rv; + + ret = NULL; + sl = (struct symlist *) xmalloc(sizeof(struct symlist)); + sl->str = NULL; + sl->sym = NULL; + + /* sanity */ + if (symh->sh_size % sizeof(Elf32_Sym)) { + //printf("elf_error\n"); + goto out; + } + + /* symbol table */ + sl->num = symh->sh_size / sizeof(Elf32_Sym); + sl->sym = (Elf32_Sym *) xmalloc(symh->sh_size); + rv = my_pread(fd, sl->sym, symh->sh_size, symh->sh_offset); + if (0 > rv) { + //perror("read"); + goto out; + } + if (rv != symh->sh_size) { + //printf("elf error\n"); + goto out; + } + + /* string table */ + sl->str = (char *) xmalloc(strh->sh_size); + rv = my_pread(fd, sl->str, strh->sh_size, strh->sh_offset); + if (0 > rv) { + //perror("read"); + goto out; + } + if (rv != strh->sh_size) { + //printf("elf error"); + goto out; + } + + ret = sl; + out: + return ret; +} + +static int do_load(int fd, symtab_t symtab) { + int rv; + size_t size; + Elf32_Ehdr ehdr; + Elf32_Shdr *shdr = NULL, *p; + Elf32_Shdr *dynsymh, *dynstrh; + Elf32_Shdr *symh, *strh; + char *shstrtab = NULL; + int i; + int ret = -1; + + /* elf header */ + rv = read(fd, &ehdr, sizeof(ehdr)); + if (0 > rv) { + LOGD(OBFUSCATE("read\n")); + goto out; + } + if (rv != sizeof(ehdr)) { + LOGD(OBFUSCATE("elf error 1\n")); + goto out; + } + if (strncmp((const char *) ELFMAG, (const char *) ehdr.e_ident, SELFMAG)) { /* sanity */ + LOGD(OBFUSCATE("not an elf\n")); + goto out; + } + if (sizeof(Elf32_Shdr) != ehdr.e_shentsize) { /* sanity */ + LOGD(OBFUSCATE("elf error 2\n")); + goto out; + } + + /* section header table */ + size = ehdr.e_shentsize * ehdr.e_shnum; + shdr = (Elf32_Shdr *) xmalloc(size); + rv = my_pread(fd, shdr, size, ehdr.e_shoff); + if (0 > rv) { + LOGD(OBFUSCATE("read\n")); + goto out; + } + if (rv != size) { + LOGD(OBFUSCATE("elf error 3 %d %d\n"), rv, size); + goto out; + } + + /* section header string table */ + size = shdr[ehdr.e_shstrndx].sh_size; + shstrtab = (char *) xmalloc(size); + rv = my_pread(fd, shstrtab, size, shdr[ehdr.e_shstrndx].sh_offset); + if (0 > rv) { + LOGD(OBFUSCATE("read\n")); + goto out; + } + if (rv != size) { + LOGD(OBFUSCATE("elf error 4 %d %d\n"), rv, size); + goto out; + } + + /* symbol table headers */ + symh = dynsymh = NULL; + strh = dynstrh = NULL; + for (i = 0, p = shdr; i < ehdr.e_shnum; i++, p++) + if (SHT_SYMTAB == p->sh_type) { + if (symh) { + LOGD(OBFUSCATE("too many symbol tables\n")); + goto out; + } + symh = p; + } else if (SHT_DYNSYM == p->sh_type) { + if (dynsymh) { + LOGD(OBFUSCATE("too many symbol tables\n")); + goto out; + } + dynsymh = p; + } else if (SHT_STRTAB == p->sh_type + && !strncmp(shstrtab + p->sh_name, OBFUSCATE(".strtab"), 7)) { + if (strh) { + LOGD(OBFUSCATE("too many string tables\n")); + goto out; + } + strh = p; + } else if (SHT_STRTAB == p->sh_type + && !strncmp(shstrtab + p->sh_name, OBFUSCATE(".dynstr"), 7)) { + if (dynstrh) { + LOGD(OBFUSCATE("too many string tables\n")); + goto out; + } + dynstrh = p; + } + /* sanity checks */ + if ((!dynsymh && dynstrh) || (dynsymh && !dynstrh)) { + LOGD(OBFUSCATE("bad dynamic symbol table\n")); + goto out; + } + if ((!symh && strh) || (symh && !strh)) { + LOGD(OBFUSCATE("bad symbol table\n")); + goto out; + } + if (!dynsymh && !symh) { + LOGD(OBFUSCATE("no symbol table\n")); + goto out; + } + + /* symbol tables */ + if (dynsymh) + symtab->dyn = get_syms(fd, dynsymh, dynstrh); + if (symh) + symtab->st = get_syms(fd, symh, strh); + ret = 0; + out: + free(shstrtab); + free(shdr); + return ret; +} + +static symtab_t load_symtab(char *filename) { + int fd; + symtab_t symtab; + + symtab = (symtab_t) xmalloc(sizeof(*symtab)); + memset(symtab, 0, sizeof(*symtab)); + + fd = open(filename, O_RDONLY); + if (0 > fd) { + LOGE(OBFUSCATE("%s open\n"), __func__); + return NULL; + } + if (0 > do_load(fd, symtab)) { + LOGE(OBFUSCATE("Error ELF parsing %s\n"), filename); + free(symtab); + symtab = NULL; + } + close(fd); + return symtab; +} + +static int load_memmap(pid_t pid, struct mm *mm, int *nmmp) { + size_t buf_size = 0x40000; + char *p_buf = (char *) malloc(buf_size); // increase this if needed for larger "maps" + char name[MAX_NAME_LEN] = {0}; + char *p; + unsigned long start, end; + struct mm *m; + int nmm = 0; + int fd, rv; + int i; + + sprintf(p_buf, OBFUSCATE("/proc/%d/maps"), pid); + fd = open(p_buf, O_RDONLY); + if (0 > fd) { + LOGE(OBFUSCATE("Can't open %s for reading\n"), p_buf); + free(p_buf); + return -1; + } + + /* Zero to ensure data is null terminated */ + memset(p_buf, 0, buf_size); + + p = p_buf; + while (1) { + rv = read(fd, p, buf_size - (p - p_buf)); + if (0 > rv) { + LOGE(OBFUSCATE("%s read"), __FUNCTION__); + free(p_buf); + return -1; + } + if (0 == rv) + break; + p += rv; + if (p - p_buf >= buf_size) { + LOGE(OBFUSCATE("Too many memory mapping\n")); + free(p_buf); + return -1; + } + } + close(fd); + + p = strtok(p_buf, "\n"); + m = mm; + while (p) { + /* parse current map line */ + rv = sscanf(p, OBFUSCATE("%08lx-%08lx %*s %*s %*s %*s %s\n"), &start, &end, name); + + p = strtok(NULL, "\n"); + + if (rv == 2) { + m = &mm[nmm++]; + m->start = start; + m->end = end; + memcpy(m->name, MEMORY_ONLY, sizeof(MEMORY_ONLY)); + continue; + } + + /* search backward for other mapping with same name */ + for (i = nmm - 1; i >= 0; i--) { + m = &mm[i]; + if (!strcmp(m->name, name)) + break; + } + + if (i >= 0) { + if (start < m->start) + m->start = start; + if (end > m->end) + m->end = end; + } else { + /* new entry */ + m = &mm[nmm++]; + m->start = start; + m->end = end; + memcpy(m->name, name, strlen(name)); + } + } + + *nmmp = nmm; + free(p_buf); + return 0; +} + +/* Find libc in MM, storing no more than LEN-1 chars of + its name in NAME and set START to its starting + address. If libc cannot be found return -1 and + leave NAME and START untouched. Otherwise return 0 + and null-terminated NAME. */ +static int find_libname(const char *libn, char *name, int len, unsigned long *start, + struct mm *mm, int nmm) { + int i; + struct mm *m; + char *p; + for (i = 0, m = mm; i < nmm; i++, m++) { + if (!strcmp(m->name, MEMORY_ONLY)) + continue; + p = strrchr(m->name, '/'); + if (!p) + continue; + p++; + if (strncmp(libn, p, strlen(libn))) + continue; + p += strlen(libn); + + /* here comes our crude test -> 'libc.so' or 'libc-[0-9]' */ + if (!strncmp(OBFUSCATE("so"), p, 2) || 1) // || (p[0] == '-' && isdigit(p[1]))) + break; + } + if (i >= nmm) + /* not found */ + return -1; + + *start = m->start; + strncpy(name, m->name, len); + if (strlen(m->name) >= len) + name[len - 1] = '\0'; + + mprotect((void *) m->start, m->end - m->start, + PROT_READ | PROT_WRITE | PROT_EXEC); + return 0; +} + +static int lookup2(struct symlist *sl, unsigned char type, char *name, + unsigned long *val) { + Elf32_Sym *p; + int len; + int i; + + len = strlen(name); + for (i = 0, p = sl->sym; i < sl->num; i++, p++) { + //LOGD("name: %s %x\n", sl->str+p->st_name, p->st_value) + if (!strncmp(sl->str + p->st_name, name, len) + && *(sl->str + p->st_name + len) == 0 + && ELF32_ST_TYPE(p->st_info) == type) { + //if (p->st_value != 0) { + *val = p->st_value; + return 0; + //} + } + } + return -1; +} + +static int lookup_sym(symtab_t s, unsigned char type, char *name, + unsigned long *val) { + if (s->dyn && !lookup2(s->dyn, type, name, val)) + return 0; + if (s->st && !lookup2(s->st, type, name, val)) + return 0; + return -1; +} + +static int lookup_func_sym(symtab_t s, char *name, unsigned long *val) { + return lookup_sym(s, STT_FUNC, name, val); +} + +int find_name(pid_t pid, const char *name, const char *libn, + unsigned long *addr) { + struct mm mm[1000] = {0}; + unsigned long libcaddr; + int nmm; + char libc[1024] = {0}; + symtab_t s; + + if (0 > load_memmap(pid, mm, &nmm)) { + LOGD(OBFUSCATE("cannot read memory map\n")); + return -1; + } + if (0 + > find_libname((char *) libn, (char *) libc, sizeof(libc), + &libcaddr, mm, nmm)) { + LOGD(OBFUSCATE("cannot find lib: %s\n"), libn); + return -1; + } + //LOGD("lib: >%s<\n", libc) + s = load_symtab(libc); + if (!s) { + LOGD(OBFUSCATE("cannot read symbol table\n")); + return -1; + } + if (0 > lookup_func_sym(s, (char *) name, addr)) { + LOGD(OBFUSCATE("cannot find function: %s\n"), name); + return -1; + } + *addr += libcaddr; + return 0; +} + +int find_libbase(pid_t pid, const char *libn, unsigned long *addr) { + struct mm mm[1000] = {0}; + unsigned long libcaddr; + int nmm; + char libc[1024] = {0}; + symtab_t s; + + if (0 > load_memmap(pid, mm, &nmm)) { + LOGD(OBFUSCATE("cannot read memory map\n")); + return -1; + } + if (0 > find_libname(libn, libc, sizeof(libc), &libcaddr, mm, nmm)) { + LOGD(OBFUSCATE("cannot find lib\n")); + return -1; + } + *addr = libcaddr; + return 0; +} diff --git a/app/src/main/jni/Substrate/SymbolFinder.h b/app/src/main/jni/Substrate/SymbolFinder.h new file mode 100644 index 0000000..7b99910 --- /dev/null +++ b/app/src/main/jni/Substrate/SymbolFinder.h @@ -0,0 +1,8 @@ +#ifndef SYMBOL_FINDER +#define SYMBOL_FINDER + +#include + +extern int find_name(pid_t pid, const char *name,const char *libn, unsigned long *addr); +extern int find_libbase(pid_t pid, const char *libn, unsigned long *addr); +#endif \ No newline at end of file diff --git a/app/src/main/jni/Substrate/hde64.c b/app/src/main/jni/Substrate/hde64.c new file mode 100644 index 0000000..d69f0c6 --- /dev/null +++ b/app/src/main/jni/Substrate/hde64.c @@ -0,0 +1,332 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#include +#include + +#include "hde64.h" +#include "table64.h" + +unsigned int hde64_disasm(const void *code, hde64s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; + uint8_t op64 = 0; + + memset(hs,0,sizeof(hde64s)); + char *tmp=(char*)hs; + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((c & 0xf0) == 0x40) { + hs->flags |= F_PREFIX_REX; + if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) + op64++; + hs->rex_r = (c & 7) >> 2; + hs->rex_x = (c & 3) >> 1; + hs->rex_b = c & 1; + if (((c = *p++) & 0xf0) == 0x40) { + opcode = c; + goto error_opcode; + } + } + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + op64++; + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + error_opcode: + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde64_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde64_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde64_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde64_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde64_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if (*ht++ & pref && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + } + + if (m_mod != 3 && m_rm == 4) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (op64) { + hs->flags |= F_IMM64; + hs->imm.imm64 = *(uint64_t *)p; + p += 8; + } else if (!(pref & PRE_66)) { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else + goto imm16_ok; + } + + + if (cflags & C_IMM16) { + imm16_ok: + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} diff --git a/app/src/main/jni/Substrate/hde64.h b/app/src/main/jni/Substrate/hde64.h new file mode 100644 index 0000000..2fcc4cb --- /dev/null +++ b/app/src/main/jni/Substrate/hde64.h @@ -0,0 +1,112 @@ +/* + * Hacker Disassembler Engine 64 + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde64.h: C/C++ header file + * + */ + +#ifndef _HDE64_H_ +#define _HDE64_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_IMM64 0x00000020 +#define F_DISP8 0x00000040 +#define F_DISP16 0x00000080 +#define F_DISP32 0x00000100 +#define F_RELATIVE 0x00000200 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_REX 0x40000000 +#define F_PREFIX_ANY 0x7f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t rex; + uint8_t rex_w; + uint8_t rex_r; + uint8_t rex_x; + uint8_t rex_b; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + uint64_t imm64; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde64s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde64_disasm(const void *code, hde64s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE64_H_ */ diff --git a/app/src/main/jni/Substrate/table64.h b/app/src/main/jni/Substrate/table64.h new file mode 100644 index 0000000..144f290 --- /dev/null +++ b/app/src/main/jni/Substrate/table64.h @@ -0,0 +1,74 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xfd +#define DELTA_FPU_MODRM 0x104 +#define DELTA_PREFIXES 0x13c +#define DELTA_OP_LOCK_OK 0x1ae +#define DELTA_OP2_LOCK_OK 0x1c6 +#define DELTA_OP_ONLY_MEM 0x1d8 +#define DELTA_OP2_ONLY_MEM 0x1e7 + +unsigned char hde64_table[] = { + 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, + 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, + 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, + 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, + 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, + 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, + 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, + 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, + 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, + 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, + 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, + 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, + 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, + 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, + 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, + 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, + 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, + 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, + 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, + 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, + 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, + 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, + 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, + 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, + 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, + 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, + 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, + 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, + 0x00,0xf0,0x02,0x00 +}; diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..1f6bb29 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..0d025f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..b0fc226 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..4726d53 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..dffca36 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..97f1be4 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..dae5e08 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..6ebcfbd Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..14ed0af Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..040a07c Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..d8ae031 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..45d47ee Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..beed3cd Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..b35a435 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #202020 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..7a355e2 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + LGL Mod Menu + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..d13d67a --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,7 @@ + + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..3756278 --- /dev/null +++ b/build.gradle @@ -0,0 +1,4 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + alias(libs.plugins.android.application) apply false +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..4387edc --- /dev/null +++ b/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..ceb0910 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,22 @@ +[versions] +agp = "8.13.2" +junit = "4.13.2" +junitVersion = "1.3.0" +espressoCore = "3.7.0" +appcompat = "1.7.1" +material = "1.13.0" +activity = "1.12.2" +constraintlayout = "2.2.1" + +[libraries] +junit = { group = "junit", name = "junit", version.ref = "junit" } +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } +activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8bdaf60 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..8fd263c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,8 @@ +#Sat Jan 31 21:42:02 BDT 2026 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..ef07e01 --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..db3a6ac --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..af639b2 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,23 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "Native-LGL" +include ':app'