├── .gitignore ├── README.md ├── SimpleApkV2.java ├── build.gradle ├── fill_v_value.py └── src └── main ├── AndroidManifest.xml ├── java ├── me │ └── piebridge │ │ ├── FakeFragment.java │ │ ├── Genuine.java │ │ ├── GenuineActivity.java │ │ └── GenuineApplication.java └── nop.java ├── jni ├── Android.mk ├── Application.mk ├── am-proxy.c ├── am-proxy.h ├── anti-xposed.c ├── anti-xposed.h ├── apk-sign-v2.c ├── apk-sign-v2.h ├── art.h ├── bitmap.c ├── bitmap.h ├── classloader.cpp ├── classloader.h ├── common.c ├── common.h ├── dex-path-list.cpp ├── dex-path-list.h ├── epic-field.c ├── epic-field.h ├── epic-method.c ├── epic-method.h ├── epic.c ├── epic.h ├── genuine.c ├── genuine_extra.c ├── genuine_extra.h ├── handle-error.c ├── handle-error.h ├── hash.c ├── hash.h ├── inline.c ├── inline.h ├── native-activity.c ├── openat.c ├── openat.h ├── path.c ├── path.h ├── plt.c ├── plt.h ├── pm.c ├── pm.h ├── xposed-nop.cpp └── xposed-nop.h └── res ├── values-zh-rCN ├── genuine.xml └── strings.xml ├── values-zh-rTW ├── genuine.xml └── strings.xml └── values ├── genuine.xml └── strings.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /*.iml 3 | /.externalNativeBuild 4 | /src/main/obj 5 | /src/main/libs 6 | /SimpleApkV2.class 7 | /src/main/jni/genuine.h 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What's `genuine`? 2 | 3 | A module anti Xposed hook, anti fake signature, anti virtual app (binder proxy), and optional anti odex, anti overlay. 4 | 5 | Since 2019-03, Genuine switch to pure c for hide itself. If you want to hide your package name and / or class name, contact me, or do it like `fill_XXX` in `genuine.c`. 6 | 7 | - Xposed hook: hook any Java method in [Xposed](https://github.com/rovo89/XposedBridge). 8 | - [EdXposed](https://github.com/ElderDrivers/EdXposed) 9 | - (optional) [TaiChi](https://github.com/taichi-framework/TaiChi), closed source 10 | 11 | - fake signature: fake your signature. 12 | `genuine` module requires usage of [Apk Sign v2](https://source.android.com/security/apksigning/v2) or [Apk Sign v3](https://source.android.com/security/apksigning/v3). 13 | 14 | - (optional) PLT Hook: currently only check `jniRegisterNativeMethods` by flag `CHECK_HOOK`. 15 | 16 | - virtual app (binder proxy): run your app in virtual app, like [VirtualApp](https://github.com/asLody/VirtualApp). 17 | 18 | - (optional) odex: modify odex codes without modify apk, like [URET](https://www.uret.in/) 19 | 20 | - (optional) overlay: overlay resources, prevent from loading apk from `/data`. 21 | 22 | # How to use? 23 | 24 | 1. run `java SimpleApkV2 `, save output to `src/main/jni/genuine.h` 25 | 26 | 2. modify `build.gradle` for `rootProject.XXX` 27 | 28 | 3. search `FIXME` in `src/main/jni/genuine.c` 29 | 30 | 4. define your own methods in `src/main/jni/genuine_extra.c` 31 | > don't forget to update your own class 32 | 33 | # features 34 | 35 | ```c 36 | /* define to turn off maps check */ 37 | // #define NO_CHECK_MAPS 38 | 39 | #ifndef NO_CHECK_MAPS 40 | /* define to anti odex */ 41 | // #define ANTI_ODEX 42 | 43 | /* define to anti overlay */ 44 | // #define ANTI_OVERLAY 45 | #endif 46 | 47 | /* define to check plt hook for jniRegisterNativeMethods */ 48 | // #define CHECK_HOOK 49 | 50 | /* define to turn off xposed check */ 51 | // #define NO_CHECK_XPOSED 52 | 53 | /* define to turn on xposed-epic check 54 | */ 55 | // #define CHECK_XPOSED_EPIC 56 | 57 | /* check use arm32 on arm64-v8a */ 58 | // #define CHECK_ARM64 59 | 60 | /* genuine false handler */ 61 | // #define GENUINE_FALSE_CRASH 62 | // #define GENUINE_FALSE_NATIVE 63 | 64 | /* genuine fake handler */ 65 | // #define GENUINE_FAKE_CRASH 66 | #define GENUINE_FAKE_NATIVE 67 | 68 | /* genuine overlay handler */ 69 | // #define GENUINE_OVERLAY_CRASH 70 | // #define GENUINE_OVERLAY_NATIVE 71 | 72 | /* genuine odex handler */ 73 | // #define GENUINE_ODEX_CRASH 74 | // #define GENUINE_ODEX_NATIVE 75 | 76 | /* genuine dex handler */ 77 | // #define GENUINE_DEX_CRASH 78 | // #define GENUINE_DEX_NATIVE 79 | 80 | /* genuine proxy handler */ 81 | // #define GENUINE_PROXY_CRASH 82 | // #define GENUINE_PROXY_NATIVE 83 | 84 | /* genuine error handler */ 85 | // #define GENUINE_ERROR_CRASH 86 | #define GENUINE_ERROR_NATIVE 87 | 88 | /* genuine fatal handler */ 89 | // #define GENUINE_FATAL_CRASH 90 | #define GENUINE_FATAL_NATIVE 91 | 92 | /* genuine noapk handler */ 93 | // #define GENUINE_NOAPK_CRASH 94 | #define GENUINE_NOAPK_NATIVE 95 | 96 | ``` 97 | 98 | # fill_xxxx 99 | 100 | ``` 101 | python3 fill_v_value.py v 102 | ``` 103 | 104 | # practices 105 | 106 | 1. make sure libgenuine.so always loaded 107 | 108 | 2. crash or show native activity for fake, error, fatal 109 | 110 | # And license? 111 | 112 | [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). 113 | 114 | For commercial usage, contact me. However, if you are individial, or enterprise less than 5 staff, you can use it under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). 115 | -------------------------------------------------------------------------------- /SimpleApkV2.java: -------------------------------------------------------------------------------- 1 | import java.io.IOException; 2 | import java.io.RandomAccessFile; 3 | import java.io.UnsupportedEncodingException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.ByteOrder; 6 | import java.util.Arrays; 7 | 8 | /** 9 | * Created by thom on 2018/11/2. 10 | */ 11 | public class SimpleApkV2 { 12 | 13 | private static final byte[] APK_V2_MAGIC = {'A', 'P', 'K', ' ', 'S', 'i', 'g', ' ', 14 | 'B', 'l', 'o', 'c', 'k', ' ', '4', '2'}; 15 | 16 | private static int[] getApkSignV2(String path) throws IOException { 17 | try ( 18 | RandomAccessFile apk = new RandomAccessFile(path, "r") 19 | ) { 20 | ByteBuffer buffer = ByteBuffer.allocate(0x10); 21 | buffer.order(ByteOrder.LITTLE_ENDIAN); 22 | 23 | apk.seek(apk.length() - 0x6); 24 | apk.readFully(buffer.array(), 0x0, 0x6); 25 | int offset = buffer.getInt(); 26 | if (buffer.getShort() != 0) { 27 | throw new UnsupportedEncodingException("no zip"); 28 | } 29 | 30 | apk.seek(offset - 0x10); 31 | apk.readFully(buffer.array(), 0x0, 0x10); 32 | 33 | if (!Arrays.equals(buffer.array(), APK_V2_MAGIC)) { 34 | throw new UnsupportedEncodingException("no apk v2"); 35 | } 36 | 37 | // Read and compare size fields 38 | apk.seek(offset - 0x18); 39 | apk.readFully(buffer.array(), 0x0, 0x8); 40 | buffer.rewind(); 41 | int size = (int) buffer.getLong(); 42 | 43 | ByteBuffer block = ByteBuffer.allocate(size + 0x8); 44 | block.order(ByteOrder.LITTLE_ENDIAN); 45 | apk.seek(offset - block.capacity()); 46 | apk.readFully(block.array(), 0x0, block.capacity()); 47 | 48 | if (size != block.getLong()) { 49 | throw new UnsupportedEncodingException("no apk v2"); 50 | } 51 | 52 | while (block.remaining() > 24) { 53 | size = (int) block.getLong(); 54 | if (block.getInt() == 0x7109871a) { 55 | // signer-sequence length, signer length, signed data length 56 | block.position(block.position() + 12); 57 | size = block.getInt(); // digests-sequence length 58 | 59 | // digests, certificates length 60 | block.position(block.position() + size + 0x4); 61 | 62 | size = block.getInt(); // certificate length 63 | break; 64 | } else { 65 | block.position(block.position() + size - 0x4); 66 | } 67 | } 68 | 69 | int hash = 1; 70 | for (int i = 0; i < size; ++i) { 71 | hash = 31 * hash + block.get(); 72 | } 73 | 74 | return new int[] {size, hash}; 75 | } 76 | } 77 | 78 | private static String formatName(String name) { 79 | int length = name.length(); 80 | StringBuilder sb = new StringBuilder(); 81 | sb.append("{"); 82 | for (int i = 0; i < length; ++i) { 83 | int c = name.charAt(i) ^ ((i + length) % 20); 84 | sb.append("0x"); 85 | sb.append(Integer.toHexString(c)); 86 | sb.append(", "); 87 | } 88 | sb.append("0x0"); 89 | sb.append("}"); 90 | return sb.toString(); 91 | } 92 | 93 | public static void main(String[] args) throws IOException { 94 | System.out.format("#define GENUINE_NAME %s%n", formatName(args[0])); 95 | int[] sizeAndHash = getApkSignV2(args[1]); 96 | System.out.format("#define GENUINE_SIZE 0x%04x%n", sizeAndHash[0]); 97 | System.out.format("#define GENUINE_HASH 0x%04x%n", sizeAndHash[1] ^ 0x14131211); 98 | String extra = "\n" + 99 | "// #define GET_GENUINE_CLASS_NAME function_name_for_get_genuine_class_name\n" + 100 | "// #define GET_GENUINE_PACKAGE_NAME function_name_for_get_genuine_package_name\n" + 101 | "\n" + 102 | "/* define to turn off maps check */\n" + 103 | "// #define NO_CHECK_MAPS\n" + 104 | "\n" + 105 | "#ifndef NO_CHECK_MAPS\n" + 106 | "/* define to anti odex */\n" + 107 | "// #define ANTI_ODEX\n" + 108 | "\n" + 109 | "/* define to anti overlay */\n" + 110 | "// #define ANTI_OVERLAY\n" + 111 | "#endif\n" + 112 | "\n" + 113 | "/* define to check plt hook for jniRegisterNativeMethods */\n" + 114 | "// #define CHECK_JNI_REGISTER_NATIVE_METHODS\n" + 115 | "\n" + 116 | "/* define to turn off xposed check */\n" + 117 | "// #define NO_CHECK_XPOSED\n" + 118 | "\n" + 119 | "/* define to turn on xposed-epic check\n" + 120 | " */\n" + 121 | "// #define CHECK_XPOSED_EPIC\n" + 122 | "\n" + 123 | "/* genuine false handler */\n" + 124 | "// #define GENUINE_FALSE_CRASH\n" + 125 | "// #define GENUINE_FALSE_NATIVE\n" + 126 | "\n" + 127 | "/* genuine fake handler */\n" + 128 | "// #define GENUINE_FAKE_CRASH\n" + 129 | "#define GENUINE_FAKE_NATIVE\n" + 130 | "\n" + 131 | "/* genuine overlay handler */\n" + 132 | "// #define GENUINE_OVERLAY_CRASH\n" + 133 | "// #define GENUINE_OVERLAY_NATIVE\n" + 134 | "\n" + 135 | "/* genuine odex handler */\n" + 136 | "// #define GENUINE_ODEX_CRASH\n" + 137 | "// #define GENUINE_ODEX_NATIVE\n" + 138 | "\n" + 139 | "/* genuine dex handler */\n" + 140 | "// #define GENUINE_DEX_CRASH\n" + 141 | "// #define GENUINE_DEX_NATIVE\n" + 142 | "\n" + 143 | "/* genuine proxy handler */\n" + 144 | "// #define GENUINE_PROXY_CRASH\n" + 145 | "// #define GENUINE_PROXY_NATIVE\n" + 146 | "\n" + 147 | "/* genuine error handler */\n" + 148 | "// #define GENUINE_ERROR_CRASH\n" + 149 | "#define GENUINE_ERROR_NATIVE\n" + 150 | "\n" + 151 | "/* genuine fatal handler */\n" + 152 | "// #define GENUINE_FATAL_CRASH\n" + 153 | "#define GENUINE_FATAL_NATIVE\n" + 154 | "\n" + 155 | "/* genuine noapk handler */\n" + 156 | "// #define GENUINE_NOAPK_CRASH\n" + 157 | "#define GENUINE_NOAPK_NATIVE"; 158 | System.out.print(extra); 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | maven { 5 | url 'https://dl.google.com/dl/android/maven2' 6 | } 7 | } 8 | dependencies { 9 | // view-source:https://dl.google.com/dl/android/maven2/com/android/tools/build/gradle/maven-metadata.xml 10 | classpath 'com.android.tools.build:gradle:4.2.2' // 7.2.0 11 | // view-source:https://repo1.maven.org/maven2/com/github/dcendents/android-maven-gradle-plugin/maven-metadata.xml 12 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | mavenCentral() 19 | maven { 20 | url 'https://dl.google.com/dl/android/maven2/' 21 | } 22 | } 23 | } 24 | 25 | if (!rootProject.hasProperty('compileSdkVersion')) { 26 | project.ext.compileSdkVersion = 32 27 | project.ext.targetSdkVersion = 32 28 | project.ext.minSdkVersion = 16 29 | project.ext.versionCode = 1 30 | project.ext.versionName = '1' 31 | project.ext.appcompatVersion = '1.4.1' 32 | project.ext.buildToolsVersion = '30.0.3' // build tools 31.0.0+ requires agp 7.0.0+ 33 | project.ext.ndkVersion = '23.1.7779620' 34 | } else { 35 | project.ext.compileSdkVersion = rootProject.compileSdkVersion 36 | project.ext.targetSdkVersion = rootProject.targetSdkVersion 37 | project.ext.minSdkVersion = rootProject.minSdkVersion 38 | project.ext.versionCode = rootProject.versionCode 39 | project.ext.versionName = rootProject.versionName 40 | project.ext.appcompatVersion = rootProject.appcompatVersion 41 | project.ext.buildToolsVersion = rootProject.buildToolsVersion 42 | project.ext.ndkVersion = rootProject.ndkVersion 43 | } 44 | 45 | apply plugin: 'com.android.library' 46 | 47 | android { 48 | compileSdkVersion project.compileSdkVersion 49 | buildToolsVersion project.buildToolsVersion 50 | ndkVersion project.ndkVersion 51 | 52 | defaultConfig { 53 | minSdkVersion project.minSdkVersion 54 | targetSdkVersion project.targetSdkVersion 55 | versionCode project.versionCode 56 | versionName project.versionName 57 | buildConfigField 'int', 'APP_VERSION_CODE', "${versionCode}" 58 | buildConfigField 'String', 'APP_VERSION_NAME', "\"${versionName}\"" 59 | ndk { 60 | abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86' 61 | } 62 | externalNativeBuild { 63 | ndkBuild { 64 | cFlags '-DVERSION=' + project.versionCode 65 | } 66 | } 67 | } 68 | 69 | externalNativeBuild { 70 | ndkBuild { 71 | path 'src/main/jni/Android.mk' 72 | } 73 | } 74 | 75 | compileOptions { 76 | sourceCompatibility JavaVersion.VERSION_1_8 77 | targetCompatibility JavaVersion.VERSION_1_8 78 | } 79 | } 80 | 81 | dependencies { 82 | implementation "androidx.appcompat:appcompat:${project.appcompatVersion}" 83 | } 84 | 85 | task deleteNativeBuild(type: Delete) { 86 | delete file('.externalNativeBuild') 87 | delete file('.cxx') 88 | } 89 | 90 | clean.finalizedBy deleteNativeBuild 91 | -------------------------------------------------------------------------------- /fill_v_value.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | # Author: Liu DongMiao 4 | # Created : Sun 20 May 2018 12:04:18 AM CST 5 | # Modified : Wed 04 May 2022 12:35:38 AM CST 6 | 7 | import sys 8 | 9 | k = sys.argv[1] 10 | v = sys.argv[2] 11 | 12 | l = len(v) 13 | 14 | P = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137] 15 | def find_m(x): 16 | p = P[:-2] 17 | low = 0 18 | high = len(p) - 1 19 | if x > p[high]: 20 | return p[high] 21 | elif x <= p[low]: 22 | return p[low] 23 | 24 | while low <= high: 25 | mid = int(low + (high - low) / 2) 26 | if x > p[mid]: 27 | if x < p[mid + 1]: 28 | return p[mid] 29 | else: 30 | low = mid + 1 31 | elif x < p[mid]: 32 | high = mid - 1 33 | else: 34 | return p[mid - 1] 35 | else: 36 | raise 37 | 38 | m = find_m(l) 39 | method = '' 40 | if '(' in v or ';' in v: 41 | method = 'signature' 42 | else: 43 | method = v.replace('/', '_').replace('%', '').replace(' ', '_') 44 | if len(sys.argv) > 3: 45 | method = sys.argv[3] 46 | if '(' in v or ';' in v and not method.endswith('_signature'): 47 | method += '_signature' 48 | print('static inline void fill_%s(char %s[]) {' % (method, k)) 49 | print(' // %s' % v) 50 | print(' static unsigned int m = 0;') 51 | print(''' 52 | if (m == 0) { 53 | m = %d; 54 | } else if (m == %d) { 55 | m = %d; 56 | } 57 | ''' % (m, P[P.index(m) + 1], P[P.index(m) + 2])) 58 | 59 | for x in range(l): 60 | o = ord(v[x]) ^ ((x + l) % m) 61 | s = repr(chr(o)) 62 | if s == '''"'"''': 63 | s = "'\\''" 64 | print(" %s[0x%x] = %s%s;" % (k, x, o > 127 and "(char) " or "", s)) 65 | 66 | print(''' for (unsigned int i = 0; i < 0x%x; ++i) { 67 | %s[i] ^= ((i + 0x%x) %% m); 68 | } 69 | %s[0x%x] = '\\0';''' % (l, k, l, k, l)) 70 | print('}') 71 | 72 | # vim: set sta sw=4 et: 73 | -------------------------------------------------------------------------------- /src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 10 | 11 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/me/piebridge/FakeFragment.java: -------------------------------------------------------------------------------- 1 | package me.piebridge; 2 | 3 | import android.app.Activity; 4 | import android.app.Dialog; 5 | import android.content.DialogInterface; 6 | import android.os.Bundle; 7 | import android.view.KeyEvent; 8 | 9 | import androidx.appcompat.app.AlertDialog; 10 | import androidx.fragment.app.DialogFragment; 11 | import androidx.fragment.app.Fragment; 12 | import androidx.fragment.app.FragmentManager; 13 | 14 | /** 15 | * Created by thom on 2018/10/31. 16 | */ 17 | public class FakeFragment extends DialogFragment 18 | implements DialogInterface.OnKeyListener, DialogInterface.OnClickListener { 19 | 20 | private static final String MESSAGE = "MESSAGE"; 21 | 22 | public FakeFragment() { 23 | setArguments(new Bundle()); 24 | setStyle(STYLE_NO_TITLE, 0); 25 | } 26 | 27 | @Override 28 | public Dialog onCreateDialog(Bundle savedInstanceState) { 29 | AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); 30 | Bundle arguments = getArguments(); 31 | builder.setMessage(getString(arguments.getInt(MESSAGE))); 32 | builder.setPositiveButton(android.R.string.ok, this); 33 | builder.setOnKeyListener(this); 34 | return builder.create(); 35 | } 36 | 37 | @Override 38 | public void onCancel(DialogInterface dialog) { 39 | finishActivity(); 40 | } 41 | 42 | private void finishActivity() { 43 | Activity activity = getActivity(); 44 | if (activity != null) { 45 | activity.finish(); 46 | System.exit(0); 47 | } 48 | } 49 | 50 | @Override 51 | public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { 52 | if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { 53 | finishActivity(); 54 | } 55 | return false; 56 | } 57 | 58 | public void setMessage(int resId) { 59 | getArguments().putInt(MESSAGE, resId); 60 | } 61 | 62 | public int getMessage() { 63 | return getArguments().getInt(MESSAGE); 64 | } 65 | 66 | @Override 67 | public void onClick(DialogInterface dialog, int which) { 68 | finishActivity(); 69 | } 70 | 71 | @Override 72 | public void onActivityCreated(Bundle savedInstanceState) { 73 | // https://stackoverflow.com/a/27084544 74 | if (super.getDialog() == null) { 75 | super.setShowsDialog(false); 76 | } 77 | try { 78 | super.onActivityCreated(savedInstanceState); 79 | } catch (NullPointerException e) { // NOSONAR 80 | // do nothing 81 | } 82 | } 83 | 84 | @Override 85 | public void show(FragmentManager manager, String tag) { 86 | Fragment fragment = manager.findFragmentByTag(tag); 87 | if (fragment == null || !fragment.isAdded()) { 88 | super.show(manager, tag); 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/me/piebridge/Genuine.java: -------------------------------------------------------------------------------- 1 | package me.piebridge; 2 | 3 | import androidx.annotation.Keep; 4 | 5 | import java.lang.reflect.Member; 6 | 7 | /** 8 | * Created by thom on 2018/10/31. 9 | */ 10 | @Keep 11 | public class Genuine { 12 | 13 | /* check true, return defined VERSION */ 14 | public static final int CHECK_TRUE = 0; 15 | 16 | /* cannot make sure check result */ 17 | public static final int CHECK_FALSE = 1; 18 | 19 | /* fake signature */ 20 | public static final int CHECK_FAKE = 2; 21 | 22 | /* third party apk loaded */ 23 | public static final int CHECK_OVERLAY = 3; 24 | 25 | /* odex is tampered */ 26 | public static final int CHECK_ODEX = 4; 27 | 28 | /* third party dex loaded */ 29 | public static final int CHECK_DEX = 5; 30 | 31 | /* binder proxy */ 32 | public static final int CHECK_PROXY = 6; 33 | 34 | /* cannot check */ 35 | public static final int CHECK_ERROR = 7; 36 | 37 | /* fatal hook */ 38 | public static final int CHECK_FATAL = 8; 39 | 40 | /* noapk */ 41 | public static final int CHECK_NOAPK = 9; 42 | 43 | static { 44 | System.loadLibrary("genuine"); 45 | } 46 | 47 | private Genuine() { 48 | 49 | } 50 | 51 | private static native Object invoke(Member m, int i, Object a, Object t, Object[] as) throws Throwable; 52 | 53 | /* refer CHECK_XXX */ 54 | public static native int version(); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/me/piebridge/GenuineActivity.java: -------------------------------------------------------------------------------- 1 | package me.piebridge; 2 | 3 | import android.os.Bundle; 4 | 5 | import androidx.appcompat.app.AppCompatActivity; 6 | import me.piebridge.genuine.R; 7 | 8 | /** 9 | * Created by thom on 2018/10/31. 10 | */ 11 | public class GenuineActivity extends AppCompatActivity { 12 | 13 | private static final String FRAGMENT_FAKE = "fragment-fake"; 14 | 15 | private static final int MAGIC = 0xc91e8d1e; 16 | 17 | private int magic; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | GenuineApplication application = (GenuineApplication) getApplication(); 23 | boolean fake = application.isFake(); 24 | magic = -Integer.parseInt("f86r4y", Character.MAX_RADIX); 25 | if (fake) { 26 | showFake(R.string.unsupported_modified ^ MAGIC); 27 | } 28 | } 29 | 30 | protected final boolean isFake() { 31 | GenuineApplication application = (GenuineApplication) getApplication(); 32 | return application.isFake(); 33 | } 34 | 35 | private void showFake(int resId) { 36 | FakeFragment fragment = (FakeFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_FAKE); 37 | if (fragment == null || fragment.getMessage() != (resId ^ magic)) { 38 | if (fragment != null) { 39 | fragment.dismiss(); 40 | } 41 | fragment = new FakeFragment(); 42 | fragment.setMessage(resId ^ magic); 43 | fragment.show(getSupportFragmentManager(), FRAGMENT_FAKE); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/me/piebridge/GenuineApplication.java: -------------------------------------------------------------------------------- 1 | package me.piebridge; 2 | 3 | import android.app.Application; 4 | 5 | import me.piebridge.genuine.BuildConfig; 6 | 7 | /** 8 | * Created by thom on 2018/10/31. 9 | */ 10 | public class GenuineApplication extends Application { 11 | 12 | static { 13 | System.loadLibrary(String.valueOf(new char[] { 14 | 'g', 'e', 'n', 'u', 'i', 'n', 'e' 15 | })); 16 | } 17 | 18 | private boolean mFake; 19 | 20 | @Override 21 | public void onCreate() { 22 | super.onCreate(); 23 | mFake = BuildConfig.APP_VERSION_CODE != Genuine.version(); 24 | } 25 | 26 | public final void setFake() { 27 | mFake = true; 28 | } 29 | 30 | public final boolean isFake() { 31 | return mFake; 32 | } 33 | 34 | public final void setGenuine() { 35 | mFake = false; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/nop.java: -------------------------------------------------------------------------------- 1 | import androidx.annotation.Keep; 2 | 3 | @Keep 4 | public class nop { 5 | 6 | private static void nop() { 7 | android.util.Log.w("nop", "nop"); 8 | } 9 | 10 | private static void oop() { 11 | android.util.Log.i("nop", "oop"); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | LOCAL_MODULE := genuine 5 | LOCAL_CFLAGS += -Oz -Wall -Wextra -Wshadow -Werror -fvisibility=hidden 6 | LOCAL_LDFLAGS := -Wl,--hash-style=both 7 | LOCAL_SRC_FILES := genuine.c genuine_extra.c plt.c inline.c 8 | LOCAL_SRC_FILES += openat.c 9 | LOCAL_SRC_FILES += am-proxy.c pm.c 10 | LOCAL_SRC_FILES += anti-xposed.c apk-sign-v2.c 11 | LOCAL_SRC_FILES += epic.c classloader.cpp hash.c 12 | LOCAL_SRC_FILES += epic-method.c epic-field.c 13 | LOCAL_SRC_FILES += dex-path-list.cpp 14 | LOCAL_SRC_FILES += xposed-nop.cpp 15 | LOCAL_SRC_FILES += common.c handle-error.c native-activity.c bitmap.c 16 | LOCAL_SRC_FILES += path.c 17 | ifeq (,$(wildcard $(LOCAL_PATH)/genuine.h)) 18 | $(warning "no genuine.h, won't check genuine") 19 | endif 20 | LOCAL_LDLIBS := -llog -landroid -ljnigraphics -lz 21 | include $(BUILD_SHARED_LIBRARY) 22 | -------------------------------------------------------------------------------- /src/main/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_PLATFORM := android-16 2 | APP_STL := system 3 | -------------------------------------------------------------------------------- /src/main/jni/am-proxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/15. 3 | // 4 | 5 | #ifndef BREVENT_AM_PROXY_H 6 | #define BREVENT_AM_PROXY_H 7 | 8 | #include 9 | #include 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | bool isAmProxy(JNIEnv *env, int sdk); 16 | 17 | #ifdef __cplusplus 18 | } 19 | #endif 20 | 21 | #endif //BREVENT_AM_PROXY_H 22 | -------------------------------------------------------------------------------- /src/main/jni/anti-xposed.h: -------------------------------------------------------------------------------- 1 | #ifndef BREVENT_ANTI_XPOSED_H 2 | #define BREVENT_ANTI_XPOSED_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | jboolean antiXposed(JNIEnv *env, jclass clazz, int sdk, bool *xposed); 11 | 12 | jclass findXposedBridge(JNIEnv *env, jobject classLoader); 13 | 14 | jclass findLoadedClass(JNIEnv *env, jobject classLoader, const char *name); 15 | 16 | bool disableXposedBridge(JNIEnv *env, jclass classXposedBridge); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | 22 | #endif //BREVENT_ANTI_XPOSED_H 23 | -------------------------------------------------------------------------------- /src/main/jni/apk-sign-v2.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/8. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef MAIN 10 | #include 11 | #ifdef __APPLE__ 12 | #include 13 | #include 14 | #endif 15 | #else 16 | 17 | #include "common.h" 18 | #include "openat.h" 19 | 20 | #endif 21 | 22 | #include "apk-sign-v2.h" 23 | 24 | static bool isApkSigBlock42(const char *buffer) { 25 | // APK Sig Block 42 26 | return *buffer == 'A' 27 | && *++buffer == 'P' 28 | && *++buffer == 'K' 29 | && *++buffer == ' ' 30 | && *++buffer == 'S' 31 | && *++buffer == 'i' 32 | && *++buffer == 'g' 33 | && *++buffer == ' ' 34 | && *++buffer == 'B' 35 | && *++buffer == 'l' 36 | && *++buffer == 'o' 37 | && *++buffer == 'c' 38 | && *++buffer == 'k' 39 | && *++buffer == ' ' 40 | && *++buffer == '4' 41 | && *++buffer == '2'; 42 | } 43 | 44 | #if defined(MAIN) && defined(__APPLE__) 45 | static unsigned char digest[CC_SHA1_DIGEST_LENGTH]; 46 | #endif 47 | 48 | static unsigned calcHash(int fd, unsigned size) { 49 | signed char c; 50 | unsigned i = 0; 51 | int hash = 1; 52 | 53 | #if defined(MAIN) && defined(__APPLE__) 54 | CC_SHA1_CTX context; 55 | CC_SHA1_Init(&context); 56 | memset(digest, 0, CC_SHA1_DIGEST_LENGTH); 57 | #endif 58 | 59 | while (i < size) { 60 | read(fd, &c, 0x1); 61 | #if defined(MAIN) && defined(__APPLE__) 62 | CC_SHA1_Update(&context, &c, 1); 63 | #endif 64 | hash = 31 * hash + c; 65 | ++i; 66 | } 67 | #if defined(MAIN) && defined(__APPLE__) 68 | CC_SHA1_Final(digest, &context); 69 | #endif 70 | return (unsigned) hash; 71 | } 72 | 73 | #ifdef MAIN 74 | static void showCert(unsigned size, unsigned hash, int sign, const char *prefix) { 75 | printf("%ssize: 0x%04x, hash: 0x%08x", prefix, size, hash ^ 0x14131211u); 76 | #ifdef __APPLE__ 77 | printf(", sha1: "); 78 | for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; ++i) { 79 | printf("%02x", digest[i]); 80 | } 81 | #endif 82 | printf(", sign v%d\n", sign); 83 | } 84 | #endif 85 | 86 | int checkSignature(const char *path) { 87 | unsigned char buffer[0x11] = {0}; 88 | uint32_t size4; 89 | uint64_t size8, size_of_block; 90 | 91 | #ifdef DEBUG 92 | LOGI("check signature for %s", path); 93 | #endif 94 | 95 | int sign = -1; 96 | #ifdef MAIN 97 | #define openAt openat 98 | #endif 99 | int fd = (int) openAt(AT_FDCWD, path, O_RDONLY); 100 | #ifdef DEBUG_OPENAT 101 | LOGI("openat %s returns %d", path, fd); 102 | #endif 103 | if (fd < 0) { 104 | return sign; 105 | } 106 | 107 | bool verified = false; 108 | sign = 1; 109 | // https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD) 110 | for (int i = 0;; ++i) { 111 | unsigned short n; 112 | lseek(fd, -i - 2, SEEK_END); 113 | read(fd, &n, 2); 114 | if (n == i) { 115 | lseek(fd, -22, SEEK_CUR); 116 | read(fd, &size4, 4); 117 | if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) { 118 | #ifdef MAIN 119 | if (i > 0) { 120 | printf("warning: comment length is %d\n", i); 121 | } 122 | #endif 123 | break; 124 | } 125 | } 126 | if (i == 0xffff) { 127 | #ifdef MAIN 128 | printf("error: cannot find eocd\n"); 129 | #endif 130 | goto clean; 131 | } 132 | } 133 | 134 | lseek(fd, 12, SEEK_CUR); 135 | // offset 136 | read(fd, &size4, 0x4); 137 | lseek(fd, (off_t) (size4 - 0x18), SEEK_SET); 138 | 139 | read(fd, &size8, 0x8); 140 | read(fd, buffer, 0x10); 141 | if (!isApkSigBlock42((char *) buffer)) { 142 | goto clean; 143 | } 144 | 145 | lseek(fd, (off_t) (size4 - (size8 + 0x8)), SEEK_SET); 146 | read(fd, &size_of_block, 0x8); 147 | if (size_of_block != size8) { 148 | goto clean; 149 | } 150 | 151 | for (;;) { 152 | uint32_t id; 153 | uint32_t offset; 154 | read(fd, &size8, 0x8); // sequence length 155 | if (size8 == size_of_block) { 156 | break; 157 | } 158 | read(fd, &id, 0x4); // id 159 | offset = 4; 160 | #ifdef MAIN 161 | printf("id: 0x%08x\n", id); 162 | #endif 163 | if ((id ^ 0xdeadbeefu) == 0xafa439f5u) { 164 | sign = 2; 165 | } else if ((id ^ 0xdeadbeefu) == 0x2efed62f) { 166 | sign = 3; 167 | } else if ((id ^ 0xdeadbeefu) == 0xc53e138eu) { 168 | sign = 31; 169 | } else { 170 | sign = 1; 171 | } 172 | if (sign > 1) { 173 | uint32_t size, hash; 174 | verified = false; 175 | read(fd, &size4, 0x4); // signer-sequence length 176 | read(fd, &size4, 0x4); // signer length 177 | read(fd, &size4, 0x4); // signed data length 178 | offset += 0x4 * 3; 179 | 180 | read(fd, &size4, 0x4); // digests-sequence length 181 | lseek(fd, (off_t) (size4), SEEK_CUR);// skip digests 182 | offset += 0x4 + size4; 183 | 184 | read(fd, &size4, 0x4); // certificates length 185 | read(fd, &size, 0x4); // certificate length 186 | offset += 0x4 * 2; 187 | hash = calcHash(fd, size); 188 | offset += size; 189 | #ifdef MAIN 190 | showCert(size, hash, sign, " signer, "); 191 | #endif 192 | #if defined(GENUINE_SIZE) && defined(GENUINE_HASH) 193 | if (size == GENUINE_SIZE && (hash ^ 0x14131211u) == GENUINE_HASH) { 194 | verified = true; 195 | } 196 | #endif 197 | if (!verified && size4 - size == 4 && (sign == 3 || sign == 31)) { 198 | int64_t attributes_size, attribute_size; 199 | uint32_t minSdk, maxSdk; 200 | read(fd, &minSdk, 0x4); // minSdk 201 | read(fd, &maxSdk, 0x4); // maxSdk 202 | read(fd, &size4, 0x4); // length of additional attributes 203 | attributes_size = size4; 204 | offset += 0x4 * 3; 205 | #ifdef MAIN 206 | printf(" minSdk: %d, maxSdk: %d\n", minSdk, maxSdk); 207 | #endif 208 | while (attributes_size > 0) { 209 | read(fd, &size4, 0x4); // attribute length 210 | attribute_size = size4; 211 | attributes_size -= 0x4; 212 | attributes_size -= attribute_size; 213 | read(fd, &size4, 0x4); // attribute ID 214 | attribute_size -= 0x4; 215 | offset += 0x4 * 2; 216 | #ifdef MAIN 217 | printf(" attribute id: 0x%08x, size: %lld\n", size4, attribute_size); 218 | #endif 219 | if ((size4 ^ 0xdeadbeefu) == 0xe50dd163u) { 220 | read(fd, &size4, 0x4); // version 221 | #ifdef MAIN 222 | printf(" version: %d\n", size4); 223 | #endif 224 | offset += 0x4; 225 | attribute_size -= 0x4; 226 | while (attribute_size > 0) { 227 | unsigned node; 228 | read(fd, &node, 0x4); // length of node 229 | offset += 0x4 + node; 230 | attribute_size -= 0x4 + node; 231 | read(fd, &size4, 0x4); // length of signed data 232 | read(fd, &size, 0x4); // length of certificate 233 | node -= 0x4 * 2 + size; 234 | hash = calcHash(fd, size); 235 | #ifdef MAIN 236 | showCert(size, hash, sign, " rotate, "); 237 | #endif 238 | #if defined(GENUINE_SIZE) && defined(GENUINE_HASH) 239 | if (size == GENUINE_SIZE && (hash ^ 0x14131211u) == GENUINE_HASH) { 240 | verified = true; 241 | } 242 | #endif 243 | lseek(fd, (off_t) node, SEEK_CUR); 244 | } 245 | #ifdef MAIN 246 | } else if (size4 == 0x559f8b02) { 247 | read(fd, &size4, 0x4); 248 | offset += 0x4; 249 | attribute_size -= 0x4; 250 | printf(" rotation minSdk: %d\n", size4); 251 | } else if (size4 == 0xc2a6b3ba) { 252 | printf(" rotation minSdk: development\n"); 253 | #endif 254 | } 255 | if (attribute_size > 0) { 256 | lseek(fd, (off_t) attribute_size, SEEK_CUR); 257 | offset += attribute_size; 258 | } 259 | } 260 | } 261 | #if defined(GENUINE_SIZE) && defined(GENUINE_HASH) 262 | if (!verified) { 263 | break; 264 | } 265 | #endif 266 | } 267 | lseek(fd, (off_t) (size8 - offset), SEEK_CUR); 268 | } 269 | 270 | clean: 271 | close(fd); 272 | 273 | if (verified) { 274 | return 0; 275 | } 276 | return sign; 277 | } 278 | 279 | #ifdef MAIN 280 | int main(int argc, char **argv) { 281 | if (argc > 1) { 282 | checkSignature(argv[1]); 283 | } 284 | } 285 | #endif 286 | -------------------------------------------------------------------------------- /src/main/jni/apk-sign-v2.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/8. 3 | // 4 | 5 | #ifndef BREVENT_APK_SIGN_V2_H 6 | #define BREVENT_APK_SIGN_V2_H 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | int checkSignature(const char *path); 13 | 14 | #ifdef __cplusplus 15 | } 16 | #endif 17 | 18 | #endif //BREVENT_APK_SIGN_V2_H 19 | -------------------------------------------------------------------------------- /src/main/jni/art.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019-09-15. 3 | // 4 | 5 | #ifndef BREVENT_ART_H 6 | #define BREVENT_ART_H 7 | 8 | #include 9 | #include "common.h" 10 | 11 | #ifndef NDEBUG 12 | #define ALWAYS_INLINE 13 | #else 14 | #define ALWAYS_INLINE __attribute__ ((always_inline)) 15 | #endif 16 | 17 | #define DCHECK(...) 18 | 19 | #define OVERRIDE override 20 | 21 | #define ATTRIBUTE_UNUSED __attribute__((__unused__)) 22 | 23 | #define REQUIRES_SHARED(...) 24 | 25 | #define MANAGED PACKED(4) 26 | #define PACKED(x) __attribute__ ((__aligned__(x), __packed__)) 27 | 28 | namespace art { 29 | 30 | namespace mirror { 31 | 32 | class Object { 33 | 34 | }; 35 | 36 | template 37 | class ObjPtr { 38 | 39 | }; 40 | 41 | template 42 | class PtrCompression { 43 | public: 44 | // Compress reference to its bit representation. 45 | static uint32_t Compress(MirrorType* mirror_ptr) { 46 | uintptr_t as_bits = reinterpret_cast(mirror_ptr); 47 | return static_cast(kPoisonReferences ? -as_bits : as_bits); 48 | } 49 | 50 | // Uncompress an encoded reference from its bit representation. 51 | static MirrorType* Decompress(uint32_t ref) { 52 | uintptr_t as_bits = kPoisonReferences ? -ref : ref; 53 | return reinterpret_cast(as_bits); 54 | } 55 | 56 | // Convert an ObjPtr to a compressed reference. 57 | static uint32_t Compress(ObjPtr ptr) REQUIRES_SHARED(Locks::mutator_lock_); 58 | }; 59 | 60 | // Value type representing a reference to a mirror::Object of type MirrorType. 61 | template 62 | class MANAGED ObjectReference { 63 | private: 64 | using Compression = PtrCompression; 65 | 66 | public: 67 | /* 68 | * Returns a pointer to the mirror of the managed object this reference is for. 69 | * 70 | * This does NOT return the current object (which isn't derived from, and 71 | * therefor cannot be a mirror::Object) as a mirror pointer. Instead, this 72 | * returns a pointer to the mirror of the managed object this refers to. 73 | * 74 | * TODO (chriswailes): Rename to GetPtr(). 75 | */ 76 | MirrorType* AsMirrorPtr() const { 77 | return Compression::Decompress(reference_); 78 | } 79 | 80 | void Assign(MirrorType* other) { 81 | reference_ = Compression::Compress(other); 82 | } 83 | 84 | void Assign(ObjPtr ptr) REQUIRES_SHARED(Locks::mutator_lock_); 85 | 86 | void Clear() { 87 | reference_ = 0; 88 | DCHECK(IsNull()); 89 | } 90 | 91 | bool IsNull() const { 92 | return reference_ == 0; 93 | } 94 | 95 | uint32_t AsVRegValue() const { 96 | return reference_; 97 | } 98 | 99 | static ObjectReference FromMirrorPtr(MirrorType* mirror_ptr) 100 | REQUIRES_SHARED(Locks::mutator_lock_) { 101 | return ObjectReference(mirror_ptr); 102 | } 103 | 104 | protected: 105 | explicit ObjectReference(MirrorType* mirror_ptr) REQUIRES_SHARED(Locks::mutator_lock_) 106 | : reference_(Compression::Compress(mirror_ptr)) { 107 | } 108 | 109 | // The encoded reference to a mirror::Object. 110 | uint32_t reference_; 111 | }; 112 | 113 | // Standard compressed reference used in the runtime. Used for StackReference and GC roots. 114 | template 115 | class MANAGED CompressedReference : public mirror::ObjectReference { 116 | public: 117 | CompressedReference() REQUIRES_SHARED(Locks::mutator_lock_) 118 | : mirror::ObjectReference(nullptr) {} 119 | 120 | static CompressedReference FromMirrorPtr(MirrorType* p) 121 | REQUIRES_SHARED(Locks::mutator_lock_) { 122 | return CompressedReference(p); 123 | } 124 | 125 | private: 126 | explicit CompressedReference(MirrorType* p) REQUIRES_SHARED(Locks::mutator_lock_) 127 | : mirror::ObjectReference(p) {} 128 | }; 129 | } 130 | 131 | class RootInfo { 132 | 133 | }; 134 | 135 | class RootVisitor { 136 | public: 137 | virtual ~RootVisitor() {} 138 | 139 | // Single root version, not overridable. 140 | ALWAYS_INLINE void VisitRoot(mirror::Object **root, const RootInfo &info) 141 | REQUIRES_SHARED(Locks::mutator_lock_) { 142 | VisitRoots(&root, 1, info); 143 | } 144 | 145 | // Single root version, not overridable. 146 | ALWAYS_INLINE void VisitRootIfNonNull(mirror::Object **root, const RootInfo &info) 147 | REQUIRES_SHARED(Locks::mutator_lock_) { 148 | if (*root != nullptr) { 149 | VisitRoot(root, info); 150 | } 151 | } 152 | 153 | virtual void VisitRoots(mirror::Object ***roots, size_t count, const RootInfo &info) 154 | REQUIRES_SHARED(Locks::mutator_lock_) = 0; 155 | 156 | virtual void VisitRoots(mirror::CompressedReference **roots, size_t count, 157 | const RootInfo &info) 158 | REQUIRES_SHARED(Locks::mutator_lock_) = 0; 159 | }; 160 | 161 | // Only visits roots one at a time, doesn't handle updating roots. Used when performance isn't 162 | // critical. 163 | class SingleRootVisitor : public RootVisitor { 164 | private: 165 | void VisitRoots(mirror::Object ***roots, size_t count, const RootInfo &info) OVERRIDE 166 | REQUIRES_SHARED(Locks::mutator_lock_) { 167 | for (size_t i = 0; i < count; ++i) { 168 | VisitRoot(*roots[i], info); 169 | } 170 | } 171 | 172 | void VisitRoots(mirror::CompressedReference **roots, size_t count, 173 | const RootInfo &info) OVERRIDE 174 | REQUIRES_SHARED(Locks::mutator_lock_) { 175 | for (size_t i = 0; i < count; ++i) { 176 | VisitRoot(roots[i]->AsMirrorPtr(), info); 177 | } 178 | } 179 | 180 | virtual void VisitRoot(mirror::Object *root, const RootInfo &info) = 0; 181 | }; 182 | 183 | class IsMarkedVisitor { 184 | public: 185 | virtual ~IsMarkedVisitor() {} 186 | 187 | // Return null if an object is not marked, otherwise returns the new address of that object. 188 | // May return the same address as the input if the object did not move. 189 | virtual mirror::Object *IsMarked(mirror::Object *obj) = 0; 190 | }; 191 | 192 | } 193 | 194 | #endif //BREVENT_ART_H 195 | -------------------------------------------------------------------------------- /src/main/jni/bitmap.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/22. 3 | // 4 | 5 | #ifndef BREVENT_BITMAP_H 6 | #define BREVENT_BITMAP_H 7 | 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | jobject asBitmap(JNIEnv *env, int width, jstring label); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif //BREVENT_BITMAP_H 21 | -------------------------------------------------------------------------------- /src/main/jni/classloader.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/2/16. 3 | // 4 | 5 | #ifndef BREVENT_CLASSLOADER_H 6 | #define BREVENT_CLASSLOADER_H 7 | 8 | #include 9 | #include "common.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | extern jmethodID methodObjectToString; 16 | extern jmethodID methodClassGetClassLoader; 17 | extern jmethodID methodClassGetName; 18 | extern jmethodID methodClassLoaderLoadClass; 19 | extern jmethodID methodClassIsArray; 20 | 21 | bool checkClassLoader(JNIEnv *env, int sdk, int *genuine); 22 | 23 | void logObject(JNIEnv *env, const char *format, jobject object); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/main/jni/common.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/20. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "common.h" 10 | #include "handle-error.h" 11 | #include "plt.h" 12 | 13 | static volatile int mGenuine; 14 | 15 | static bool onCheckTrue(JNIEnv *env __unused) { 16 | #ifdef DEBUG_NATIVE 17 | has_native_libs(); 18 | #endif 19 | #ifdef DEBUG_GENUINE_MOCK 20 | start_native_activity_async(env); 21 | #endif 22 | return true; 23 | } 24 | 25 | static bool onCheckFalse(JNIEnv *env __unused) { 26 | #if defined(GENUINE_FALSE_CRASH) 27 | return false; 28 | #elif defined(GENUINE_FALSE_NATIVE) 29 | start_native_activity_async(env); 30 | #endif 31 | return true; 32 | } 33 | 34 | static bool onCheckFake(JNIEnv *env __unused) { 35 | #if defined(GENUINE_FAKE_CRASH) 36 | return false; 37 | #elif defined(GENUINE_FAKE_NATIVE) 38 | start_native_activity_async(env); 39 | #endif 40 | return true; 41 | } 42 | 43 | static bool onCheckOverlay(JNIEnv *env __unused) { 44 | #if defined(GENUINE_OVERLAY_CRASH) 45 | return false; 46 | #elif defined(GENUINE_OVERLAY_NATIVE) 47 | start_native_activity_async(env); 48 | #endif 49 | return true; 50 | } 51 | 52 | static bool onCheckOdex(JNIEnv *env __unused) { 53 | #if defined(GENUINE_ODEX_CRASH) 54 | return false; 55 | #elif defined(GENUINE_ODEX_NATIVE) 56 | start_native_activity_async(env); 57 | #endif 58 | return true; 59 | } 60 | 61 | static bool onCheckDex(JNIEnv *env __unused) { 62 | #if defined(GENUINE_DEX_CRASH) 63 | return false; 64 | #elif defined(GENUINE_DEX_NATIVE) 65 | start_native_activity_async(env); 66 | #endif 67 | return true; 68 | } 69 | 70 | static bool onCheckProxy(JNIEnv *env __unused) { 71 | #if defined(GENUINE_PROXY_CRASH) 72 | return false; 73 | #elif defined(GENUINE_PROXY_NATIVE) 74 | start_native_activity_async(env); 75 | #endif 76 | return true; 77 | } 78 | 79 | static bool onCheckError(JNIEnv *env __unused) { 80 | #if defined(GENUINE_ERROR_CRASH) 81 | return false; 82 | #elif defined(GENUINE_ERROR_NATIVE) 83 | start_native_activity_async(env); 84 | #endif 85 | return true; 86 | } 87 | 88 | static bool onCheckFatal(JNIEnv *env __unused) { 89 | #if defined(GENUINE_FALTAL_CRASH) 90 | return false; 91 | #elif defined(GENUINE_FATAL_NATIVE) 92 | start_native_activity_async(env); 93 | #endif 94 | return true; 95 | } 96 | 97 | static bool onCheckNoapk(JNIEnv *env __unused) { 98 | #if defined(GENUINE_NOAPK_CRASH) 99 | return false; 100 | #elif defined(GENUINE_NOAPK_NATIVE) 101 | start_native_activity_async(env); 102 | #endif 103 | return true; 104 | } 105 | 106 | bool setGenuine(JNIEnv *env, int genuine) { 107 | mGenuine = genuine; 108 | switch (genuine) { 109 | case CHECK_TRUE: 110 | return onCheckTrue(env); 111 | case CHECK_FALSE: 112 | return onCheckFalse(env); 113 | case CHECK_FAKE: 114 | return onCheckFake(env); 115 | case CHECK_OVERLAY: 116 | return onCheckOverlay(env); 117 | case CHECK_ODEX: 118 | return onCheckOdex(env); 119 | case CHECK_DEX: 120 | return onCheckDex(env); 121 | case CHECK_PROXY: 122 | return onCheckProxy(env); 123 | case CHECK_ERROR: 124 | return onCheckError(env); 125 | case CHECK_FATAL: 126 | return onCheckFatal(env); 127 | case CHECK_NOAPK: 128 | return onCheckNoapk(env); 129 | default: 130 | return true; 131 | } 132 | } 133 | 134 | int getGenuine() { 135 | return mGenuine; 136 | } 137 | 138 | char *getGenuineClassName() { 139 | #ifdef GET_GENUINE_CLASS_NAME 140 | return GET_GENUINE_CLASS_NAME(); 141 | #else 142 | #ifndef GENUINE_CLAZZ 143 | #define GENUINE_CLAZZ "me/piebridge/Genuine" 144 | #endif 145 | return strdup(GENUINE_CLAZZ); 146 | #endif 147 | } 148 | 149 | char *getGenuinePackageName() { 150 | #ifdef GET_GENUINE_PACKAGE_NAME 151 | return GET_GENUINE_PACKAGE_NAME(); 152 | #elif defined(GENUINE_NAME) 153 | static unsigned int m = 0; 154 | if (m == 0) { 155 | m = 20; 156 | } else if (m == 23) { 157 | m = 29; 158 | } 159 | char name[] = GENUINE_NAME; 160 | unsigned int length = sizeof(name) - 1; 161 | for (unsigned int i = 0; i < length; ++i) { 162 | name[i] ^= ((i + length) % m); 163 | } 164 | name[length] = '\0'; 165 | return strdup(name); 166 | #else 167 | return NULL; 168 | #endif 169 | } 170 | 171 | __attribute__((__format__ (__printf__, 2, 0))) 172 | void genuine_log_print(int prio, const char *fmt, ...) { 173 | va_list ap; 174 | va_start(ap, fmt); 175 | __android_log_vprint(prio, TAG, fmt, ap); 176 | va_end(ap); 177 | } 178 | 179 | static inline void fill_ro_build_version_sdk(char v[]) { 180 | // ro.build.version.sdk 181 | static unsigned int m = 0; 182 | 183 | if (m == 0) { 184 | m = 19; 185 | } else if (m == 23) { 186 | m = 29; 187 | } 188 | 189 | v[0x0] = 's'; 190 | v[0x1] = 'm'; 191 | v[0x2] = '-'; 192 | v[0x3] = 'f'; 193 | v[0x4] = 'p'; 194 | v[0x5] = 'o'; 195 | v[0x6] = 'k'; 196 | v[0x7] = 'l'; 197 | v[0x8] = '\''; 198 | v[0x9] = '|'; 199 | v[0xa] = 'n'; 200 | v[0xb] = '~'; 201 | v[0xc] = '~'; 202 | v[0xd] = 'g'; 203 | v[0xe] = '`'; 204 | v[0xf] = '~'; 205 | v[0x10] = '?'; 206 | v[0x11] = 'a'; 207 | v[0x12] = 'd'; 208 | v[0x13] = 'j'; 209 | for (unsigned int i = 0; i < 0x14; ++i) { 210 | v[i] ^= ((i + 0x14) % m); 211 | } 212 | v[0x14] = '\0'; 213 | } 214 | 215 | int getSdk() { 216 | static int sdk = 0; 217 | if (sdk == 0) { 218 | char v1[0x20]; 219 | char prop[PROP_VALUE_MAX] = {0}; 220 | fill_ro_build_version_sdk(v1); 221 | __system_property_get(v1, prop); 222 | sdk = (int) strtol(prop, NULL, 10); 223 | } 224 | return sdk; 225 | } 226 | 227 | #ifdef DEBUG 228 | 229 | void debug(JNIEnv *env, const char *format, jobject object) { 230 | if (object == NULL) { 231 | LOGI(format, NULL); 232 | } else { 233 | jclass objectClass = (*env)->FindClass(env, "java/lang/Object"); 234 | jmethodID toString = (*env)->GetMethodID(env, objectClass, "toString", 235 | "()Ljava/lang/String;"); 236 | jstring string = (jstring) (*env)->CallObjectMethod(env, object, toString); 237 | const char *value = (*env)->GetStringUTFChars(env, string, NULL); 238 | LOGI(format, value); 239 | (*env)->ReleaseStringUTFChars(env, string, value); 240 | (*env)->DeleteLocalRef(env, string); 241 | (*env)->DeleteLocalRef(env, objectClass); 242 | } 243 | } 244 | 245 | #endif 246 | -------------------------------------------------------------------------------- /src/main/jni/common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/20. 3 | // 4 | 5 | #ifndef BREVENT_COMMON_H 6 | #define BREVENT_COMMON_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #if __has_include("genuine.h") 13 | #include "genuine.h" 14 | #endif 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | enum { 21 | CHECK_TRUE, 22 | CHECK_FALSE, 23 | CHECK_FAKE, 24 | CHECK_OVERLAY, 25 | CHECK_ODEX, 26 | CHECK_DEX, 27 | CHECK_PROXY, 28 | CHECK_ERROR, 29 | CHECK_FATAL, 30 | CHECK_NOAPK, 31 | }; 32 | 33 | #ifndef TAG 34 | #define TAG "Genuine" 35 | #endif 36 | 37 | #ifndef LOGI 38 | #define LOGI(...) (genuine_log_print(ANDROID_LOG_INFO, __VA_ARGS__)) 39 | #endif 40 | 41 | #ifndef LOGW 42 | #define LOGW(...) (genuine_log_print(ANDROID_LOG_WARN, __VA_ARGS__)) 43 | #endif 44 | 45 | #ifndef LOGE 46 | #define LOGE(...) (genuine_log_print(ANDROID_LOG_ERROR, __VA_ARGS__)) 47 | #endif 48 | 49 | extern jmethodID methodNop; 50 | extern size_t artMethodSize; 51 | 52 | void genuine_log_print(int prio, const char *fmt, ...); 53 | 54 | char *getGenuinePackageName(); 55 | 56 | char *getGenuineClassName(); 57 | 58 | bool setGenuine(JNIEnv *env, int genuine); 59 | 60 | int getGenuine(); 61 | 62 | int getSdk(); 63 | 64 | bool has_native_libs(); 65 | 66 | #ifdef DEBUG 67 | 68 | void debug(JNIEnv *env, const char *format, jobject object); 69 | 70 | #else 71 | #define debug(...) do {} while(0); 72 | #endif 73 | 74 | #ifdef __cplusplus 75 | } 76 | #endif 77 | 78 | #endif //BREVENT_COMMON_H 79 | -------------------------------------------------------------------------------- /src/main/jni/dex-path-list.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2022/5/3. 3 | // 4 | 5 | #include 6 | #include 7 | #include "dex-path-list.h" 8 | #include "classloader.h" 9 | #include "common.h" 10 | 11 | static inline void fill_java_util_Arrays(char v[]) { 12 | // java/util/Arrays 13 | static unsigned int m = 0; 14 | 15 | if (m == 0) { 16 | m = 13; 17 | } else if (m == 17) { 18 | m = 19; 19 | } 20 | 21 | v[0x0] = 'i'; 22 | v[0x1] = 'e'; 23 | v[0x2] = 's'; 24 | v[0x3] = 'g'; 25 | v[0x4] = '('; 26 | v[0x5] = '}'; 27 | v[0x6] = '}'; 28 | v[0x7] = 'c'; 29 | v[0x8] = 'g'; 30 | v[0x9] = '#'; 31 | v[0xa] = 'A'; 32 | v[0xb] = 's'; 33 | v[0xc] = 'p'; 34 | v[0xd] = 'b'; 35 | v[0xe] = '}'; 36 | v[0xf] = 'v'; 37 | for (unsigned int i = 0; i < 0x10; ++i) { 38 | v[i] ^= ((i + 0x10) % m); 39 | } 40 | v[0x10] = '\0'; 41 | } 42 | 43 | static inline void fill_dexElements(char v[]) { 44 | // dexElements 45 | static unsigned int m = 0; 46 | 47 | if (m == 0) { 48 | m = 7; 49 | } else if (m == 11) { 50 | m = 13; 51 | } 52 | 53 | v[0x0] = '`'; 54 | v[0x1] = '`'; 55 | v[0x2] = '~'; 56 | v[0x3] = 'E'; 57 | v[0x4] = 'm'; 58 | v[0x5] = 'g'; 59 | v[0x6] = 'n'; 60 | v[0x7] = 'a'; 61 | v[0x8] = 'k'; 62 | v[0x9] = 'r'; 63 | v[0xa] = 's'; 64 | for (unsigned int i = 0; i < 0xb; ++i) { 65 | v[i] ^= ((i + 0xb) % m); 66 | } 67 | v[0xb] = '\0'; 68 | } 69 | 70 | static inline void fill_dexElements_signature(char v[]) { 71 | // [Ldalvik/system/DexPathList$Element; 72 | static unsigned int m = 0; 73 | 74 | if (m == 0) { 75 | m = 31; 76 | } else if (m == 37) { 77 | m = 41; 78 | } 79 | 80 | v[0x0] = '^'; 81 | v[0x1] = 'J'; 82 | v[0x2] = 'c'; 83 | v[0x3] = 'i'; 84 | v[0x4] = 'e'; 85 | v[0x5] = '|'; 86 | v[0x6] = 'b'; 87 | v[0x7] = 'g'; 88 | v[0x8] = '"'; 89 | v[0x9] = '}'; 90 | v[0xa] = 'v'; 91 | v[0xb] = 'c'; 92 | v[0xc] = 'e'; 93 | v[0xd] = 'w'; 94 | v[0xe] = '~'; 95 | v[0xf] = ';'; 96 | v[0x10] = 'Q'; 97 | v[0x11] = 's'; 98 | v[0x12] = 'o'; 99 | v[0x13] = 'H'; 100 | v[0x14] = 'x'; 101 | v[0x15] = 'n'; 102 | v[0x16] = 's'; 103 | v[0x17] = 'P'; 104 | v[0x18] = 't'; 105 | v[0x19] = 'm'; 106 | v[0x1a] = 't'; 107 | v[0x1b] = '%'; 108 | v[0x1c] = 'G'; 109 | v[0x1d] = 'o'; 110 | v[0x1e] = 'a'; 111 | v[0x1f] = 'h'; 112 | v[0x20] = 'c'; 113 | v[0x21] = 'i'; 114 | v[0x22] = '|'; 115 | v[0x23] = '2'; 116 | for (unsigned int i = 0; i < 0x24; ++i) { 117 | v[i] ^= ((i + 0x24) % m); 118 | } 119 | v[0x24] = '\0'; 120 | } 121 | 122 | static inline void fill_InMemoryDexFile(char v[]) { 123 | // InMemoryDexFile 124 | static unsigned int m = 0; 125 | 126 | if (m == 0) { 127 | m = 13; 128 | } else if (m == 17) { 129 | m = 19; 130 | } 131 | 132 | v[0x0] = 'K'; 133 | v[0x1] = 'm'; 134 | v[0x2] = 'I'; 135 | v[0x3] = '`'; 136 | v[0x4] = 'k'; 137 | v[0x5] = 'h'; 138 | v[0x6] = 'z'; 139 | v[0x7] = 'p'; 140 | v[0x8] = 'N'; 141 | v[0x9] = 'n'; 142 | v[0xa] = 't'; 143 | v[0xb] = 'F'; 144 | v[0xc] = 'h'; 145 | v[0xd] = 'n'; 146 | v[0xe] = 'f'; 147 | for (unsigned int i = 0; i < 0xf; ++i) { 148 | v[i] ^= ((i + 0xf) % m); 149 | } 150 | v[0xf] = '\0'; 151 | } 152 | 153 | static inline void fill__apk(char v[]) { 154 | // .apk 155 | static unsigned int m = 0; 156 | 157 | if (m == 0) { 158 | m = 3; 159 | } else if (m == 5) { 160 | m = 7; 161 | } 162 | 163 | v[0x0] = '/'; 164 | v[0x1] = 'c'; 165 | v[0x2] = 'p'; 166 | v[0x3] = 'j'; 167 | for (unsigned int i = 0; i < 0x4; ++i) { 168 | v[i] ^= ((i + 0x4) % m); 169 | } 170 | v[0x4] = '\0'; 171 | } 172 | 173 | static inline void fill__data_app_(char v[]) { 174 | // /data/app/ 175 | static unsigned int m = 0; 176 | 177 | if (m == 0) { 178 | m = 7; 179 | } else if (m == 11) { 180 | m = 13; 181 | } 182 | 183 | v[0x0] = ','; 184 | v[0x1] = '`'; 185 | v[0x2] = 'd'; 186 | v[0x3] = 'r'; 187 | v[0x4] = 'a'; 188 | v[0x5] = '.'; 189 | v[0x6] = 'c'; 190 | v[0x7] = 's'; 191 | v[0x8] = 't'; 192 | v[0x9] = '*'; 193 | for (unsigned int i = 0; i < 0xa; ++i) { 194 | v[i] ^= ((i + 0xa) % m); 195 | } 196 | v[0xa] = '\0'; 197 | } 198 | 199 | static bool checkAbnormal(const char *check, bool dex, bool *hasInMemoryDex) { 200 | char v[0x10]; 201 | fill_InMemoryDexFile(v); 202 | if (dex && strstr(check, v)) { 203 | if (hasInMemoryDex) { 204 | *hasInMemoryDex = true; 205 | } 206 | } 207 | fill__data_app_(v); 208 | if (!strstr(check, v)) { 209 | return false; 210 | } 211 | fill__apk(v); 212 | bool abnormal = false; 213 | if (strstr(check, v)) { 214 | char *packageName = getGenuinePackageName(); 215 | if (!strstr(check, packageName)) { 216 | LOGW(check); 217 | abnormal = true; 218 | } 219 | #ifdef GENUINE_NAME 220 | free(packageName); 221 | #endif 222 | } 223 | return abnormal; 224 | } 225 | 226 | static inline void fill_nativeLibraryDirectories(char v[]) { 227 | // nativeLibraryDirectories 228 | static unsigned int m = 0; 229 | 230 | if (m == 0) { 231 | m = 23; 232 | } else if (m == 29) { 233 | m = 31; 234 | } 235 | 236 | v[0x0] = 'o'; 237 | v[0x1] = 'c'; 238 | v[0x2] = 'w'; 239 | v[0x3] = 'm'; 240 | v[0x4] = 's'; 241 | v[0x5] = 'c'; 242 | v[0x6] = 'K'; 243 | v[0x7] = 'a'; 244 | v[0x8] = 'k'; 245 | v[0x9] = 'x'; 246 | v[0xa] = 'j'; 247 | v[0xb] = '~'; 248 | v[0xc] = 't'; 249 | v[0xd] = 'J'; 250 | v[0xe] = 'f'; 251 | v[0xf] = 'b'; 252 | v[0x10] = 't'; 253 | v[0x11] = 'q'; 254 | v[0x12] = 'g'; 255 | v[0x13] = '{'; 256 | v[0x14] = 'g'; 257 | v[0x15] = '\x7f'; 258 | v[0x16] = 'e'; 259 | v[0x17] = 'r'; 260 | for (unsigned int i = 0; i < 0x18; ++i) { 261 | v[i] ^= ((i + 0x18) % m); 262 | } 263 | v[0x18] = '\0'; 264 | } 265 | 266 | static inline void fill_nativeLibraryDirectories_signature(char v[]) { 267 | // Ljava/util/List; 268 | static unsigned int m = 0; 269 | 270 | if (m == 0) { 271 | m = 13; 272 | } else if (m == 17) { 273 | m = 19; 274 | } 275 | 276 | v[0x0] = 'O'; 277 | v[0x1] = 'n'; 278 | v[0x2] = 'd'; 279 | v[0x3] = 'p'; 280 | v[0x4] = 'f'; 281 | v[0x5] = '\''; 282 | v[0x6] = '|'; 283 | v[0x7] = '~'; 284 | v[0x8] = 'b'; 285 | v[0x9] = '`'; 286 | v[0xa] = '/'; 287 | v[0xb] = 'M'; 288 | v[0xc] = 'k'; 289 | v[0xd] = 'p'; 290 | v[0xe] = 'p'; 291 | v[0xf] = '>'; 292 | for (unsigned int i = 0; i < 0x10; ++i) { 293 | v[i] ^= ((i + 0x10) % m); 294 | } 295 | v[0x10] = '\0'; 296 | } 297 | 298 | static inline void fill_java_util_List(char v[]) { 299 | // java/util/List 300 | static unsigned int m = 0; 301 | 302 | if (m == 0) { 303 | m = 13; 304 | } else if (m == 17) { 305 | m = 19; 306 | } 307 | 308 | v[0x0] = 'k'; 309 | v[0x1] = 'c'; 310 | v[0x2] = 'u'; 311 | v[0x3] = 'e'; 312 | v[0x4] = '*'; 313 | v[0x5] = 's'; 314 | v[0x6] = 's'; 315 | v[0x7] = 'a'; 316 | v[0x8] = 'e'; 317 | v[0x9] = '%'; 318 | v[0xa] = 'G'; 319 | v[0xb] = 'e'; 320 | v[0xc] = 's'; 321 | v[0xd] = 'u'; 322 | for (unsigned int i = 0; i < 0xe; ++i) { 323 | v[i] ^= ((i + 0xe) % m); 324 | } 325 | v[0xe] = '\0'; 326 | } 327 | 328 | static inline void fill_toString(char v[]) { 329 | // toString 330 | static unsigned int m = 0; 331 | 332 | if (m == 0) { 333 | m = 7; 334 | } else if (m == 11) { 335 | m = 13; 336 | } 337 | 338 | v[0x0] = 'u'; 339 | v[0x1] = 'm'; 340 | v[0x2] = 'P'; 341 | v[0x3] = 'p'; 342 | v[0x4] = 'w'; 343 | v[0x5] = 'o'; 344 | v[0x6] = 'n'; 345 | v[0x7] = 'f'; 346 | for (unsigned int i = 0; i < 0x8; ++i) { 347 | v[i] ^= ((i + 0x8) % m); 348 | } 349 | v[0x8] = '\0'; 350 | } 351 | 352 | static inline void fill_Arrays_toString_signature(char v[]) { 353 | // ([Ljava/lang/Object;)Ljava/lang/String; 354 | static unsigned int m = 0; 355 | 356 | if (m == 0) { 357 | m = 37; 358 | } else if (m == 41) { 359 | m = 43; 360 | } 361 | 362 | v[0x0] = '*'; 363 | v[0x1] = 'X'; 364 | v[0x2] = 'H'; 365 | v[0x3] = 'o'; 366 | v[0x4] = 'g'; 367 | v[0x5] = 'q'; 368 | v[0x6] = 'i'; 369 | v[0x7] = '&'; 370 | v[0x8] = 'f'; 371 | v[0x9] = 'j'; 372 | v[0xa] = 'b'; 373 | v[0xb] = 'j'; 374 | v[0xc] = '!'; 375 | v[0xd] = '@'; 376 | v[0xe] = 'r'; 377 | v[0xf] = '{'; 378 | v[0x10] = 'w'; 379 | v[0x11] = 'p'; 380 | v[0x12] = '`'; 381 | v[0x13] = '.'; 382 | v[0x14] = '?'; 383 | v[0x15] = '['; 384 | v[0x16] = 'r'; 385 | v[0x17] = 'x'; 386 | v[0x18] = 'l'; 387 | v[0x19] = 'z'; 388 | v[0x1a] = '3'; 389 | v[0x1b] = 'q'; 390 | v[0x1c] = '\x7f'; 391 | v[0x1d] = 'q'; 392 | v[0x1e] = 'G'; 393 | v[0x1f] = '\x0e'; 394 | v[0x20] = 'q'; 395 | v[0x21] = 'W'; 396 | v[0x22] = 'V'; 397 | v[0x23] = 'i'; 398 | v[0x24] = 'o'; 399 | v[0x25] = 'e'; 400 | v[0x26] = '8'; 401 | for (unsigned int i = 0; i < 0x27; ++i) { 402 | v[i] ^= ((i + 0x27) % m); 403 | } 404 | v[0x27] = '\0'; 405 | } 406 | 407 | static inline void fill_toArray(char v[]) { 408 | // toArray 409 | static unsigned int m = 0; 410 | 411 | if (m == 0) { 412 | m = 5; 413 | } else if (m == 7) { 414 | m = 11; 415 | } 416 | 417 | v[0x0] = 'v'; 418 | v[0x1] = 'l'; 419 | v[0x2] = 'E'; 420 | v[0x3] = 'r'; 421 | v[0x4] = 's'; 422 | v[0x5] = 'c'; 423 | v[0x6] = 'z'; 424 | for (unsigned int i = 0; i < 0x7; ++i) { 425 | v[i] ^= ((i + 0x7) % m); 426 | } 427 | v[0x7] = '\0'; 428 | } 429 | 430 | static inline void fill_Arrays_toArray_signature(char v[]) { 431 | // ()[Ljava/lang/Object; 432 | static unsigned int m = 0; 433 | 434 | if (m == 0) { 435 | m = 19; 436 | } else if (m == 23) { 437 | m = 29; 438 | } 439 | 440 | v[0x0] = '*'; 441 | v[0x1] = '*'; 442 | v[0x2] = '_'; 443 | v[0x3] = 'I'; 444 | v[0x4] = 'l'; 445 | v[0x5] = 'f'; 446 | v[0x6] = '~'; 447 | v[0x7] = 'h'; 448 | v[0x8] = '%'; 449 | v[0x9] = 'g'; 450 | v[0xa] = 'm'; 451 | v[0xb] = 'c'; 452 | v[0xc] = 'i'; 453 | v[0xd] = ' '; 454 | v[0xe] = '_'; 455 | v[0xf] = 's'; 456 | v[0x10] = 'x'; 457 | v[0x11] = 'e'; 458 | v[0x12] = 'b'; 459 | v[0x13] = 'v'; 460 | v[0x14] = '8'; 461 | for (unsigned int i = 0; i < 0x15; ++i) { 462 | v[i] ^= ((i + 0x15) % m); 463 | } 464 | v[0x15] = '\0'; 465 | } 466 | 467 | static inline void fill_isEmpty(char v[]) { 468 | // isEmpty 469 | static unsigned int m = 0; 470 | 471 | if (m == 0) { 472 | m = 5; 473 | } else if (m == 7) { 474 | m = 11; 475 | } 476 | 477 | v[0x0] = 'k'; 478 | v[0x1] = 'p'; 479 | v[0x2] = 'A'; 480 | v[0x3] = 'm'; 481 | v[0x4] = 'q'; 482 | v[0x5] = 'v'; 483 | v[0x6] = 'z'; 484 | for (unsigned int i = 0; i < 0x7; ++i) { 485 | v[i] ^= ((i + 0x7) % m); 486 | } 487 | v[0x7] = '\0'; 488 | } 489 | 490 | static inline void fill_Arrays_isEmpty_signature(char v[]) { 491 | // ()Z 492 | static unsigned int m = 0; 493 | 494 | if (m == 0) { 495 | m = 2; 496 | } else if (m == 3) { 497 | m = 5; 498 | } 499 | 500 | v[0x0] = ')'; 501 | v[0x1] = ')'; 502 | v[0x2] = '['; 503 | for (unsigned int i = 0; i < 0x3; ++i) { 504 | v[i] ^= ((i + 0x3) % m); 505 | } 506 | v[0x3] = '\0'; 507 | } 508 | 509 | static inline void fill_pathList(char v[]) { 510 | // pathList 511 | static unsigned int m = 0; 512 | 513 | if (m == 0) { 514 | m = 7; 515 | } else if (m == 11) { 516 | m = 13; 517 | } 518 | 519 | v[0x0] = 'q'; 520 | v[0x1] = 'c'; 521 | v[0x2] = 'w'; 522 | v[0x3] = 'l'; 523 | v[0x4] = 'I'; 524 | v[0x5] = 'o'; 525 | v[0x6] = 's'; 526 | v[0x7] = 'u'; 527 | for (unsigned int i = 0; i < 0x8; ++i) { 528 | v[i] ^= ((i + 0x8) % m); 529 | } 530 | v[0x8] = '\0'; 531 | } 532 | 533 | static inline void fill_pathList_signature(char v[]) { 534 | // Ldalvik/system/DexPathList; 535 | static unsigned int m = 0; 536 | 537 | if (m == 0) { 538 | m = 23; 539 | } else if (m == 29) { 540 | m = 31; 541 | } 542 | 543 | v[0x0] = 'H'; 544 | v[0x1] = 'a'; 545 | v[0x2] = 'g'; 546 | v[0x3] = 'k'; 547 | v[0x4] = '~'; 548 | v[0x5] = '`'; 549 | v[0x6] = 'a'; 550 | v[0x7] = '$'; 551 | v[0x8] = '\x7f'; 552 | v[0x9] = 't'; 553 | v[0xa] = '}'; 554 | v[0xb] = '{'; 555 | v[0xc] = 'u'; 556 | v[0xd] = '|'; 557 | v[0xe] = '='; 558 | v[0xf] = 'W'; 559 | v[0x10] = 'q'; 560 | v[0x11] = 'm'; 561 | v[0x12] = 'F'; 562 | v[0x13] = 'a'; 563 | v[0x14] = 'u'; 564 | v[0x15] = 'j'; 565 | v[0x16] = 'O'; 566 | v[0x17] = 'm'; 567 | v[0x18] = 'v'; 568 | v[0x19] = 'r'; 569 | v[0x1a] = '<'; 570 | for (unsigned int i = 0; i < 0x1b; ++i) { 571 | v[i] ^= ((i + 0x1b) % m); 572 | } 573 | v[0x1b] = '\0'; 574 | } 575 | 576 | static bool showDexPathList(JNIEnv *env, jobject dexPathList, bool *hasInMemoryDex) { 577 | bool abnormal; 578 | jobject nativeLibraryDirectories; 579 | char v[0x40], v2[0x40]; 580 | jclass classDexPathList = env->GetObjectClass(dexPathList); 581 | fill_dexElements(v); 582 | fill_dexElements_signature(v2); 583 | jfieldID fieldDexElements = env->GetFieldID(classDexPathList, v, v2); 584 | if (env->ExceptionCheck()) { 585 | #ifdef DEBUG 586 | env->ExceptionDescribe(); 587 | #endif 588 | env->ExceptionClear(); 589 | } 590 | #ifdef DEBUG 591 | LOGI("fieldDexElements: %p", fieldDexElements); 592 | #endif 593 | fill_nativeLibraryDirectories(v); 594 | fill_nativeLibraryDirectories_signature(v2); 595 | jfieldID fieldNativeLibraryDirectories = env->GetFieldID(classDexPathList, v, v2); 596 | if (env->ExceptionCheck()) { 597 | #ifdef DEBUG 598 | env->ExceptionDescribe(); 599 | #endif 600 | env->ExceptionClear(); 601 | } 602 | #ifdef DEBUG 603 | LOGI("fieldNativeLibraryDirectories: %p", fieldNativeLibraryDirectories); 604 | #endif 605 | fill_java_util_Arrays(v); 606 | jclass classArrays = env->FindClass(v); 607 | fill_toString(v); 608 | fill_Arrays_toString_signature(v2); 609 | jmethodID methodArraysToString = env->GetStaticMethodID(classArrays, v, v2); 610 | fill_java_util_List(v); 611 | jclass classList = env->FindClass(v); 612 | fill_toArray(v); 613 | fill_Arrays_toArray_signature(v2); 614 | jmethodID methodListToArray = env->GetMethodID(classList, v, v2); 615 | fill_isEmpty(v); 616 | fill_Arrays_isEmpty_signature(v2); 617 | jmethodID methodListIsEmpty = env->GetMethodID(classList, v, v2); 618 | if (fieldDexElements == nullptr) { 619 | auto dexPathListAsString = (jstring) env->CallNonvirtualObjectMethod(dexPathList, classDexPathList, methodObjectToString); 620 | const char *dexPathListAsChar = env->GetStringUTFChars(dexPathListAsString, nullptr); 621 | #ifdef DEBUG 622 | LOGI("dexPathList: %s", dexPathListAsChar); 623 | #endif 624 | abnormal = checkAbnormal(dexPathListAsChar, true, hasInMemoryDex); 625 | env->ReleaseStringUTFChars(dexPathListAsString, dexPathListAsChar); 626 | env->DeleteLocalRef(dexPathListAsString); 627 | } else { 628 | auto dexElements = (jobjectArray) env->GetObjectField(dexPathList, fieldDexElements); 629 | auto dexElementsAsString = (jstring) env->CallStaticObjectMethod(classArrays, methodArraysToString, dexElements); 630 | const char *dexElementsAsChar = env->GetStringUTFChars(dexElementsAsString, nullptr); 631 | #ifdef DEBUG 632 | LOGI("dexElements: %s", dexElementsAsChar); 633 | #endif 634 | abnormal = checkAbnormal(dexElementsAsChar, true, hasInMemoryDex); 635 | env->ReleaseStringUTFChars(dexElementsAsString, dexElementsAsChar); 636 | env->DeleteLocalRef(dexElementsAsString); 637 | env->DeleteLocalRef(dexElements); 638 | } 639 | if (fieldNativeLibraryDirectories == nullptr) { 640 | goto clean; 641 | } 642 | nativeLibraryDirectories = env->GetObjectField(dexPathList, fieldNativeLibraryDirectories); 643 | if (nativeLibraryDirectories == nullptr) { 644 | goto clean; 645 | } 646 | if (!env->CallBooleanMethod(nativeLibraryDirectories, methodListIsEmpty)) { 647 | jobject nativeLibraryDirectoriesAsArray = env->CallObjectMethod(nativeLibraryDirectories, methodListToArray); 648 | auto nativeLibraryDirectoriesAsString = (jstring) env->CallStaticObjectMethod(classArrays, methodArraysToString, nativeLibraryDirectoriesAsArray); 649 | const char *nativeLibraryDirectoriesAsChar = env->GetStringUTFChars(nativeLibraryDirectoriesAsString, nullptr); 650 | #ifdef DEBUG 651 | LOGI("nativeLibraryDirectories: %s", nativeLibraryDirectoriesAsChar); 652 | #endif 653 | abnormal |= checkAbnormal(nativeLibraryDirectoriesAsChar, false, hasInMemoryDex); 654 | env->ReleaseStringUTFChars(nativeLibraryDirectoriesAsString, nativeLibraryDirectoriesAsChar); 655 | env->DeleteLocalRef(nativeLibraryDirectoriesAsString); 656 | env->DeleteLocalRef(nativeLibraryDirectoriesAsArray); 657 | } 658 | env->DeleteLocalRef(nativeLibraryDirectories); 659 | clean: 660 | env->DeleteLocalRef(classList); 661 | env->DeleteLocalRef(classArrays); 662 | env->DeleteLocalRef(classDexPathList); 663 | return abnormal; 664 | } 665 | 666 | bool hasAbnormalClassLoader(JNIEnv *env, jclass baseDexClassLoader, jobject object, bool *hasInMemoryDex) { 667 | bool abnormal; 668 | char v[0x40], v2[0x40]; 669 | if (hasInMemoryDex) { 670 | *hasInMemoryDex = false; 671 | } 672 | jclass clazz = env->GetObjectClass(object); 673 | debug(env, "baseDexClassLoader: %s", baseDexClassLoader); 674 | fill_pathList(v); 675 | fill_pathList_signature(v2); 676 | jfieldID fieldPathList = env->GetFieldID(baseDexClassLoader, v, v2); 677 | #ifdef DEBUG 678 | LOGI("fieldPathList: %p", fieldPathList); 679 | #endif 680 | if (env->ExceptionCheck()) { 681 | #ifdef DEBUG 682 | env->ExceptionDescribe(); 683 | #endif 684 | env->ExceptionClear(); 685 | } 686 | if (fieldPathList != nullptr) { 687 | jobject pathList = env->GetObjectField(object, fieldPathList); 688 | abnormal = showDexPathList(env, pathList, hasInMemoryDex); 689 | env->DeleteLocalRef(pathList); 690 | } else { 691 | auto classLoaderAsString = (jstring) env->CallNonvirtualObjectMethod(object, baseDexClassLoader, methodObjectToString); 692 | const char *classLoaderAsChar = env->GetStringUTFChars(classLoaderAsString, nullptr); 693 | abnormal = checkAbnormal(classLoaderAsChar, true, hasInMemoryDex); 694 | env->ReleaseStringUTFChars(classLoaderAsString, classLoaderAsChar); 695 | env->DeleteLocalRef(classLoaderAsString); 696 | } 697 | env->DeleteLocalRef(clazz); 698 | return abnormal; 699 | } -------------------------------------------------------------------------------- /src/main/jni/dex-path-list.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2022/5/3. 3 | // 4 | 5 | #ifndef BREVENT_DEX_PATH_LIST_H 6 | #define BREVENT_DEX_PATH_LIST_H 7 | 8 | #include 9 | #include "jni.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | bool hasAbnormalClassLoader(JNIEnv *env, jclass baseDexClassLoader, jobject object, bool *hasInMemoryDex); 16 | 17 | #ifdef __cplusplus 18 | } 19 | #endif 20 | 21 | #endif //BREVENT_DEX_PATH_LIST_H 22 | -------------------------------------------------------------------------------- /src/main/jni/epic-field.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2020/9/9. 3 | // 4 | 5 | #include "epic-field.h" 6 | #include 7 | 8 | #define STATIC 0x00000008 9 | 10 | #ifdef CHECK_XPOSED_EPIC 11 | static inline void fill_java_lang_Class(char v[]) { 12 | // java/lang/Class 13 | static unsigned int m = 0; 14 | 15 | if (m == 0) { 16 | m = 13; 17 | } else if (m == 17) { 18 | m = 19; 19 | } 20 | 21 | v[0x0] = 'h'; 22 | v[0x1] = 'b'; 23 | v[0x2] = 'r'; 24 | v[0x3] = 'd'; 25 | v[0x4] = ')'; 26 | v[0x5] = 'k'; 27 | v[0x6] = 'i'; 28 | v[0x7] = 'g'; 29 | v[0x8] = 'm'; 30 | v[0x9] = '$'; 31 | v[0xa] = 'O'; 32 | v[0xb] = 'l'; 33 | v[0xc] = '`'; 34 | v[0xd] = 'q'; 35 | v[0xe] = 'p'; 36 | for (unsigned int i = 0; i < 0xf; ++i) { 37 | v[i] ^= ((i + 0xf) % m); 38 | } 39 | v[0xf] = '\0'; 40 | } 41 | 42 | static inline void fill_getDeclaredFields(char v[]) { 43 | // getDeclaredFields 44 | static unsigned int m = 0; 45 | 46 | if (m == 0) { 47 | m = 13; 48 | } else if (m == 17) { 49 | m = 19; 50 | } 51 | 52 | v[0x0] = 'c'; 53 | v[0x1] = '`'; 54 | v[0x2] = 'r'; 55 | v[0x3] = 'C'; 56 | v[0x4] = 'm'; 57 | v[0x5] = 'j'; 58 | v[0x6] = 'f'; 59 | v[0x7] = 'j'; 60 | v[0x8] = '~'; 61 | v[0x9] = 'e'; 62 | v[0xa] = 'e'; 63 | v[0xb] = 'D'; 64 | v[0xc] = 'j'; 65 | v[0xd] = 'a'; 66 | v[0xe] = 'i'; 67 | v[0xf] = 'b'; 68 | v[0x10] = 't'; 69 | for (unsigned int i = 0; i < 0x11; ++i) { 70 | v[i] ^= ((i + 0x11) % m); 71 | } 72 | v[0x11] = '\0'; 73 | } 74 | 75 | static inline void fill_getDeclaredFields_signature(char v[]) { 76 | // ()[Ljava/lang/reflect/Field; 77 | static unsigned int m = 0; 78 | 79 | if (m == 0) { 80 | m = 23; 81 | } else if (m == 29) { 82 | m = 31; 83 | } 84 | 85 | v[0x0] = '-'; 86 | v[0x1] = '/'; 87 | v[0x2] = '\\'; 88 | v[0x3] = 'D'; 89 | v[0x4] = 'c'; 90 | v[0x5] = 'k'; 91 | v[0x6] = '}'; 92 | v[0x7] = 'm'; 93 | v[0x8] = '"'; 94 | v[0x9] = 'b'; 95 | v[0xa] = 'n'; 96 | v[0xb] = '~'; 97 | v[0xc] = 'v'; 98 | v[0xd] = '='; 99 | v[0xe] = 'a'; 100 | v[0xf] = 'q'; 101 | v[0x10] = 's'; 102 | v[0x11] = 'z'; 103 | v[0x12] = 'e'; 104 | v[0x13] = 'b'; 105 | v[0x14] = 'v'; 106 | v[0x15] = ','; 107 | v[0x16] = 'B'; 108 | v[0x17] = 'l'; 109 | v[0x18] = 'c'; 110 | v[0x19] = 'k'; 111 | v[0x1a] = 'l'; 112 | v[0x1b] = '2'; 113 | for (unsigned int i = 0; i < 0x1c; ++i) { 114 | v[i] ^= ((i + 0x1c) % m); 115 | } 116 | v[0x1c] = '\0'; 117 | } 118 | 119 | static inline void fill_java_lang_reflect_Field(char v[]) { 120 | // java/lang/reflect/Field 121 | static unsigned int m = 0; 122 | 123 | if (m == 0) { 124 | m = 19; 125 | } else if (m == 23) { 126 | m = 29; 127 | } 128 | 129 | v[0x0] = 'n'; 130 | v[0x1] = 'd'; 131 | v[0x2] = 'p'; 132 | v[0x3] = 'f'; 133 | v[0x4] = '\''; 134 | v[0x5] = 'e'; 135 | v[0x6] = 'k'; 136 | v[0x7] = 'e'; 137 | v[0x8] = 'k'; 138 | v[0x9] = '"'; 139 | v[0xa] = '|'; 140 | v[0xb] = 'j'; 141 | v[0xc] = 'v'; 142 | v[0xd] = '}'; 143 | v[0xe] = 'w'; 144 | v[0xf] = 'c'; 145 | v[0x10] = 'u'; 146 | v[0x11] = '-'; 147 | v[0x12] = 'E'; 148 | v[0x13] = 'm'; 149 | v[0x14] = '`'; 150 | v[0x15] = 'j'; 151 | v[0x16] = 'c'; 152 | for (unsigned int i = 0; i < 0x17; ++i) { 153 | v[i] ^= ((i + 0x17) % m); 154 | } 155 | v[0x17] = '\0'; 156 | } 157 | 158 | static inline void fill_getModifiers(char v[]) { 159 | // getModifiers 160 | static unsigned int m = 0; 161 | 162 | if (m == 0) { 163 | m = 11; 164 | } else if (m == 13) { 165 | m = 17; 166 | } 167 | 168 | v[0x0] = 'f'; 169 | v[0x1] = 'g'; 170 | v[0x2] = 'w'; 171 | v[0x3] = 'I'; 172 | v[0x4] = 'j'; 173 | v[0x5] = 'b'; 174 | v[0x6] = 'n'; 175 | v[0x7] = 'n'; 176 | v[0x8] = '`'; 177 | v[0x9] = 'o'; 178 | v[0xa] = 'r'; 179 | v[0xb] = 'r'; 180 | for (unsigned int i = 0; i < 0xc; ++i) { 181 | v[i] ^= ((i + 0xc) % m); 182 | } 183 | v[0xc] = '\0'; 184 | } 185 | 186 | static inline void fill_getModifiers_signature(char v[]) { 187 | // ()I 188 | static unsigned int m = 0; 189 | 190 | if (m == 0) { 191 | m = 2; 192 | } else if (m == 3) { 193 | m = 5; 194 | } 195 | 196 | v[0x0] = ')'; 197 | v[0x1] = ')'; 198 | v[0x2] = 'H'; 199 | for (unsigned int i = 0; i < 0x3; ++i) { 200 | v[i] ^= ((i + 0x3) % m); 201 | } 202 | v[0x3] = '\0'; 203 | } 204 | 205 | static inline void fill_getType(char v[]) { 206 | // getType 207 | static unsigned int m = 0; 208 | 209 | if (m == 0) { 210 | m = 5; 211 | } else if (m == 7) { 212 | m = 11; 213 | } 214 | 215 | v[0x0] = 'e'; 216 | v[0x1] = 'f'; 217 | v[0x2] = 'p'; 218 | v[0x3] = 'T'; 219 | v[0x4] = 'x'; 220 | v[0x5] = 'r'; 221 | v[0x6] = 'f'; 222 | for (unsigned int i = 0; i < 0x7; ++i) { 223 | v[i] ^= ((i + 0x7) % m); 224 | } 225 | v[0x7] = '\0'; 226 | } 227 | 228 | static inline void fill_getType_signature(char v[]) { 229 | // ()Ljava/lang/Class; 230 | static unsigned int m = 0; 231 | 232 | if (m == 0) { 233 | m = 17; 234 | } else if (m == 19) { 235 | m = 23; 236 | } 237 | 238 | v[0x0] = '*'; 239 | v[0x1] = '*'; 240 | v[0x2] = 'H'; 241 | v[0x3] = 'o'; 242 | v[0x4] = 'g'; 243 | v[0x5] = 'q'; 244 | v[0x6] = 'i'; 245 | v[0x7] = '&'; 246 | v[0x8] = 'f'; 247 | v[0x9] = 'j'; 248 | v[0xa] = 'b'; 249 | v[0xb] = 'j'; 250 | v[0xc] = '!'; 251 | v[0xd] = 'L'; 252 | v[0xe] = '|'; 253 | v[0xf] = 'a'; 254 | v[0x10] = 'r'; 255 | v[0x11] = 'q'; 256 | v[0x12] = '8'; 257 | for (unsigned int i = 0; i < 0x13; ++i) { 258 | v[i] ^= ((i + 0x13) % m); 259 | } 260 | v[0x13] = '\0'; 261 | } 262 | 263 | static inline void fill_getName(char v[]) { 264 | // getName 265 | static unsigned int m = 0; 266 | 267 | if (m == 0) { 268 | m = 5; 269 | } else if (m == 7) { 270 | m = 11; 271 | } 272 | 273 | v[0x0] = 'e'; 274 | v[0x1] = 'f'; 275 | v[0x2] = 'p'; 276 | v[0x3] = 'N'; 277 | v[0x4] = '`'; 278 | v[0x5] = 'o'; 279 | v[0x6] = 'f'; 280 | for (unsigned int i = 0; i < 0x7; ++i) { 281 | v[i] ^= ((i + 0x7) % m); 282 | } 283 | v[0x7] = '\0'; 284 | } 285 | 286 | static inline void fill_getName_signature(char v[]) { 287 | // ()Ljava/lang/String; 288 | static unsigned int m = 0; 289 | 290 | if (m == 0) { 291 | m = 19; 292 | } else if (m == 23) { 293 | m = 29; 294 | } 295 | 296 | v[0x0] = ')'; 297 | v[0x1] = '+'; 298 | v[0x2] = 'O'; 299 | v[0x3] = 'n'; 300 | v[0x4] = 'd'; 301 | v[0x5] = 'p'; 302 | v[0x6] = 'f'; 303 | v[0x7] = '\''; 304 | v[0x8] = 'e'; 305 | v[0x9] = 'k'; 306 | v[0xa] = 'e'; 307 | v[0xb] = 'k'; 308 | v[0xc] = '"'; 309 | v[0xd] = ']'; 310 | v[0xe] = '{'; 311 | v[0xf] = 'b'; 312 | v[0x10] = 'x'; 313 | v[0x11] = '|'; 314 | v[0x12] = 'g'; 315 | v[0x13] = ':'; 316 | for (unsigned int i = 0; i < 0x14; ++i) { 317 | v[i] ^= ((i + 0x14) % m); 318 | } 319 | v[0x14] = '\0'; 320 | } 321 | 322 | static inline void fill_L_java_lang_Object(char v[]) { 323 | // [Ljava/lang/Object; 324 | static unsigned int m = 0; 325 | 326 | if (m == 0) { 327 | m = 17; 328 | } else if (m == 19) { 329 | m = 23; 330 | } 331 | 332 | v[0x0] = 'Y'; 333 | v[0x1] = 'O'; 334 | v[0x2] = 'n'; 335 | v[0x3] = 'd'; 336 | v[0x4] = 'p'; 337 | v[0x5] = 'f'; 338 | v[0x6] = '\''; 339 | v[0x7] = 'e'; 340 | v[0x8] = 'k'; 341 | v[0x9] = 'e'; 342 | v[0xa] = 'k'; 343 | v[0xb] = '"'; 344 | v[0xc] = 'A'; 345 | v[0xd] = 'm'; 346 | v[0xe] = 'z'; 347 | v[0xf] = 'e'; 348 | v[0x10] = 'b'; 349 | v[0x11] = 'v'; 350 | v[0x12] = '8'; 351 | for (unsigned int i = 0; i < 0x13; ++i) { 352 | v[i] ^= ((i + 0x13) % m); 353 | } 354 | v[0x13] = '\0'; 355 | } 356 | 357 | static char *findField(JNIEnv *env, jobject clazz, int staticFlag, jclass type) { 358 | char v1[0x20], v2[0x20]; 359 | char *name = NULL; 360 | 361 | fill_java_lang_Class(v1); 362 | jclass classClass = (*env)->FindClass(env, v1); 363 | 364 | fill_getDeclaredFields(v1); 365 | fill_getDeclaredFields_signature(v2); 366 | jmethodID getDeclaredFields = (*env)->GetMethodID(env, classClass, v1, v2); 367 | 368 | fill_java_lang_reflect_Field(v1); 369 | jclass classField = (*env)->FindClass(env, v1); 370 | 371 | fill_getModifiers(v1); 372 | fill_getModifiers_signature(v2); 373 | jmethodID getModifiers = (*env)->GetMethodID(env, classField, v1, v2); 374 | 375 | fill_getType(v1); 376 | fill_getType_signature(v2); 377 | jmethodID getType = (*env)->GetMethodID(env, classField, v1, v2); 378 | 379 | fill_getName(v1); 380 | fill_getName_signature(v2); 381 | jmethodID getName = (*env)->GetMethodID(env, classField, v1, v2); 382 | 383 | jobjectArray fields = (*env)->CallObjectMethod(env, clazz, getDeclaredFields); 384 | int length = (*env)->GetArrayLength(env, fields); 385 | for (int i = 0; i < length; ++i) { 386 | jobject field = (*env)->GetObjectArrayElement(env, fields, i); 387 | debug(env, "field: %s", field); 388 | int modifier = (*env)->CallIntMethod(env, field, getModifiers); 389 | if ((modifier & STATIC) == staticFlag 390 | && (*env)->IsSameObject(env, type, (*env)->CallObjectMethod(env, field, getType))) { 391 | jstring fieldString = (*env)->CallObjectMethod(env, field, getName); 392 | const char *fieldName = (*env)->GetStringUTFChars(env, fieldString, NULL); 393 | name = strdup(fieldName); 394 | (*env)->ReleaseStringUTFChars(env, fieldString, fieldName); 395 | (*env)->DeleteLocalRef(env, fieldString); 396 | } 397 | (*env)->DeleteLocalRef(env, field); 398 | if (name != NULL) { 399 | break; 400 | } 401 | } 402 | 403 | (*env)->DeleteLocalRef(env, fields); 404 | (*env)->DeleteLocalRef(env, classField); 405 | (*env)->DeleteLocalRef(env, classClass); 406 | return name; 407 | } 408 | 409 | char *findObjectArrayName(JNIEnv *env, jobject clazz) { 410 | char v[0x20]; 411 | fill_L_java_lang_Object(v); 412 | jclass classObjectArray = (*env)->FindClass(env, v); 413 | char *name = findField(env, clazz, 0, classObjectArray); 414 | (*env)->DeleteLocalRef(env, classObjectArray); 415 | return name; 416 | } 417 | 418 | static inline void fill_java_util_Map(char v[]) { 419 | // java/util/Map 420 | static unsigned int m = 0; 421 | 422 | if (m == 0) { 423 | m = 11; 424 | } else if (m == 13) { 425 | m = 17; 426 | } 427 | 428 | v[0x0] = 'h'; 429 | v[0x1] = 'b'; 430 | v[0x2] = 'r'; 431 | v[0x3] = 'd'; 432 | v[0x4] = ')'; 433 | v[0x5] = 'r'; 434 | v[0x6] = '|'; 435 | v[0x7] = '`'; 436 | v[0x8] = 'f'; 437 | v[0x9] = '/'; 438 | v[0xa] = 'L'; 439 | v[0xb] = 'c'; 440 | v[0xc] = 's'; 441 | for (unsigned int i = 0; i < 0xd; ++i) { 442 | v[i] ^= ((i + 0xd) % m); 443 | } 444 | v[0xd] = '\0'; 445 | } 446 | 447 | char *findStaticMapName(JNIEnv *env, jobject clazz) { 448 | char v[0x20]; 449 | fill_java_util_Map(v); 450 | jclass classMap = (*env)->FindClass(env, v); 451 | char *name = findField(env, clazz, STATIC, classMap); 452 | (*env)->DeleteLocalRef(env, classMap); 453 | return name; 454 | } 455 | #endif 456 | -------------------------------------------------------------------------------- /src/main/jni/epic-field.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2020/9/9. 3 | // 4 | 5 | #ifndef BREVENT_EPIC_FIELD_H 6 | #define BREVENT_EPIC_FIELD_H 7 | 8 | #include "common.h" 9 | #include 10 | 11 | char *findObjectArrayName(JNIEnv *env, jobject clazz); 12 | 13 | char *findStaticMapName(JNIEnv *env, jobject clazz); 14 | 15 | #endif //BREVENT_EPIC_FIELD_H 16 | -------------------------------------------------------------------------------- /src/main/jni/epic-method.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2020/9/9. 3 | // 4 | 5 | #include "epic-method.h" 6 | #include 7 | #include 8 | 9 | #define NATIVE 0x00000100 10 | #define STATIC 0x00000008 11 | 12 | #ifdef CHECK_XPOSED_EPIC 13 | static inline void fill_java_lang_Class(char v[]) { 14 | // java/lang/Class 15 | static unsigned int m = 0; 16 | 17 | if (m == 0) { 18 | m = 13; 19 | } else if (m == 17) { 20 | m = 19; 21 | } 22 | 23 | v[0x0] = 'h'; 24 | v[0x1] = 'b'; 25 | v[0x2] = 'r'; 26 | v[0x3] = 'd'; 27 | v[0x4] = ')'; 28 | v[0x5] = 'k'; 29 | v[0x6] = 'i'; 30 | v[0x7] = 'g'; 31 | v[0x8] = 'm'; 32 | v[0x9] = '$'; 33 | v[0xa] = 'O'; 34 | v[0xb] = 'l'; 35 | v[0xc] = '`'; 36 | v[0xd] = 'q'; 37 | v[0xe] = 'p'; 38 | for (unsigned int i = 0; i < 0xf; ++i) { 39 | v[i] ^= ((i + 0xf) % m); 40 | } 41 | v[0xf] = '\0'; 42 | } 43 | 44 | static inline void fill_getDeclaredMethods(char v[]) { 45 | // getDeclaredMethods 46 | static unsigned int m = 0; 47 | 48 | if (m == 0) { 49 | m = 17; 50 | } else if (m == 19) { 51 | m = 23; 52 | } 53 | 54 | v[0x0] = 'f'; 55 | v[0x1] = 'g'; 56 | v[0x2] = 'w'; 57 | v[0x3] = '@'; 58 | v[0x4] = '`'; 59 | v[0x5] = 'e'; 60 | v[0x6] = 'k'; 61 | v[0x7] = 'i'; 62 | v[0x8] = '{'; 63 | v[0x9] = 'o'; 64 | v[0xa] = 'o'; 65 | v[0xb] = 'A'; 66 | v[0xc] = 'h'; 67 | v[0xd] = 'z'; 68 | v[0xe] = 'g'; 69 | v[0xf] = '\x7f'; 70 | v[0x10] = 'd'; 71 | v[0x11] = 'r'; 72 | for (unsigned int i = 0; i < 0x12; ++i) { 73 | v[i] ^= ((i + 0x12) % m); 74 | } 75 | v[0x12] = '\0'; 76 | } 77 | 78 | static inline void fill_getDeclaredMethods_signature(char v[]) { 79 | // ()[Ljava/lang/reflect/Method; 80 | static unsigned int m = 0; 81 | 82 | if (m == 0) { 83 | m = 23; 84 | } else if (m == 29) { 85 | m = 31; 86 | } 87 | 88 | v[0x0] = '.'; 89 | v[0x1] = '.'; 90 | v[0x2] = 'S'; 91 | v[0x3] = 'E'; 92 | v[0x4] = '`'; 93 | v[0x5] = 'j'; 94 | v[0x6] = 'z'; 95 | v[0x7] = 'l'; 96 | v[0x8] = '!'; 97 | v[0x9] = 'c'; 98 | v[0xa] = 'q'; 99 | v[0xb] = '\x7f'; 100 | v[0xc] = 'u'; 101 | v[0xd] = '<'; 102 | v[0xe] = 'f'; 103 | v[0xf] = 'p'; 104 | v[0x10] = 'p'; 105 | v[0x11] = 'l'; 106 | v[0x12] = 'd'; 107 | v[0x13] = 'a'; 108 | v[0x14] = 'w'; 109 | v[0x15] = '+'; 110 | v[0x16] = 'H'; 111 | v[0x17] = 'c'; 112 | v[0x18] = 's'; 113 | v[0x19] = '`'; 114 | v[0x1a] = 'f'; 115 | v[0x1b] = 'n'; 116 | v[0x1c] = '0'; 117 | for (unsigned int i = 0; i < 0x1d; ++i) { 118 | v[i] ^= ((i + 0x1d) % m); 119 | } 120 | v[0x1d] = '\0'; 121 | } 122 | 123 | static inline void fill_java_lang_reflect_Method(char v[]) { 124 | // java/lang/reflect/Method 125 | static unsigned int m = 0; 126 | 127 | if (m == 0) { 128 | m = 23; 129 | } else if (m == 29) { 130 | m = 31; 131 | } 132 | 133 | v[0x0] = 'k'; 134 | v[0x1] = 'c'; 135 | v[0x2] = 'u'; 136 | v[0x3] = 'e'; 137 | v[0x4] = '*'; 138 | v[0x5] = 'j'; 139 | v[0x6] = 'f'; 140 | v[0x7] = 'f'; 141 | v[0x8] = 'n'; 142 | v[0x9] = '%'; 143 | v[0xa] = 'y'; 144 | v[0xb] = 'i'; 145 | v[0xc] = 'k'; 146 | v[0xd] = 'b'; 147 | v[0xe] = 'j'; 148 | v[0xf] = 's'; 149 | v[0x10] = 'e'; 150 | v[0x11] = '='; 151 | v[0x12] = '^'; 152 | v[0x13] = 'q'; 153 | v[0x14] = 'a'; 154 | v[0x15] = '~'; 155 | v[0x16] = 'o'; 156 | v[0x17] = 'e'; 157 | for (unsigned int i = 0; i < 0x18; ++i) { 158 | v[i] ^= ((i + 0x18) % m); 159 | } 160 | v[0x18] = '\0'; 161 | } 162 | 163 | static inline void fill_getModifiers(char v[]) { 164 | // getModifiers 165 | static unsigned int m = 0; 166 | 167 | if (m == 0) { 168 | m = 11; 169 | } else if (m == 13) { 170 | m = 17; 171 | } 172 | 173 | v[0x0] = 'f'; 174 | v[0x1] = 'g'; 175 | v[0x2] = 'w'; 176 | v[0x3] = 'I'; 177 | v[0x4] = 'j'; 178 | v[0x5] = 'b'; 179 | v[0x6] = 'n'; 180 | v[0x7] = 'n'; 181 | v[0x8] = '`'; 182 | v[0x9] = 'o'; 183 | v[0xa] = 'r'; 184 | v[0xb] = 'r'; 185 | for (unsigned int i = 0; i < 0xc; ++i) { 186 | v[i] ^= ((i + 0xc) % m); 187 | } 188 | v[0xc] = '\0'; 189 | } 190 | 191 | static inline void fill_getModifiers_signature(char v[]) { 192 | // ()I 193 | static unsigned int m = 0; 194 | 195 | if (m == 0) { 196 | m = 2; 197 | } else if (m == 3) { 198 | m = 5; 199 | } 200 | 201 | v[0x0] = ')'; 202 | v[0x1] = ')'; 203 | v[0x2] = 'H'; 204 | for (unsigned int i = 0; i < 0x3; ++i) { 205 | v[i] ^= ((i + 0x3) % m); 206 | } 207 | v[0x3] = '\0'; 208 | } 209 | 210 | static inline void fill_getParameterTypes(char v[]) { 211 | // getParameterTypes 212 | static unsigned int m = 0; 213 | 214 | if (m == 0) { 215 | m = 13; 216 | } else if (m == 17) { 217 | m = 19; 218 | } 219 | 220 | v[0x0] = 'c'; 221 | v[0x1] = '`'; 222 | v[0x2] = 'r'; 223 | v[0x3] = 'W'; 224 | v[0x4] = 'i'; 225 | v[0x5] = '{'; 226 | v[0x6] = 'k'; 227 | v[0x7] = 'f'; 228 | v[0x8] = 'i'; 229 | v[0x9] = 't'; 230 | v[0xa] = 'd'; 231 | v[0xb] = 'p'; 232 | v[0xc] = 'W'; 233 | v[0xd] = '}'; 234 | v[0xe] = 'u'; 235 | v[0xf] = 'c'; 236 | v[0x10] = 't'; 237 | for (unsigned int i = 0; i < 0x11; ++i) { 238 | v[i] ^= ((i + 0x11) % m); 239 | } 240 | v[0x11] = '\0'; 241 | } 242 | 243 | static inline void fill_getParameterTypes_signature(char v[]) { 244 | // ()[Ljava/lang/Class; 245 | static unsigned int m = 0; 246 | 247 | if (m == 0) { 248 | m = 19; 249 | } else if (m == 23) { 250 | m = 29; 251 | } 252 | 253 | v[0x0] = ')'; 254 | v[0x1] = '+'; 255 | v[0x2] = 'X'; 256 | v[0x3] = 'H'; 257 | v[0x4] = 'o'; 258 | v[0x5] = 'g'; 259 | v[0x6] = 'q'; 260 | v[0x7] = 'i'; 261 | v[0x8] = '&'; 262 | v[0x9] = 'f'; 263 | v[0xa] = 'j'; 264 | v[0xb] = 'b'; 265 | v[0xc] = 'j'; 266 | v[0xd] = '!'; 267 | v[0xe] = 'L'; 268 | v[0xf] = '|'; 269 | v[0x10] = 'p'; 270 | v[0x11] = 'a'; 271 | v[0x12] = 's'; 272 | v[0x13] = ':'; 273 | for (unsigned int i = 0; i < 0x14; ++i) { 274 | v[i] ^= ((i + 0x14) % m); 275 | } 276 | v[0x14] = '\0'; 277 | } 278 | 279 | static inline void fill_getReturnType(char v[]) { 280 | // getReturnType 281 | static unsigned int m = 0; 282 | 283 | if (m == 0) { 284 | m = 11; 285 | } else if (m == 13) { 286 | m = 17; 287 | } 288 | 289 | v[0x0] = 'e'; 290 | v[0x1] = 'f'; 291 | v[0x2] = 'p'; 292 | v[0x3] = 'W'; 293 | v[0x4] = 'c'; 294 | v[0x5] = 's'; 295 | v[0x6] = '}'; 296 | v[0x7] = '{'; 297 | v[0x8] = 'd'; 298 | v[0x9] = 'T'; 299 | v[0xa] = 'x'; 300 | v[0xb] = 'r'; 301 | v[0xc] = 'f'; 302 | for (unsigned int i = 0; i < 0xd; ++i) { 303 | v[i] ^= ((i + 0xd) % m); 304 | } 305 | v[0xd] = '\0'; 306 | } 307 | 308 | static inline void fill_getReturnType_signature(char v[]) { 309 | // ()Ljava/lang/Class; 310 | static unsigned int m = 0; 311 | 312 | if (m == 0) { 313 | m = 17; 314 | } else if (m == 19) { 315 | m = 23; 316 | } 317 | 318 | v[0x0] = '*'; 319 | v[0x1] = '*'; 320 | v[0x2] = 'H'; 321 | v[0x3] = 'o'; 322 | v[0x4] = 'g'; 323 | v[0x5] = 'q'; 324 | v[0x6] = 'i'; 325 | v[0x7] = '&'; 326 | v[0x8] = 'f'; 327 | v[0x9] = 'j'; 328 | v[0xa] = 'b'; 329 | v[0xb] = 'j'; 330 | v[0xc] = '!'; 331 | v[0xd] = 'L'; 332 | v[0xe] = '|'; 333 | v[0xf] = 'a'; 334 | v[0x10] = 'r'; 335 | v[0x11] = 'q'; 336 | v[0x12] = '8'; 337 | for (unsigned int i = 0; i < 0x13; ++i) { 338 | v[i] ^= ((i + 0x13) % m); 339 | } 340 | v[0x13] = '\0'; 341 | } 342 | 343 | static inline void fill_getName(char v[]) { 344 | // getName 345 | static unsigned int m = 0; 346 | 347 | if (m == 0) { 348 | m = 5; 349 | } else if (m == 7) { 350 | m = 11; 351 | } 352 | 353 | v[0x0] = 'e'; 354 | v[0x1] = 'f'; 355 | v[0x2] = 'p'; 356 | v[0x3] = 'N'; 357 | v[0x4] = '`'; 358 | v[0x5] = 'o'; 359 | v[0x6] = 'f'; 360 | for (unsigned int i = 0; i < 0x7; ++i) { 361 | v[i] ^= ((i + 0x7) % m); 362 | } 363 | v[0x7] = '\0'; 364 | } 365 | 366 | static inline void fill_getName_signature(char v[]) { 367 | // ()Ljava/lang/String; 368 | static unsigned int m = 0; 369 | 370 | if (m == 0) { 371 | m = 19; 372 | } else if (m == 23) { 373 | m = 29; 374 | } 375 | 376 | v[0x0] = ')'; 377 | v[0x1] = '+'; 378 | v[0x2] = 'O'; 379 | v[0x3] = 'n'; 380 | v[0x4] = 'd'; 381 | v[0x5] = 'p'; 382 | v[0x6] = 'f'; 383 | v[0x7] = '\''; 384 | v[0x8] = 'e'; 385 | v[0x9] = 'k'; 386 | v[0xa] = 'e'; 387 | v[0xb] = 'k'; 388 | v[0xc] = '"'; 389 | v[0xd] = ']'; 390 | v[0xe] = '{'; 391 | v[0xf] = 'b'; 392 | v[0x10] = 'x'; 393 | v[0x11] = '|'; 394 | v[0x12] = 'g'; 395 | v[0x13] = ':'; 396 | for (unsigned int i = 0; i < 0x14; ++i) { 397 | v[i] ^= ((i + 0x14) % m); 398 | } 399 | v[0x14] = '\0'; 400 | } 401 | 402 | static inline void fill_java_lang_String(char v[]) { 403 | // java/lang/String 404 | static unsigned int m = 0; 405 | 406 | if (m == 0) { 407 | m = 13; 408 | } else if (m == 17) { 409 | m = 19; 410 | } 411 | 412 | v[0x0] = 'i'; 413 | v[0x1] = 'e'; 414 | v[0x2] = 's'; 415 | v[0x3] = 'g'; 416 | v[0x4] = '('; 417 | v[0x5] = 'd'; 418 | v[0x6] = 'h'; 419 | v[0x7] = 'd'; 420 | v[0x8] = 'l'; 421 | v[0x9] = '#'; 422 | v[0xa] = 'S'; 423 | v[0xb] = 'u'; 424 | v[0xc] = 'p'; 425 | v[0xd] = 'j'; 426 | v[0xe] = 'j'; 427 | v[0xf] = 'b'; 428 | for (unsigned int i = 0; i < 0x10; ++i) { 429 | v[i] ^= ((i + 0x10) % m); 430 | } 431 | v[0x10] = '\0'; 432 | } 433 | 434 | static inline void fill_signature(char v[]) { 435 | // ()Ljava/lang/String; 436 | static unsigned int m = 0; 437 | 438 | if (m == 0) { 439 | m = 19; 440 | } else if (m == 23) { 441 | m = 29; 442 | } 443 | 444 | v[0x0] = ')'; 445 | v[0x1] = '+'; 446 | v[0x2] = 'O'; 447 | v[0x3] = 'n'; 448 | v[0x4] = 'd'; 449 | v[0x5] = 'p'; 450 | v[0x6] = 'f'; 451 | v[0x7] = '\''; 452 | v[0x8] = 'e'; 453 | v[0x9] = 'k'; 454 | v[0xa] = 'e'; 455 | v[0xb] = 'k'; 456 | v[0xc] = '"'; 457 | v[0xd] = ']'; 458 | v[0xe] = '{'; 459 | v[0xf] = 'b'; 460 | v[0x10] = 'x'; 461 | v[0x11] = '|'; 462 | v[0x12] = 'g'; 463 | v[0x13] = ':'; 464 | for (unsigned int i = 0; i < 0x14; ++i) { 465 | v[i] ^= ((i + 0x14) % m); 466 | } 467 | v[0x14] = '\0'; 468 | } 469 | 470 | char *findVoidStringName(JNIEnv *env, jclass clazz) { 471 | char v1[0x20], v2[0x20]; 472 | char signature[0x20]; 473 | char *name = NULL; 474 | 475 | fill_java_lang_Class(v1); 476 | jclass classClass = (*env)->FindClass(env, v1); 477 | 478 | fill_getDeclaredMethods(v1); 479 | fill_getDeclaredMethods_signature(v2); 480 | jmethodID getDeclaredMethods = (*env)->GetMethodID(env, classClass, v1, v2); 481 | 482 | fill_java_lang_reflect_Method(v1); 483 | jclass classMethod = (*env)->FindClass(env, v1); 484 | 485 | fill_getModifiers(v1); 486 | fill_getModifiers_signature(v2); 487 | jmethodID getModifiers = (*env)->GetMethodID(env, classMethod, v1, v2); 488 | 489 | fill_getParameterTypes(v1); 490 | fill_getParameterTypes_signature(v2); 491 | jmethodID getParameterTypes = (*env)->GetMethodID(env, classMethod, v1, v2); 492 | 493 | fill_getReturnType(v1); 494 | fill_getReturnType_signature(v2); 495 | jmethodID getReturnType = (*env)->GetMethodID(env, classMethod, v1, v2); 496 | 497 | fill_getName(v1); 498 | fill_getName_signature(v2); 499 | jmethodID getName = (*env)->GetMethodID(env, classMethod, v1, v2); 500 | 501 | fill_java_lang_String(v1); 502 | jclass classString = (*env)->FindClass(env, v1); 503 | 504 | jobjectArray methods = (*env)->CallObjectMethod(env, clazz, getDeclaredMethods); 505 | int length = (*env)->GetArrayLength(env, methods); 506 | fill_signature(signature); 507 | for (int i = 0; i < length; ++i) { 508 | jobject method = (*env)->GetObjectArrayElement(env, methods, i); 509 | debug(env, "method: %s", method); 510 | int modifier = (*env)->CallIntMethod(env, method, getModifiers); 511 | if ((modifier & (NATIVE | STATIC)) == STATIC 512 | && (*env)->GetArrayLength(env, (*env)->CallObjectMethod(env, method, getParameterTypes)) == 0 513 | && (*env)->IsSameObject(env, classString, (*env)->CallObjectMethod(env, method, getReturnType))) { 514 | jstring methodString = (*env)->CallObjectMethod(env, method, getName); 515 | const char *methodName = (*env)->GetStringUTFChars(env, methodString, NULL); 516 | jmethodID methodMethod = (*env)->GetStaticMethodID(env, clazz, methodName, signature); 517 | jstring bridgeString = (*env)->CallStaticObjectMethod(env, clazz, methodMethod); 518 | if ((*env)->ExceptionCheck(env)) { 519 | (*env)->ExceptionClear(env); 520 | } 521 | if (bridgeString != NULL) { 522 | const char *bridgeName = (*env)->GetStringUTFChars(env, bridgeString, NULL); 523 | #ifdef DEBUG 524 | LOGI("bridgeName: %s", bridgeName); 525 | #endif 526 | if (*bridgeName == 'L') { 527 | name = strdup(bridgeName + 1); 528 | } else { 529 | name = strdup(bridgeName); 530 | } 531 | (*env)->ReleaseStringUTFChars(env, bridgeString, bridgeName); 532 | (*env)->DeleteLocalRef(env, bridgeString); 533 | } 534 | (*env)->ReleaseStringUTFChars(env, methodString, methodName); 535 | (*env)->DeleteLocalRef(env, methodString); 536 | } 537 | (*env)->DeleteLocalRef(env, method); 538 | if (name != NULL) { 539 | char *x = name; 540 | while (*x != ';' && *x != '\0') { 541 | x++; 542 | } 543 | *x = '\0'; 544 | break; 545 | } 546 | } 547 | 548 | (*env)->DeleteLocalRef(env, methods); 549 | (*env)->DeleteLocalRef(env, classString); 550 | (*env)->DeleteLocalRef(env, classMethod); 551 | (*env)->DeleteLocalRef(env, classClass); 552 | return name; 553 | } 554 | #endif 555 | -------------------------------------------------------------------------------- /src/main/jni/epic-method.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2020/9/9. 3 | // 4 | 5 | #ifndef BREVENT_EPIC_METHOD_H 6 | #define BREVENT_EPIC_METHOD_H 7 | 8 | #include "common.h" 9 | #include 10 | 11 | char *findVoidStringName(JNIEnv *env, jobject clazz); 12 | 13 | #endif //BREVENT_EPIC_METHOD_H 14 | -------------------------------------------------------------------------------- /src/main/jni/epic.h: -------------------------------------------------------------------------------- 1 | #ifndef EPIC_H 2 | #define EPIC_H 3 | 4 | #include 5 | #include 6 | #include "common.h" 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | #ifdef CHECK_XPOSED_EPIC 13 | 14 | bool doAntiEpic(JNIEnv *env, jobject classLoader); 15 | 16 | #endif 17 | 18 | void clearHandler(JNIEnv *env, int sdk); 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | 24 | #endif -------------------------------------------------------------------------------- /src/main/jni/genuine_extra.c: -------------------------------------------------------------------------------- 1 | #include "genuine_extra.h" 2 | 3 | // FIXME: your own methods 4 | 5 | jint JNI_OnLoad_Extra(JNIEnv *env __unused, jclass clazz __unused) { 6 | // FIXME: register your own methods 7 | // return env->RegisterNatives(clazz, methods, NELEM(methods)); 8 | return JNI_OK; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/jni/genuine_extra.h: -------------------------------------------------------------------------------- 1 | #ifndef GENUINE_EXTRA_H 2 | #define GENUINE_EXTRA_H 3 | 4 | #include 5 | #include 6 | 7 | #ifndef NELEM 8 | #define NELEM(x) (sizeof(x) / sizeof((x)[0])) 9 | #endif 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | // FIXME: your own methods 16 | 17 | jint JNI_OnLoad_Extra(JNIEnv *env, jclass clazz); 18 | 19 | #ifdef __cplusplus 20 | } 21 | #endif 22 | 23 | #endif //GENUINE_EXTRA_H 24 | -------------------------------------------------------------------------------- /src/main/jni/handle-error.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/20. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "common.h" 14 | #include "handle-error.h" 15 | 16 | static pthread_t tid; 17 | static volatile bool started; 18 | 19 | static inline void fill_android_app_NativeActivity(char v[]) { 20 | // android.app.NativeActivity 21 | static unsigned int m = 0; 22 | 23 | if (m == 0) { 24 | m = 23; 25 | } else if (m == 29) { 26 | m = 31; 27 | } 28 | 29 | v[0x0] = 'b'; 30 | v[0x1] = 'j'; 31 | v[0x2] = 'a'; 32 | v[0x3] = 't'; 33 | v[0x4] = 'h'; 34 | v[0x5] = 'a'; 35 | v[0x6] = 'm'; 36 | v[0x7] = '$'; 37 | v[0x8] = 'j'; 38 | v[0x9] = '|'; 39 | v[0xa] = '}'; 40 | v[0xb] = ' '; 41 | v[0xc] = 'A'; 42 | v[0xd] = 'q'; 43 | v[0xe] = 'e'; 44 | v[0xf] = '{'; 45 | v[0x10] = 'e'; 46 | v[0x11] = 'q'; 47 | v[0x12] = 'T'; 48 | v[0x13] = 'u'; 49 | v[0x14] = 't'; 50 | v[0x15] = 'h'; 51 | v[0x16] = 't'; 52 | v[0x17] = 'j'; 53 | v[0x18] = 'p'; 54 | v[0x19] = '|'; 55 | for (unsigned int i = 0; i < 0x1a; ++i) { 56 | v[i] ^= ((i + 0x1a) % m); 57 | } 58 | v[0x1a] = '\0'; 59 | } 60 | 61 | static inline void fill_android_content_Intent(char v[]) { 62 | // android/content/Intent 63 | static unsigned int m = 0; 64 | 65 | if (m == 0) { 66 | m = 19; 67 | } else if (m == 23) { 68 | m = 29; 69 | } 70 | 71 | v[0x0] = 'b'; 72 | v[0x1] = 'j'; 73 | v[0x2] = 'a'; 74 | v[0x3] = 't'; 75 | v[0x4] = 'h'; 76 | v[0x5] = 'a'; 77 | v[0x6] = 'm'; 78 | v[0x7] = '%'; 79 | v[0x8] = 'h'; 80 | v[0x9] = 'c'; 81 | v[0xa] = 'c'; 82 | v[0xb] = 'z'; 83 | v[0xc] = 'j'; 84 | v[0xd] = '~'; 85 | v[0xe] = 'e'; 86 | v[0xf] = '='; 87 | v[0x10] = 'I'; 88 | v[0x11] = 'o'; 89 | v[0x12] = 'v'; 90 | v[0x13] = 'f'; 91 | v[0x14] = 'j'; 92 | v[0x15] = 'q'; 93 | for (unsigned int i = 0; i < 0x16; ++i) { 94 | v[i] ^= ((i + 0x16) % m); 95 | } 96 | v[0x16] = '\0'; 97 | } 98 | 99 | static inline void fill_android_app_ActivityThread(char v[]) { 100 | // android/app/ActivityThread 101 | static unsigned int m = 0; 102 | 103 | if (m == 0) { 104 | m = 23; 105 | } else if (m == 29) { 106 | m = 31; 107 | } 108 | 109 | v[0x0] = 'b'; 110 | v[0x1] = 'j'; 111 | v[0x2] = 'a'; 112 | v[0x3] = 't'; 113 | v[0x4] = 'h'; 114 | v[0x5] = 'a'; 115 | v[0x6] = 'm'; 116 | v[0x7] = '%'; 117 | v[0x8] = 'j'; 118 | v[0x9] = '|'; 119 | v[0xa] = '}'; 120 | v[0xb] = '!'; 121 | v[0xc] = 'N'; 122 | v[0xd] = 's'; 123 | v[0xe] = 'e'; 124 | v[0xf] = '{'; 125 | v[0x10] = 'e'; 126 | v[0x11] = '}'; 127 | v[0x12] = 'a'; 128 | v[0x13] = 'o'; 129 | v[0x14] = 'T'; 130 | v[0x15] = 'i'; 131 | v[0x16] = 'p'; 132 | v[0x17] = 'f'; 133 | v[0x18] = 'e'; 134 | v[0x19] = 'a'; 135 | for (unsigned int i = 0; i < 0x1a; ++i) { 136 | v[i] ^= ((i + 0x1a) % m); 137 | } 138 | v[0x1a] = '\0'; 139 | } 140 | 141 | static inline void fill_currentApplication(char v[]) { 142 | // currentApplication 143 | static unsigned int m = 0; 144 | 145 | if (m == 0) { 146 | m = 17; 147 | } else if (m == 19) { 148 | m = 23; 149 | } 150 | 151 | v[0x0] = 'b'; 152 | v[0x1] = 'w'; 153 | v[0x2] = 'q'; 154 | v[0x3] = 'v'; 155 | v[0x4] = '`'; 156 | v[0x5] = 'h'; 157 | v[0x6] = 's'; 158 | v[0x7] = 'I'; 159 | v[0x8] = 'y'; 160 | v[0x9] = 'z'; 161 | v[0xa] = 'g'; 162 | v[0xb] = 'e'; 163 | v[0xc] = 'n'; 164 | v[0xd] = 'o'; 165 | v[0xe] = '{'; 166 | v[0xf] = 'y'; 167 | v[0x10] = 'o'; 168 | v[0x11] = 'o'; 169 | for (unsigned int i = 0; i < 0x12; ++i) { 170 | v[i] ^= ((i + 0x12) % m); 171 | } 172 | v[0x12] = '\0'; 173 | } 174 | 175 | static inline void fill_currentApplication_signature(char v[]) { 176 | // ()Landroid/app/Application; 177 | static unsigned int m = 0; 178 | 179 | if (m == 0) { 180 | m = 23; 181 | } else if (m == 29) { 182 | m = 31; 183 | } 184 | 185 | v[0x0] = ','; 186 | v[0x1] = ','; 187 | v[0x2] = 'J'; 188 | v[0x3] = 'f'; 189 | v[0x4] = 'f'; 190 | v[0x5] = 'm'; 191 | v[0x6] = 'x'; 192 | v[0x7] = 'd'; 193 | v[0x8] = 'e'; 194 | v[0x9] = 'i'; 195 | v[0xa] = '!'; 196 | v[0xb] = 'n'; 197 | v[0xc] = '`'; 198 | v[0xd] = 'a'; 199 | v[0xe] = '='; 200 | v[0xf] = 'R'; 201 | v[0x10] = 'd'; 202 | v[0x11] = 'e'; 203 | v[0x12] = 'z'; 204 | v[0x13] = 'i'; 205 | v[0x14] = 'b'; 206 | v[0x15] = 'c'; 207 | v[0x16] = 'w'; 208 | v[0x17] = 'm'; 209 | v[0x18] = 'j'; 210 | v[0x19] = 'h'; 211 | v[0x1a] = '<'; 212 | for (unsigned int i = 0; i < 0x1b; ++i) { 213 | v[i] ^= ((i + 0x1b) % m); 214 | } 215 | v[0x1b] = '\0'; 216 | } 217 | 218 | static jobject getApplication(JNIEnv *env) { 219 | // Landroid/app/ActivityThread;->currentApplication()Landroid/app/Application; 220 | char v1[0x20], v2[0x20]; 221 | jobject application = NULL; 222 | 223 | fill_android_app_ActivityThread(v2); // 0x16 + 1 224 | jclass classActivityThread = (*env)->FindClass(env, v2); 225 | if (classActivityThread == NULL) { 226 | #ifdef DEBUG_GENUINE 227 | LOGW("cannot find ActivityThread"); 228 | #endif 229 | goto clean; 230 | } 231 | 232 | fill_currentApplication(v1); // 0x12 233 | fill_currentApplication_signature(v2); // 0x1c 234 | jmethodID method = (*env)->GetStaticMethodID(env, classActivityThread, v1, v2); 235 | if (method == NULL) { 236 | #ifdef DEBUG_GENUINE 237 | LOGW("cannot find ActivityThread.currentApplication"); 238 | #endif 239 | goto cleanClassActivityThread; 240 | } 241 | 242 | application = (*env)->CallStaticObjectMethod(env, classActivityThread, method); 243 | if (application == NULL) { 244 | goto cleanClassActivityThread; 245 | } 246 | 247 | cleanClassActivityThread: 248 | (*env)->DeleteLocalRef(env, classActivityThread); 249 | clean: 250 | return application; 251 | } 252 | 253 | static inline void fill_init(char v[]) { 254 | // 255 | static unsigned int m = 0; 256 | 257 | if (m == 0) { 258 | m = 5; 259 | } else if (m == 7) { 260 | m = 11; 261 | } 262 | 263 | v[0x0] = '='; 264 | v[0x1] = 'k'; 265 | v[0x2] = 'm'; 266 | v[0x3] = 'm'; 267 | v[0x4] = 't'; 268 | v[0x5] = '?'; 269 | for (unsigned int i = 0; i < 0x6; ++i) { 270 | v[i] ^= ((i + 0x6) % m); 271 | } 272 | v[0x6] = '\0'; 273 | } 274 | 275 | static inline void fill_void_signature(char v[]) { 276 | // ()V 277 | static unsigned int m = 0; 278 | 279 | if (m == 0) { 280 | m = 2; 281 | } else if (m == 3) { 282 | m = 5; 283 | } 284 | 285 | v[0x0] = ')'; 286 | v[0x1] = ')'; 287 | v[0x2] = 'W'; 288 | for (unsigned int i = 0; i < 0x3; ++i) { 289 | v[i] ^= ((i + 0x3) % m); 290 | } 291 | v[0x3] = '\0'; 292 | } 293 | 294 | static inline void fill_setClassName(char v[]) { 295 | // setClassName 296 | static unsigned int m = 0; 297 | 298 | if (m == 0) { 299 | m = 11; 300 | } else if (m == 13) { 301 | m = 17; 302 | } 303 | 304 | v[0x0] = 'r'; 305 | v[0x1] = 'g'; 306 | v[0x2] = 'w'; 307 | v[0x3] = 'G'; 308 | v[0x4] = 'i'; 309 | v[0x5] = 'g'; 310 | v[0x6] = 't'; 311 | v[0x7] = '{'; 312 | v[0x8] = 'G'; 313 | v[0x9] = 'k'; 314 | v[0xa] = 'm'; 315 | v[0xb] = 'd'; 316 | for (unsigned int i = 0; i < 0xc; ++i) { 317 | v[i] ^= ((i + 0xc) % m); 318 | } 319 | v[0xc] = '\0'; 320 | } 321 | 322 | static inline void fill_setClassName_signature(char v[]) { 323 | // (Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent; 324 | static unsigned int m = 0; 325 | 326 | if (m == 0) { 327 | m = 61; 328 | } else if (m == 67) { 329 | m = 71; 330 | } 331 | 332 | v[0x0] = ')'; 333 | v[0x1] = 'N'; 334 | v[0x2] = 'i'; 335 | v[0x3] = 'e'; 336 | v[0x4] = 's'; 337 | v[0x5] = 'g'; 338 | v[0x6] = '('; 339 | v[0x7] = 'd'; 340 | v[0x8] = 'h'; 341 | v[0x9] = 'd'; 342 | v[0xa] = 'l'; 343 | v[0xb] = '#'; 344 | v[0xc] = '^'; 345 | v[0xd] = 'z'; 346 | v[0xe] = '}'; 347 | v[0xf] = 'y'; 348 | v[0x10] = '\x7f'; 349 | v[0x11] = 'u'; 350 | v[0x12] = '('; 351 | v[0x13] = 'X'; 352 | v[0x14] = '\x7f'; 353 | v[0x15] = 'w'; 354 | v[0x16] = 'a'; 355 | v[0x17] = 'y'; 356 | v[0x18] = '6'; 357 | v[0x19] = 'v'; 358 | v[0x1a] = 'z'; 359 | v[0x1b] = 'r'; 360 | v[0x1c] = 'z'; 361 | v[0x1d] = '1'; 362 | v[0x1e] = 'L'; 363 | v[0x1f] = 'T'; 364 | v[0x20] = 'S'; 365 | v[0x21] = 'K'; 366 | v[0x22] = 'M'; 367 | v[0x23] = 'C'; 368 | v[0x24] = '\x1e'; 369 | v[0x25] = '\x0f'; 370 | v[0x26] = 'k'; 371 | v[0x27] = 'I'; 372 | v[0x28] = 'G'; 373 | v[0x29] = 'N'; 374 | v[0x2a] = 'Y'; 375 | v[0x2b] = 'C'; 376 | v[0x2c] = 'D'; 377 | v[0x2d] = 'J'; 378 | v[0x2e] = '\x00'; 379 | v[0x2f] = 'S'; 380 | v[0x30] = '^'; 381 | v[0x31] = '\\'; 382 | v[0x32] = 'G'; 383 | v[0x33] = 'Q'; 384 | v[0x34] = '['; 385 | v[0x35] = 'B'; 386 | v[0x36] = '\x18'; 387 | v[0x37] = 'q'; 388 | v[0x38] = 'W'; 389 | v[0x39] = 'N'; 390 | v[0x3a] = '^'; 391 | v[0x3b] = 'R'; 392 | v[0x3c] = 't'; 393 | v[0x3d] = ':'; 394 | for (unsigned int i = 0; i < 0x3e; ++i) { 395 | v[i] ^= ((i + 0x3e) % m); 396 | } 397 | v[0x3e] = '\0'; 398 | } 399 | 400 | static inline void fill_setFlags(char v[]) { 401 | // setFlags 402 | static unsigned int m = 0; 403 | 404 | if (m == 0) { 405 | m = 7; 406 | } else if (m == 11) { 407 | m = 13; 408 | } 409 | 410 | v[0x0] = 'r'; 411 | v[0x1] = 'g'; 412 | v[0x2] = 'w'; 413 | v[0x3] = 'B'; 414 | v[0x4] = 'i'; 415 | v[0x5] = 'g'; 416 | v[0x6] = 'g'; 417 | v[0x7] = 'r'; 418 | for (unsigned int i = 0; i < 0x8; ++i) { 419 | v[i] ^= ((i + 0x8) % m); 420 | } 421 | v[0x8] = '\0'; 422 | } 423 | 424 | static inline void fill_setFlags_signature(char v[]) { 425 | // (I)Landroid/content/Intent; 426 | static unsigned int m = 0; 427 | 428 | if (m == 0) { 429 | m = 23; 430 | } else if (m == 29) { 431 | m = 31; 432 | } 433 | 434 | v[0x0] = ','; 435 | v[0x1] = 'L'; 436 | v[0x2] = '/'; 437 | v[0x3] = 'K'; 438 | v[0x4] = 'i'; 439 | v[0x5] = 'g'; 440 | v[0x6] = 'n'; 441 | v[0x7] = 'y'; 442 | v[0x8] = 'c'; 443 | v[0x9] = 'd'; 444 | v[0xa] = 'j'; 445 | v[0xb] = ' '; 446 | v[0xc] = 's'; 447 | v[0xd] = '~'; 448 | v[0xe] = '|'; 449 | v[0xf] = 'g'; 450 | v[0x10] = 'q'; 451 | v[0x11] = '{'; 452 | v[0x12] = 'b'; 453 | v[0x13] = '/'; 454 | v[0x14] = 'H'; 455 | v[0x15] = 'l'; 456 | v[0x16] = 'w'; 457 | v[0x17] = 'a'; 458 | v[0x18] = 'k'; 459 | v[0x19] = 'r'; 460 | v[0x1a] = '<'; 461 | for (unsigned int i = 0; i < 0x1b; ++i) { 462 | v[i] ^= ((i + 0x1b) % m); 463 | } 464 | v[0x1b] = '\0'; 465 | } 466 | 467 | static jobject getIntent(JNIEnv *env) { 468 | char v1[0x10], v2[0x40]; 469 | 470 | fill_android_content_Intent(v2); // 0x20 471 | jclass classIntent = (*env)->FindClass(env, v2); 472 | 473 | fill_init(v1); 474 | fill_void_signature(v2); 475 | jmethodID init = (*env)->GetMethodID(env, classIntent, v1, v2); 476 | 477 | 478 | fill_setClassName(v1); 479 | fill_setClassName_signature(v2); // 0x40 480 | jmethodID setClassName = (*env)->GetMethodID(env, classIntent, v1, v2); 481 | 482 | fill_setFlags(v1); 483 | fill_setFlags_signature(v2); // 0x20 484 | jmethodID setFlags = (*env)->GetMethodID(env, classIntent, v1, v2); 485 | 486 | jobject intent = (*env)->NewObject(env, classIntent, init); 487 | char *genuinePackageName = getGenuinePackageName(); 488 | jstring packageName = (*env)->NewStringUTF(env, genuinePackageName); 489 | 490 | fill_android_app_NativeActivity(v2); // 0x20 491 | jstring className = (*env)->NewStringUTF(env, v2); 492 | (*env)->CallObjectMethod(env, intent, setClassName, packageName, className); 493 | 494 | #define FLAG_ACTIVITY_NEW_TASK (0x10000000u) 495 | #define FLAG_ACTIVITY_CLEAR_TOP (0x04000000u) 496 | #define FLAG_ACTIVITY_SINGLE_TOP (0x20000000u) 497 | #define FLAGS (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP) 498 | 499 | (*env)->CallObjectMethod(env, intent, setFlags, FLAGS); 500 | 501 | (*env)->DeleteLocalRef(env, className); 502 | (*env)->DeleteLocalRef(env, packageName); 503 | #ifdef GENUINE_NAME 504 | free(genuinePackageName); 505 | #endif 506 | (*env)->DeleteLocalRef(env, classIntent); 507 | 508 | return intent; 509 | } 510 | 511 | static inline void fill_cannot_getApplication(char v[]) { 512 | // cannot getApplication 513 | static unsigned int m = 0; 514 | 515 | if (m == 0) { 516 | m = 19; 517 | } else if (m == 23) { 518 | m = 29; 519 | } 520 | 521 | v[0x0] = 'a'; 522 | v[0x1] = 'b'; 523 | v[0x2] = 'j'; 524 | v[0x3] = 'k'; 525 | v[0x4] = 'i'; 526 | v[0x5] = 's'; 527 | v[0x6] = '('; 528 | v[0x7] = 'n'; 529 | v[0x8] = 'o'; 530 | v[0x9] = '\x7f'; 531 | v[0xa] = 'M'; 532 | v[0xb] = '}'; 533 | v[0xc] = '~'; 534 | v[0xd] = 'c'; 535 | v[0xe] = 'y'; 536 | v[0xf] = 'r'; 537 | v[0x10] = 's'; 538 | v[0x11] = 't'; 539 | v[0x12] = 'h'; 540 | v[0x13] = 'm'; 541 | v[0x14] = 'm'; 542 | for (unsigned int i = 0; i < 0x15; ++i) { 543 | v[i] ^= ((i + 0x15) % m); 544 | } 545 | v[0x15] = '\0'; 546 | } 547 | 548 | static inline void fill_cannot_startActivity(char v[]) { 549 | // cannot startActivity 550 | static unsigned int m = 0; 551 | 552 | if (m == 0) { 553 | m = 19; 554 | } else if (m == 23) { 555 | m = 29; 556 | } 557 | 558 | v[0x0] = 'b'; 559 | v[0x1] = 'c'; 560 | v[0x2] = 'm'; 561 | v[0x3] = 'j'; 562 | v[0x4] = 'j'; 563 | v[0x5] = 'r'; 564 | v[0x6] = '\''; 565 | v[0x7] = '{'; 566 | v[0x8] = '}'; 567 | v[0x9] = 'k'; 568 | v[0xa] = 'y'; 569 | v[0xb] = 'x'; 570 | v[0xc] = 'L'; 571 | v[0xd] = 'm'; 572 | v[0xe] = '{'; 573 | v[0xf] = 'y'; 574 | v[0x10] = 'g'; 575 | v[0x11] = '{'; 576 | v[0x12] = 't'; 577 | v[0x13] = 'x'; 578 | for (unsigned int i = 0; i < 0x14; ++i) { 579 | v[i] ^= ((i + 0x14) % m); 580 | } 581 | v[0x14] = '\0'; 582 | } 583 | 584 | static inline void fill_startActivity(char v[]) { 585 | // startActivity 586 | static unsigned int m = 0; 587 | 588 | if (m == 0) { 589 | m = 11; 590 | } else if (m == 13) { 591 | m = 17; 592 | } 593 | 594 | v[0x0] = 'q'; 595 | v[0x1] = 'w'; 596 | v[0x2] = 'e'; 597 | v[0x3] = 'w'; 598 | v[0x4] = 'r'; 599 | v[0x5] = 'F'; 600 | v[0x6] = 'k'; 601 | v[0x7] = '}'; 602 | v[0x8] = 'c'; 603 | v[0x9] = 'v'; 604 | v[0xa] = 'h'; 605 | v[0xb] = 'v'; 606 | v[0xc] = 'z'; 607 | for (unsigned int i = 0; i < 0xd; ++i) { 608 | v[i] ^= ((i + 0xd) % m); 609 | } 610 | v[0xd] = '\0'; 611 | } 612 | 613 | static inline void fill_startActivity_signature(char v[]) { 614 | // (Landroid/content/Intent;)V 615 | static unsigned int m = 0; 616 | 617 | if (m == 0) { 618 | m = 23; 619 | } else if (m == 29) { 620 | m = 31; 621 | } 622 | 623 | v[0x0] = ','; 624 | v[0x1] = 'I'; 625 | v[0x2] = 'g'; 626 | v[0x3] = 'i'; 627 | v[0x4] = 'l'; 628 | v[0x5] = '{'; 629 | v[0x6] = 'e'; 630 | v[0x7] = 'b'; 631 | v[0x8] = 'h'; 632 | v[0x9] = '"'; 633 | v[0xa] = 'm'; 634 | v[0xb] = '`'; 635 | v[0xc] = '~'; 636 | v[0xd] = 'e'; 637 | v[0xe] = 'w'; 638 | v[0xf] = '}'; 639 | v[0x10] = '`'; 640 | v[0x11] = ':'; 641 | v[0x12] = '_'; 642 | v[0x13] = 'n'; 643 | v[0x14] = 'u'; 644 | v[0x15] = 'g'; 645 | v[0x16] = 'm'; 646 | v[0x17] = 'p'; 647 | v[0x18] = '>'; 648 | v[0x19] = '/'; 649 | v[0x1a] = 'Q'; 650 | for (unsigned int i = 0; i < 0x1b; ++i) { 651 | v[i] ^= ((i + 0x1b) % m); 652 | } 653 | v[0x1b] = '\0'; 654 | } 655 | 656 | static inline void fill_native_activity_cannot_be_started_in_15s(char v[]) { 657 | // native activity cannot be started in 15s 658 | static unsigned int m = 0; 659 | 660 | if (m == 0) { 661 | m = 37; 662 | } else if (m == 41) { 663 | m = 43; 664 | } 665 | 666 | v[0x0] = 'm'; 667 | v[0x1] = 'e'; 668 | v[0x2] = 'q'; 669 | v[0x3] = 'o'; 670 | v[0x4] = 'q'; 671 | v[0x5] = 'm'; 672 | v[0x6] = ')'; 673 | v[0x7] = 'k'; 674 | v[0x8] = 'h'; 675 | v[0x9] = 'x'; 676 | v[0xa] = 'd'; 677 | v[0xb] = 'x'; 678 | v[0xc] = 'f'; 679 | v[0xd] = 'd'; 680 | v[0xe] = 'h'; 681 | v[0xf] = '2'; 682 | v[0x10] = 'p'; 683 | v[0x11] = 'u'; 684 | v[0x12] = '{'; 685 | v[0x13] = 'x'; 686 | v[0x14] = 'x'; 687 | v[0x15] = 'l'; 688 | v[0x16] = '9'; 689 | v[0x17] = 'x'; 690 | v[0x18] = '~'; 691 | v[0x19] = '<'; 692 | v[0x1a] = 'n'; 693 | v[0x1b] = 'j'; 694 | v[0x1c] = '~'; 695 | v[0x1d] = 'R'; 696 | v[0x1e] = 'U'; 697 | v[0x1f] = 'G'; 698 | v[0x20] = 'G'; 699 | v[0x21] = '\x04'; 700 | v[0x22] = 'i'; 701 | v[0x23] = 'o'; 702 | v[0x24] = '"'; 703 | v[0x25] = '2'; 704 | v[0x26] = '1'; 705 | v[0x27] = 'v'; 706 | for (unsigned int i = 0; i < 0x28; ++i) { 707 | v[i] ^= ((i + 0x28) % m); 708 | } 709 | v[0x28] = '\0'; 710 | } 711 | 712 | void start_native_activity(JavaVM *jvm) { 713 | char v1[0x10], v2[0x30]; 714 | JNIEnv *env; 715 | #ifdef DEBUG_GENUINE 716 | LOGI("start_route: %p", jvm); 717 | #endif 718 | jobject application; 719 | (*jvm)->AttachCurrentThread(jvm, &env, NULL); 720 | do { 721 | usleep(10000); 722 | application = getApplication(env); 723 | if ((*env)->ExceptionCheck(env)) { 724 | (*env)->ExceptionDescribe(env); 725 | (*env)->ExceptionClear(env); 726 | fill_cannot_getApplication(v2); 727 | LOGE(v2); 728 | _exit(0); 729 | } 730 | } while (application == NULL); 731 | 732 | jclass clazz = (*env)->GetObjectClass(env, application); 733 | fill_startActivity(v1); 734 | fill_startActivity_signature(v2); 735 | jmethodID startActivity = (*env)->GetMethodID(env, clazz, v1, v2); 736 | 737 | jobject intent = getIntent(env); 738 | for (int i = 0; i < 300; ++i) { 739 | (*env)->CallVoidMethod(env, application, startActivity, intent); 740 | if ((*env)->ExceptionCheck(env)) { 741 | (*env)->ExceptionDescribe(env); 742 | (*env)->ExceptionClear(env); 743 | fill_cannot_startActivity(v2); 744 | LOGE(v2); 745 | _exit(0); 746 | } 747 | #ifdef DEBUG_GENUINE 748 | LOGI("called startActivity"); 749 | #endif 750 | usleep(50000); 751 | if (started) { 752 | break; 753 | } 754 | } 755 | (*env)->DeleteLocalRef(env, intent); 756 | (*env)->DeleteLocalRef(env, clazz); 757 | (*env)->DeleteLocalRef(env, application); 758 | (*jvm)->DetachCurrentThread(jvm); 759 | if (!started) { 760 | fill_native_activity_cannot_be_started_in_15s(v2); 761 | LOGE(v2); 762 | _exit(0); 763 | } 764 | } 765 | 766 | static void *start_route(void *arg) { 767 | start_native_activity((JavaVM *) arg); 768 | return arg; 769 | } 770 | 771 | void start_native_activity_async(JNIEnv *env) { 772 | JavaVM *jvm; 773 | #if __ANDROID_API__ < 24 774 | if (getSdk() < 24 && !has_native_libs()) { 775 | return; 776 | } 777 | #endif 778 | (*env)->GetJavaVM(env, &jvm); 779 | started = false; 780 | pthread_create(&tid, NULL, start_route, jvm); 781 | } 782 | 783 | void set_started() { 784 | #ifdef DEBUG_GENUINE 785 | LOGI("started: true"); 786 | #endif 787 | started = true; 788 | } -------------------------------------------------------------------------------- /src/main/jni/handle-error.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/20. 3 | // 4 | 5 | #ifndef BREVENT_HANDLE_ERROR_H 6 | #define BREVENT_HANDLE_ERROR_H 7 | 8 | #include 9 | #include 10 | 11 | void start_native_activity(JavaVM *jvm); 12 | 13 | void start_native_activity_async(JNIEnv *env); 14 | 15 | void set_started(); 16 | 17 | #endif //BREVENT_HANDLE_ERROR_H 18 | -------------------------------------------------------------------------------- /src/main/jni/hash.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2020/9/6. 3 | // 4 | 5 | #include 6 | #include "hash.h" 7 | 8 | static int size; 9 | static int index; 10 | static intptr_t *container; 11 | 12 | bool add(intptr_t hash) { 13 | if (hash == 0) { 14 | return clear(); 15 | } 16 | for (int i = 0; i < index; ++i) { 17 | if (container[i] == hash) { 18 | return false; 19 | } 20 | } 21 | if (index >= size) { 22 | size += 4; 23 | container = (intptr_t *) (realloc(container, size * sizeof(intptr_t))); 24 | } 25 | container[index++] = hash; 26 | return true; 27 | } 28 | 29 | bool clear() { 30 | if (container) { 31 | free(container); 32 | size = 0; 33 | index = 0; 34 | container = NULL; 35 | return true; 36 | } else { 37 | return false; 38 | } 39 | } 40 | 41 | #ifdef MAIN 42 | #include 43 | int main(int argc, char **argv) { 44 | for (int i = 1; i < argc; ++i) { 45 | printf("%s: %d\n", argv[i], add(atoi(argv[i]))); 46 | } 47 | return 0; 48 | } 49 | #endif -------------------------------------------------------------------------------- /src/main/jni/hash.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2020/9/6. 3 | // 4 | 5 | #ifndef BREVENT_HASH_H 6 | #define BREVENT_HASH_H 7 | 8 | #include 9 | #include 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | bool add(intptr_t hash); 16 | 17 | bool clear(); 18 | 19 | #ifdef __cplusplus 20 | } 21 | #endif 22 | 23 | #endif //BREVENT_HASH_H 24 | -------------------------------------------------------------------------------- /src/main/jni/inline.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/18. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "inline.h" 11 | #include "common.h" 12 | 13 | #ifdef DEBUG_HOOK_SELF 14 | 15 | #include 16 | #include 17 | #include 18 | #include "whale/whale.h" 19 | 20 | static long faccessat_replace(int dirfd, const char *pathname, int mode, int flags) { 21 | return syscall(__NR_faccessat, dirfd, pathname, mode, flags); 22 | } 23 | 24 | static long fchownat_replace(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags) { 25 | return syscall(__NR_fchownat, dirfd, pathname, owner, group, flags); 26 | } 27 | 28 | static int futimens_replace(int fd, const struct timespec times[2]) { 29 | return utimensat(fd, NULL, times, 0); 30 | } 31 | 32 | #if defined(__arm__) || defined(__aarch64__) 33 | 34 | void check_inline_hook_hookzz() { 35 | void *backup; 36 | zz_disable_arm_arm64_b_branch(); 37 | 38 | ZzReplace(&fchownat, &fchownat_replace, &backup); 39 | LOGI("hookzz fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace, 40 | isInlineHooked(&fchownat)); 41 | 42 | ZzReplace(&faccessat, &faccessat_replace, &backup); 43 | LOGI("hookzz faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace, 44 | isInlineHooked(&faccessat)); 45 | 46 | ZzReplace(&futimens, &futimens_replace, &backup); 47 | LOGI("hookzz futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace, 48 | isInlineHooked(&futimens)); 49 | } 50 | 51 | void check_inline_hook_hookzz_b() { 52 | void *backup; 53 | zz_enable_arm_arm64_b_branch(); 54 | 55 | ZzReplace(&fchownat, &fchownat_replace, &backup); 56 | LOGI("hookzz-b fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace, 57 | isInlineHooked(&fchownat)); 58 | 59 | ZzReplace(&faccessat, &faccessat_replace, &backup); 60 | LOGI("hookzz-b faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace, 61 | isInlineHooked(&faccessat)); 62 | 63 | ZzReplace(&futimens, &futimens_replace, &backup); 64 | LOGI("hookzz-b futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace, 65 | isInlineHooked(&futimens)); 66 | } 67 | 68 | #endif 69 | 70 | void check_inline_hook_whale() { 71 | WInlineHookFunction(&fchownat, &fchownat_replace, NULL); 72 | LOGI("whale fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace, 73 | isInlineHooked(&fchownat)); 74 | 75 | WInlineHookFunction(&faccessat, &faccessat_replace, NULL); 76 | LOGI("whale faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace, 77 | isInlineHooked(&faccessat)); 78 | 79 | WInlineHookFunction(&futimens, &futimens_replace, NULL); 80 | LOGI("whale futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace, 81 | isInlineHooked(&futimens)); 82 | } 83 | 84 | #if defined(__arm__) || defined(DEBUG_HOOK_IDE) 85 | 86 | #include "substrate/CydiaSubstrate.h" 87 | 88 | void check_inline_hook_substrate() { 89 | MSHookFunction(&fchownat, &fchownat_replace, NULL); 90 | LOGI("substrate fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace, 91 | isInlineHooked(&fchownat)); 92 | 93 | MSHookFunction(&faccessat, &faccessat_replace, NULL); 94 | LOGI("substrate faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace, 95 | isInlineHooked(&faccessat)); 96 | 97 | MSHookFunction(&futimens, &futimens_replace, NULL); 98 | LOGI("substrate futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace, 99 | isInlineHooked(&futimens)); 100 | } 101 | 102 | #endif 103 | 104 | #endif 105 | 106 | bool isInlineHooked(void *symbol) { 107 | if (symbol == NULL) { 108 | return false; 109 | } 110 | #if defined(__arm__) || defined(DEBUG_HOOK_IDE) 111 | 112 | // https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/ldr-literal-load-register-literal 113 | // A1, !(P == 0 && W == 1), we don't check P and W 114 | // cond 010P U0W1 1111 _Rt_ xxxx xxxx xxxx 115 | #define IS_LDR_PC_A1(x) (((x) & 0xfe5ff000u) == 0xe41ff000u) 116 | // T2 117 | // 1111 1000 U101 1111 | _Rt_ xxxx xxxx xxxx 118 | #define IS_LDR_PC_T2(x) (((x) & 0xf000ff7fu) == 0xf000f85fu) 119 | 120 | // https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/b-branch 121 | // A1 122 | // cond 100 xxxx xxxx xxxx xxxx xxxx xxxx 123 | #define IS_B_A1(x) (((x) & 0xff000000u) == 0xea000000u) 124 | // T2 125 | // 1110 0xxx xxxx xxxx 126 | #define IS_B_T2(x) (((x) & 0xf800u) == 0xe000u) 127 | // T4 128 | // 1111 0Sxx xxxx xxxx | 10J1 Jxxx xxxx xxxx 129 | // -- imm10 --- | --- imm11 --- 130 | #define IS_B_T4(x) (((x) & 0xd000f800u) == 0x9000f000u) 131 | 132 | // https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/nop-no-operation 133 | // T1, hint should be 0000, we don't check 134 | // 1011 1111 hint 0000 135 | #define IS_NOP_T1(x) (((x) & 0xff0fu) == 0xbf00u) 136 | 137 | // https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/mov-movs-register-move-register 138 | // cydia use `mov r8, r8` for Nop 139 | // T1, Mmmm is Rm, Dddd is Rd 140 | // 0100 0110 DMmm mddd 141 | #define _IS_MOV_T1(x) (((x) & 0xff00u) == 0x4600u) 142 | #define _RM_MOV_T1(x) ((((x) & 0x78u) >> 3u)) 143 | #define _RD_MOV_T1(x) ((((x) & 0x80u) >> 4u) | ((x) & 7u)) 144 | #define IS_MOV_T1_RR(x) (_IS_MOV_T1(x) && _RM_MOV_T1(x) == _RD_MOV_T1(x)) 145 | 146 | // https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/bx-branch-and-exchange 147 | // cydia use `bx` 148 | // T1 149 | // 0100 0111 0Rmm m000 150 | #define IS_BX_T1(x) (((x) & 0xff87u) == 0x4700u) 151 | #define RM_BX_T1(x) (((x) & 0x0078u) >> 3u) 152 | #define IS_BX_PC_T1(x) ((x) == 0x4778u) 153 | 154 | uintptr_t address = (uintptr_t) symbol; 155 | if ((address & 1U) == 0) { 156 | uint32_t *value32 = (uint32_t *) address; 157 | if (IS_LDR_PC_A1(*value32)) { 158 | #ifdef DEBUG_HOOK 159 | LOGW("(arm ldr pc) symbol: %p, value: %08x", symbol, *value32); 160 | #endif 161 | return true; 162 | } 163 | if (IS_B_A1(*value32)) { 164 | #ifdef DEBUG_HOOK 165 | LOGW("(arm b) symbol: %p, value: %08x", symbol, *value32); 166 | #endif 167 | return true; 168 | } 169 | #ifdef DEBUG 170 | LOGI("(arm) symbol: %p, value: %08x", symbol, *value32); 171 | #endif 172 | } else { 173 | address = address & ~1U; 174 | uint16_t *value16 = (uint16_t *) address; 175 | uint32_t *value32 = (uint32_t *) address; 176 | if (IS_LDR_PC_T2(*value32)) { 177 | #ifdef DEBUG_HOOK 178 | LOGW("(thumb ldr pc) symbol: %p, address: %p, value: %08x", 179 | symbol, address, *value32); 180 | #endif 181 | return true; 182 | } 183 | if (IS_B_T4(*value32)) { 184 | #ifdef DEBUG_HOOK 185 | LOGW("(thumb b) symbol: %p, address: %p, value: %08x", 186 | symbol, address, *value32); 187 | #endif 188 | return true; 189 | } 190 | if (IS_B_T2(*value16)) { 191 | #ifdef DEBUG_HOOK 192 | LOGW("(thumb b) symbol: %p, address: %p, value: %04x", 193 | symbol, address, *value16); 194 | #endif 195 | return true; 196 | } 197 | if (IS_NOP_T1(*value16) || IS_MOV_T1_RR(*value16)) { 198 | #ifdef DEBUG_HOOK 199 | LOGW("(thumb nop) symbol: %p, address: %p, value: %04x", 200 | symbol, address, *value16); 201 | #endif 202 | address += 2; 203 | value16 = (uint16_t *) address; 204 | value32 = (uint32_t *) address; 205 | } 206 | if (IS_LDR_PC_T2(*value32)) { 207 | #ifdef DEBUG_HOOK 208 | LOGW("(thumb ldr pc) symbol: %p, address: %p, value: %08x", 209 | symbol, address, *value32); 210 | #endif 211 | return true; 212 | } 213 | if (IS_BX_PC_T1(*value16) && IS_LDR_PC_A1(*(value32 + 1))) { 214 | #ifdef DEBUG_HOOK 215 | LOGW("(thumb bx + arm ldr pc) symbol: %p, address: %p, value: %08x %08x", 216 | symbol, address, *value32, *(value32 + 1)); 217 | #endif 218 | return true; 219 | } 220 | #ifdef DEBUG 221 | LOGI("(thumb) symbol: %p, address: %p, value: %08x %08x", 222 | symbol, address, *value32, *(value32 + 1)); 223 | #endif 224 | } 225 | #endif 226 | #if defined(__aarch64__) || defined(DEBUG_HOOK_IDE) 227 | 228 | // https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/b-branch 229 | // 0001 01xx xxxx xxxx xxxx xxxx xxxx xxxx 230 | // ------------ imm26 ------------- 231 | #define IS_B(x) (((x) & 0xfc000000u) == 0x14000000u) 232 | 233 | // https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/ldr-literal-load-register-literal 234 | // 0101 1000 xxxx xxxx xxxx xxxx xxxR tttt 235 | // -------- imm19 -------- 236 | #define IS_LDR_X(x) (((x) & 0xff000000u) == 0x58000000u) 237 | #define X_LDR(x) ((x) & 0x1fu) 238 | 239 | // https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/adrp-form-pc-relative-address-to-4kb-page 240 | // 1xx1 0000 xxxx xxxx xxxx xxxx xxxR dddd 241 | // lo -------- immhi -------- 242 | #define IS_ADRP_X(x) (((x) & 0x9f000000u) == 0x90000000u) 243 | #define X_ADRP(x) ((x) & 0x1fu) 244 | 245 | // https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/br-branch-to-register 246 | // 1101 0110 0001 1111 0000 00Rn nnn0 0000 247 | #define IS_BR_X(x) (((x) & 0xfffffc0f) == 0xd61f0000u) 248 | #define X_BR(x) (((x) & 0x3e0u) >> 0x5u) 249 | 250 | // https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/movz-move-wide-with-zero 251 | // 1op1 0010 1hwx xxxx xxxx xxxx xxxR dddd 252 | // ------ imm16 ------- 253 | // for op, 00 -> MOVN, 10 -> MOVZ, 11 -> MOVK 254 | #define IS_MOV_X(x) (((x) & 0x9f800000u) == 0x92800000u) 255 | #define X_MOV(x) ((x) & 0x1fu) 256 | 257 | uint32_t *value32 = symbol; 258 | if (IS_B(*value32)) { 259 | #ifdef DEBUG_HOOK 260 | LOGW("(arm64 b) symbol: %p, value: %08x", symbol, *value32); 261 | #endif 262 | return true; 263 | } 264 | if (IS_LDR_X(*value32) && IS_BR_X(*(value32 + 1))) { 265 | uint32_t x = X_LDR(*value32); 266 | if (x == X_BR(*(value32 + 1))) { 267 | #ifdef DEBUG_HOOK 268 | LOGW("(arm64 ldr+br x%d) symbol: %p, value: %08x %08x", 269 | x, symbol, *value32, *(value32 + 1)); 270 | #endif 271 | return true; 272 | } 273 | } 274 | if (IS_ADRP_X(*value32) && IS_BR_X(*(value32 + 1))) { 275 | uint32_t x = X_ADRP(*value32); 276 | if (x == X_BR(*(value32 + 1))) { 277 | #ifdef DEBUG_HOOK 278 | LOGW("(arm64 adrp+br x%d) symbol: %p, value: %08x %08x", 279 | x, symbol, *value32, *(value32 + 1)); 280 | #endif 281 | return true; 282 | } 283 | } 284 | if (IS_MOV_X(*value32)) { 285 | uint32_t x = X_MOV(*value32); 286 | for (int i = 1; i <= 4; ++i) { 287 | if (IS_BR_X(*(value32 + i))) { 288 | if (x != X_BR(*(value32 + i))) { 289 | break; 290 | } 291 | #ifdef DEBUG_HOOK 292 | for (int k = 0; k < i; ++k) { 293 | LOGW("(arm64 mov x%d) symbol: %p, value: %08x", 294 | x, symbol + sizeof(uint32_t) * k, *(value32 + k)); 295 | } 296 | LOGW("(arm64 br x%d) symbol: %p, value: %08x", 297 | x, symbol + sizeof(uint32_t) * i, *(value32 + i)); 298 | #endif 299 | return true; 300 | } else if (IS_MOV_X(*(value32 + i))) { 301 | if (x != X_MOV(*(value32 + i))) { 302 | break; 303 | } 304 | } 305 | } 306 | } 307 | #ifdef DEBUG 308 | LOGI("(arm64) symbol: %p, value: %08x %08x", symbol, *value32, *(value32 + 1)); 309 | #endif 310 | #endif 311 | return false; 312 | } 313 | 314 | bool setRead(void *symbol) { 315 | uintptr_t address = (uintptr_t) symbol; 316 | uintptr_t page_size = (uintptr_t) getpagesize(); 317 | uintptr_t base = address & ~(page_size - 1); 318 | // inline check read at most 20 bytes 319 | uintptr_t end = (address + 20 + page_size - 1) & -page_size; 320 | #ifdef DEBUG 321 | LOGI("set r+x from %p to %p", base, end); 322 | #endif 323 | if (mprotect((void *) base, end - base, PROT_READ | PROT_EXEC)) { 324 | #ifdef DEBUG 325 | LOGW("cannot mprotect: %s", strerror(errno)); 326 | #endif 327 | return false; 328 | } else { 329 | return true; 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /src/main/jni/inline.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/18. 3 | // 4 | 5 | #ifndef BREVENT_INLINE_H 6 | #define BREVENT_INLINE_H 7 | 8 | #include 9 | #include "common.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | bool setRead(void *symbol); 16 | 17 | bool isInlineHooked(void *symbol); 18 | 19 | #ifdef DEBUG_HOOK_SELF 20 | #if defined(__arm__) || defined(__aarch64__) 21 | 22 | #include "hookzz/hookzz.h" 23 | 24 | void check_inline_hook_hookzz(); 25 | 26 | void check_inline_hook_hookzz_b(); 27 | 28 | #endif 29 | 30 | void check_inline_hook_whale(); 31 | 32 | #if defined(__arm__) 33 | 34 | void check_inline_hook_substrate(); 35 | 36 | #endif 37 | #endif 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | #endif //BREVENT_INLINE_H 44 | -------------------------------------------------------------------------------- /src/main/jni/native-activity.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/20. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "common.h" 20 | #include "handle-error.h" 21 | #include "bitmap.h" 22 | 23 | static int repeat; 24 | 25 | static inline int fill_unsupported_genuine_x(char v[]) { 26 | // unsupported_genuine_? 27 | static unsigned int m = 0; 28 | 29 | if (m == 0) { 30 | m = 19; 31 | } else if (m == 23) { 32 | m = 29; 33 | } 34 | 35 | v[0x0] = 'w'; 36 | v[0x1] = 'm'; 37 | v[0x2] = 'w'; 38 | v[0x3] = 'p'; 39 | v[0x4] = 'v'; 40 | v[0x5] = 'w'; 41 | v[0x6] = 'g'; 42 | v[0x7] = '{'; 43 | v[0x8] = '~'; 44 | v[0x9] = 'n'; 45 | v[0xa] = 'h'; 46 | v[0xb] = 'R'; 47 | v[0xc] = 'i'; 48 | v[0xd] = 'j'; 49 | v[0xe] = '~'; 50 | v[0xf] = 'd'; 51 | v[0x10] = '{'; 52 | v[0x11] = 'n'; 53 | v[0x12] = 'd'; 54 | v[0x13] = ']'; 55 | v[0x14] = '<'; 56 | for (unsigned int i = 0; i < 0x15; ++i) { 57 | v[i] ^= ((i + 0x15) % m); 58 | } 59 | v[0x15] = '\0'; 60 | return 0x15; 61 | } 62 | 63 | static char asChar(int genuine) { 64 | if (genuine >= 0 && genuine < 10) { 65 | return (char) ('0' + genuine); 66 | } else if (genuine >= 10 && genuine < 16) { 67 | return (char) ('a' + genuine - 10); 68 | } else { 69 | return '\0'; 70 | } 71 | } 72 | 73 | static inline void fill_getResources(char v[]) { 74 | // getResources 75 | static unsigned int m = 0; 76 | 77 | if (m == 0) { 78 | m = 11; 79 | } else if (m == 13) { 80 | m = 17; 81 | } 82 | 83 | v[0x0] = 'f'; 84 | v[0x1] = 'g'; 85 | v[0x2] = 'w'; 86 | v[0x3] = 'V'; 87 | v[0x4] = '`'; 88 | v[0x5] = 'u'; 89 | v[0x6] = 'h'; 90 | v[0x7] = '}'; 91 | v[0x8] = '{'; 92 | v[0x9] = 'i'; 93 | v[0xa] = 'e'; 94 | v[0xb] = 'r'; 95 | for (unsigned int i = 0; i < 0xc; ++i) { 96 | v[i] ^= ((i + 0xc) % m); 97 | } 98 | v[0xc] = '\0'; 99 | } 100 | 101 | static inline void fill_getResources_signature(char v[]) { 102 | // ()Landroid/content/res/Resources; 103 | static unsigned int m = 0; 104 | 105 | if (m == 0) { 106 | m = 31; 107 | } else if (m == 37) { 108 | m = 41; 109 | } 110 | 111 | v[0x0] = '*'; 112 | v[0x1] = '*'; 113 | v[0x2] = 'H'; 114 | v[0x3] = 'd'; 115 | v[0x4] = 'h'; 116 | v[0x5] = 'c'; 117 | v[0x6] = 'z'; 118 | v[0x7] = 'f'; 119 | v[0x8] = 'c'; 120 | v[0x9] = 'o'; 121 | v[0xa] = '#'; 122 | v[0xb] = 'n'; 123 | v[0xc] = 'a'; 124 | v[0xd] = 'a'; 125 | v[0xe] = 'd'; 126 | v[0xf] = 't'; 127 | v[0x10] = '|'; 128 | v[0x11] = 'g'; 129 | v[0x12] = ';'; 130 | v[0x13] = 'g'; 131 | v[0x14] = 's'; 132 | v[0x15] = 'd'; 133 | v[0x16] = '7'; 134 | v[0x17] = 'K'; 135 | v[0x18] = '\x7f'; 136 | v[0x19] = 'h'; 137 | v[0x1a] = 's'; 138 | v[0x1b] = 'h'; 139 | v[0x1c] = 'l'; 140 | v[0x1d] = 'c'; 141 | v[0x1e] = 'd'; 142 | v[0x1f] = 'q'; 143 | v[0x20] = '8'; 144 | for (unsigned int i = 0; i < 0x21; ++i) { 145 | v[i] ^= ((i + 0x21) % m); 146 | } 147 | v[0x21] = '\0'; 148 | } 149 | 150 | static inline void fill_getIdentifier(char v[]) { 151 | // getIdentifier 152 | static unsigned int m = 0; 153 | 154 | if (m == 0) { 155 | m = 11; 156 | } else if (m == 13) { 157 | m = 17; 158 | } 159 | 160 | v[0x0] = 'e'; 161 | v[0x1] = 'f'; 162 | v[0x2] = 'p'; 163 | v[0x3] = 'L'; 164 | v[0x4] = 'b'; 165 | v[0x5] = 'b'; 166 | v[0x6] = 'f'; 167 | v[0x7] = '}'; 168 | v[0x8] = 'c'; 169 | v[0x9] = 'f'; 170 | v[0xa] = 'h'; 171 | v[0xb] = 'g'; 172 | v[0xc] = 'q'; 173 | for (unsigned int i = 0; i < 0xd; ++i) { 174 | v[i] ^= ((i + 0xd) % m); 175 | } 176 | v[0xd] = '\0'; 177 | } 178 | 179 | static inline void fill_getIdentifier_signature(char v[]) { 180 | // (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I 181 | static unsigned int m = 0; 182 | 183 | if (m == 0) { 184 | m = 53; 185 | } else if (m == 59) { 186 | m = 61; 187 | } 188 | 189 | v[0x0] = ','; 190 | v[0x1] = 'I'; 191 | v[0x2] = 'l'; 192 | v[0x3] = 'f'; 193 | v[0x4] = '~'; 194 | v[0x5] = 'h'; 195 | v[0x6] = '%'; 196 | v[0x7] = 'g'; 197 | v[0x8] = 'm'; 198 | v[0x9] = 'c'; 199 | v[0xa] = 'i'; 200 | v[0xb] = ' '; 201 | v[0xc] = 'C'; 202 | v[0xd] = 'e'; 203 | v[0xe] = '`'; 204 | v[0xf] = 'z'; 205 | v[0x10] = 'z'; 206 | v[0x11] = 'r'; 207 | v[0x12] = '-'; 208 | v[0x13] = '['; 209 | v[0x14] = 'r'; 210 | v[0x15] = 'x'; 211 | v[0x16] = 'l'; 212 | v[0x17] = 'z'; 213 | v[0x18] = '3'; 214 | v[0x19] = 'q'; 215 | v[0x1a] = '\x7f'; 216 | v[0x1b] = 'q'; 217 | v[0x1c] = 'G'; 218 | v[0x1d] = '\x0e'; 219 | v[0x1e] = 'q'; 220 | v[0x1f] = 'W'; 221 | v[0x20] = 'V'; 222 | v[0x21] = 'L'; 223 | v[0x22] = 'H'; 224 | v[0x23] = '@'; 225 | v[0x24] = '\x13'; 226 | v[0x25] = 'e'; 227 | v[0x26] = '@'; 228 | v[0x27] = 'J'; 229 | v[0x28] = 'Z'; 230 | v[0x29] = 'L'; 231 | v[0x2a] = '\x01'; 232 | v[0x2b] = 'C'; 233 | v[0x2c] = 'Q'; 234 | v[0x2d] = '_'; 235 | v[0x2e] = 'U'; 236 | v[0x2f] = '\x1c'; 237 | v[0x30] = 'g'; 238 | v[0x31] = 't'; 239 | v[0x32] = 's'; 240 | v[0x33] = 'k'; 241 | v[0x34] = 'm'; 242 | v[0x35] = 'c'; 243 | v[0x36] = '>'; 244 | v[0x37] = '/'; 245 | v[0x38] = 'N'; 246 | for (unsigned int i = 0; i < 0x39; ++i) { 247 | v[i] ^= ((i + 0x39) % m); 248 | } 249 | v[0x39] = '\0'; 250 | } 251 | 252 | static inline void fill_getString(char v[]) { 253 | // getString 254 | static unsigned int m = 0; 255 | 256 | if (m == 0) { 257 | m = 7; 258 | } else if (m == 11) { 259 | m = 13; 260 | } 261 | 262 | v[0x0] = 'e'; 263 | v[0x1] = 'f'; 264 | v[0x2] = 'p'; 265 | v[0x3] = 'V'; 266 | v[0x4] = 'r'; 267 | v[0x5] = 'r'; 268 | v[0x6] = 'h'; 269 | v[0x7] = 'l'; 270 | v[0x8] = 'd'; 271 | for (unsigned int i = 0; i < 0x9; ++i) { 272 | v[i] ^= ((i + 0x9) % m); 273 | } 274 | v[0x9] = '\0'; 275 | } 276 | 277 | static inline void fill_getString_signature(char v[]) { 278 | // (I)Ljava/lang/String; 279 | static unsigned int m = 0; 280 | 281 | if (m == 0) { 282 | m = 19; 283 | } else if (m == 23) { 284 | m = 29; 285 | } 286 | 287 | v[0x0] = '*'; 288 | v[0x1] = 'J'; 289 | v[0x2] = '-'; 290 | v[0x3] = 'I'; 291 | v[0x4] = 'l'; 292 | v[0x5] = 'f'; 293 | v[0x6] = '~'; 294 | v[0x7] = 'h'; 295 | v[0x8] = '%'; 296 | v[0x9] = 'g'; 297 | v[0xa] = 'm'; 298 | v[0xb] = 'c'; 299 | v[0xc] = 'i'; 300 | v[0xd] = ' '; 301 | v[0xe] = 'C'; 302 | v[0xf] = 'e'; 303 | v[0x10] = '`'; 304 | v[0x11] = 'i'; 305 | v[0x12] = 'o'; 306 | v[0x13] = 'e'; 307 | v[0x14] = '8'; 308 | for (unsigned int i = 0; i < 0x15; ++i) { 309 | v[i] ^= ((i + 0x15) % m); 310 | } 311 | v[0x15] = '\0'; 312 | } 313 | 314 | static inline void fill_string(char v[]) { 315 | // string 316 | static unsigned int m = 0; 317 | 318 | if (m == 0) { 319 | m = 5; 320 | } else if (m == 7) { 321 | m = 11; 322 | } 323 | 324 | v[0x0] = 'r'; 325 | v[0x1] = 'v'; 326 | v[0x2] = 'q'; 327 | v[0x3] = 'm'; 328 | v[0x4] = 'n'; 329 | v[0x5] = 'f'; 330 | for (unsigned int i = 0; i < 0x6; ++i) { 331 | v[i] ^= ((i + 0x6) % m); 332 | } 333 | v[0x6] = '\0'; 334 | } 335 | 336 | 337 | static jobject getLabel(JNIEnv *env, jobject activity) { 338 | char v1[0x10], v2[0x40]; 339 | 340 | jclass classActivity = (*env)->GetObjectClass(env, activity); 341 | 342 | fill_getResources(v1); 343 | fill_getResources_signature(v2); 344 | jmethodID getResources = (*env)->GetMethodID(env, classActivity, v1, v2); 345 | 346 | jobject resources = (*env)->CallObjectMethod(env, activity, getResources); 347 | 348 | jclass classResources = (*env)->GetObjectClass(env, resources); 349 | 350 | fill_getIdentifier(v1); 351 | fill_getIdentifier_signature(v2); 352 | jmethodID getIdentifier = (*env)->GetMethodID(env, classResources, v1, v2); 353 | 354 | fill_getString(v1); 355 | fill_getString_signature(v2); 356 | jmethodID getString = (*env)->GetMethodID(env, classResources, v1, v2); 357 | 358 | int length = fill_unsupported_genuine_x(v2); 359 | v2[length - 1] = asChar(getGenuine()); 360 | #if defined(DEBUG) || defined(DEBUG_GENUINE) 361 | LOGI("v: %s", v2); 362 | #endif 363 | jstring name = (*env)->NewStringUTF(env, v2); 364 | 365 | fill_string(v1); 366 | jstring stringType = (*env)->NewStringUTF(env, v1); 367 | char *packageName = getGenuinePackageName(); 368 | jstring stringPackageName = (*env)->NewStringUTF(env, packageName); 369 | int label = (*env)->CallIntMethod(env, resources, getIdentifier, name, stringType, 370 | stringPackageName); 371 | 372 | jstring string = (*env)->CallObjectMethod(env, resources, getString, label); 373 | 374 | (*env)->DeleteLocalRef(env, stringPackageName); 375 | (*env)->DeleteLocalRef(env, stringType); 376 | (*env)->DeleteLocalRef(env, name); 377 | (*env)->DeleteLocalRef(env, classResources); 378 | (*env)->DeleteLocalRef(env, resources); 379 | (*env)->DeleteLocalRef(env, classActivity); 380 | free(packageName); 381 | 382 | return string; 383 | } 384 | 385 | static void onNativeWindowCreated(ANativeActivity *activity, ANativeWindow *window) { 386 | #ifdef DEBUG_GENUINE 387 | LOGI("onNativeWindowCreated start %p %p", activity, window); 388 | LOGI("window, format: %d, width: %d, height: %d", ANativeWindow_getFormat(window), ANativeWindow_getWidth(window), ANativeWindow_getHeight(window)); 389 | #endif 390 | 391 | #define FORMAT AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM 392 | if (ANativeWindow_getFormat(window) != FORMAT) { 393 | ANativeWindow_setBuffersGeometry(window, ANativeWindow_getWidth(window), ANativeWindow_getHeight(window), FORMAT); 394 | } 395 | 396 | ANativeWindow_Buffer buffer = {0}; 397 | ANativeWindow_lock(window, &buffer, NULL); 398 | 399 | #ifdef DEBUG_GENUINE 400 | LOGI("buffer, format: %d, width: %d, height: %d, stride: %d", 401 | buffer.format, buffer.width, buffer.height, buffer.stride); 402 | #endif 403 | if (buffer.format != FORMAT) { 404 | ANativeWindow_unlockAndPost(window); 405 | return; 406 | } 407 | 408 | uint32_t *bits = buffer.bits; 409 | for (int i = 0; i < buffer.height; ++i) { 410 | memset(bits, 0, buffer.width * sizeof(uint32_t)); 411 | bits += buffer.stride; 412 | } 413 | 414 | JNIEnv *env = activity->env; 415 | jstring label = getLabel(env, activity->clazz); 416 | jobject bitmap = asBitmap(env, (int) (buffer.width * 0.618), label); 417 | 418 | void *pixels; 419 | AndroidBitmapInfo info; 420 | AndroidBitmap_getInfo(env, bitmap, &info); 421 | AndroidBitmap_lockPixels(env, bitmap, &pixels); 422 | 423 | uint32_t *dst = buffer.bits; 424 | uint32_t *src = pixels; 425 | size_t top = (buffer.height - info.height) / 2; 426 | size_t left = (buffer.width - info.width) / 2; 427 | dst += buffer.stride * top; 428 | for (size_t i = 0; i < info.height; ++i) { 429 | dst += left; 430 | memcpy(dst, src, info.width * sizeof(uint32_t)); 431 | src += info.width; 432 | dst += (buffer.stride - left); 433 | } 434 | 435 | AndroidBitmap_unlockPixels(env, bitmap); 436 | ANativeWindow_unlockAndPost(window); 437 | #ifdef DEBUG_GENUINE 438 | LOGI("onNativeWindowCreated complete %p %p", activity, window); 439 | #endif 440 | } 441 | 442 | static void onStart(ANativeActivity *activity __unused) { 443 | #ifdef DEBUG_GENUINE 444 | LOGI("onStart %p", activity); 445 | #endif 446 | set_started(); 447 | } 448 | 449 | static void onStop(ANativeActivity *activity __unused) { 450 | #ifdef DEBUG_GENUINE 451 | LOGI("onStop %p", activity); 452 | #endif 453 | kill(getpid(), SIGTERM); 454 | _exit(0); 455 | } 456 | 457 | static void onResume(ANativeActivity *activity __unused) { 458 | #ifdef DEBUG_GENUINE 459 | LOGI("onResume %p", activity); 460 | #endif 461 | } 462 | 463 | static void onDestroy(ANativeActivity *activity __unused) { 464 | #ifdef DEBUG_GENUINE 465 | LOGI("onDestroy %p", activity); 466 | #endif 467 | } 468 | 469 | #ifdef DEBUG 470 | static const char *action_name(const AInputEvent *event) { 471 | int action = AKeyEvent_getAction(event); 472 | switch (action) { 473 | case AKEY_EVENT_ACTION_DOWN: 474 | return "down"; 475 | case AKEY_EVENT_ACTION_UP: 476 | return "up"; 477 | case AKEY_EVENT_ACTION_MULTIPLE: 478 | return "multiple"; 479 | default: 480 | return "unknown"; 481 | } 482 | } 483 | #endif 484 | 485 | static int handleEvent(AInputEvent *event) { 486 | switch (AInputEvent_getType(event)) { 487 | case AINPUT_EVENT_TYPE_KEY: 488 | if (AKeyEvent_getKeyCode(event) == AKEYCODE_BACK) { 489 | #ifdef DEBUG 490 | LOGI("back %s", action_name(event)); 491 | #endif 492 | if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP) { 493 | ++repeat; 494 | #ifdef DEBUG_GENUINE 495 | LOGI("back, repeat: %d", repeat); 496 | #endif 497 | } 498 | return repeat < 6; 499 | } 500 | break; 501 | case AINPUT_EVENT_TYPE_MOTION: 502 | break; 503 | default: 504 | break; 505 | } 506 | repeat = 0; 507 | return 0; 508 | } 509 | 510 | static int onInputEvent(int fd __unused, int events, void *data) { 511 | AInputEvent *event; 512 | if (events == ALOOPER_EVENT_INPUT) { 513 | while (AInputQueue_getEvent(data, &event) >= 0) { 514 | if (AInputQueue_preDispatchEvent(data, event) == 0) { 515 | AInputQueue_finishEvent(data, event, handleEvent(event)); 516 | } 517 | } 518 | } 519 | return 1; 520 | } 521 | 522 | static void onInputQueueCreated(ANativeActivity *activity, AInputQueue *queue) { 523 | #ifdef DEBUG_GENUINE 524 | LOGI("onInputQueueCreated"); 525 | #endif 526 | AInputQueue_attachLooper(queue, activity->instance, ALOOPER_POLL_CALLBACK, onInputEvent, queue); 527 | } 528 | 529 | static void onInputQueueDestroyed(ANativeActivity *activity __unused, AInputQueue *queue) { 530 | #ifdef DEBUG_GENUINE 531 | LOGI("onInputQueueDestroyed"); 532 | #endif 533 | AInputQueue_detachLooper(queue); 534 | } 535 | 536 | JNIEXPORT void __unused ANativeActivity_onCreate(ANativeActivity *activity, 537 | void *savedState __unused, 538 | size_t savedStateSize __unused) { 539 | activity->callbacks->onStart = onStart; 540 | activity->callbacks->onStop = onStop; 541 | activity->callbacks->onResume = onResume; 542 | activity->callbacks->onDestroy = onDestroy; 543 | activity->callbacks->onInputQueueCreated = onInputQueueCreated; 544 | activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; 545 | activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; 546 | 547 | activity->instance = ALooper_prepare(0); 548 | } 549 | -------------------------------------------------------------------------------- /src/main/jni/openat.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/30. 3 | // 4 | 5 | #include 6 | #include 7 | #include "openat.h" 8 | 9 | #define STR_HELPER(x) #x 10 | #define STR(x) STR_HELPER(x) 11 | 12 | intptr_t openAt(intptr_t fd, const char *path, intptr_t flag) { 13 | #if defined(__arm__) 14 | intptr_t r; 15 | asm volatile( 16 | #ifndef OPTIMIZE_ASM 17 | "mov r0, %1\n\t" 18 | "mov r1, %2\n\t" 19 | "mov r2, %3\n\t" 20 | #endif 21 | 22 | "mov ip, r7\n\t" 23 | ".cfi_register r7, ip\n\t" 24 | "ldr r7, =" STR(__NR_openat) "\n\t" 25 | "svc #0\n\t" 26 | "mov r7, ip\n\t" 27 | ".cfi_restore r7\n\t" 28 | 29 | #ifndef OPTIMIZE_ASM 30 | "mov %0, r0\n\t" 31 | #endif 32 | : "=r" (r) 33 | : "r" (fd), "r" (path), "r" (flag)); 34 | return r; 35 | #elif defined(__aarch64__) 36 | intptr_t r; 37 | asm volatile( 38 | #ifndef OPTIMIZE_ASM 39 | "mov x0, %1\n\t" 40 | "mov x1, %2\n\t" 41 | "mov x2, %3\n\t" 42 | #endif 43 | 44 | "mov x8, " STR(__NR_openat) "\n\t" 45 | "svc #0\n\t" 46 | 47 | #ifndef OPTIMIZE_ASM 48 | "mov %0, x0\n\t" 49 | #endif 50 | : "=r" (r) 51 | : "r" (fd), "r" (path), "r" (flag)); 52 | return r; 53 | #else 54 | return (intptr_t) syscall(__NR_openat, fd, path, flag); 55 | #endif 56 | } 57 | -------------------------------------------------------------------------------- /src/main/jni/openat.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/30. 3 | // 4 | 5 | #ifndef BREVENT_OPENAT_H 6 | #define BREVENT_OPENAT_H 7 | 8 | #include 9 | #include 10 | #include "common.h" 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #ifdef __ANDROID__ 17 | 18 | intptr_t openAt(intptr_t fd, const char *path, intptr_t flag); 19 | 20 | #else 21 | #define openAt openat 22 | #endif 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | 28 | #endif //BREVENT_OPENAT_H 29 | -------------------------------------------------------------------------------- /src/main/jni/path.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019-05-03. 3 | // 4 | 5 | #include "path.h" 6 | #include 7 | 8 | static inline bool isSystem(const char *str) { 9 | return str != NULL 10 | && *str == '/' 11 | && *++str == 's' 12 | && *++str == 'y' 13 | && *++str == 's' 14 | && *++str == 't' 15 | && *++str == 'e' 16 | && *++str == 'm' 17 | && *++str == '/'; 18 | } 19 | 20 | static inline bool isVendor(const char *str) { 21 | return str != NULL 22 | && *str == '/' 23 | && *++str == 'v' 24 | && *++str == 'e' 25 | && *++str == 'n' 26 | && *++str == 'd' 27 | && *++str == 'o' 28 | && *++str == 'r' 29 | && *++str == '/'; 30 | } 31 | 32 | static inline bool isOem(const char *str) { 33 | return str != NULL 34 | && *str == '/' 35 | && *++str == 'o' 36 | && *++str == 'e' 37 | && *++str == 'm' 38 | && *++str == '/'; 39 | } 40 | 41 | bool isThirdParty(const char *str) { 42 | if (isSystem(str) || isVendor(str) || isOem(str)) { 43 | return false; 44 | } else { 45 | return true; 46 | } 47 | } 48 | 49 | bool isDataApp(const char *str) { 50 | return str != NULL 51 | && *str == '/' 52 | && *++str == 'd' 53 | && *++str == 'a' 54 | && *++str == 't' 55 | && *++str == 'a' 56 | && *++str == '/' 57 | && *++str == 'a' 58 | && *++str == 'p' 59 | && *++str == 'p' 60 | && *++str == '/'; 61 | } -------------------------------------------------------------------------------- /src/main/jni/path.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019-05-03. 3 | // 4 | 5 | #ifndef BREVENT_PATH_H 6 | #define BREVENT_PATH_H 7 | 8 | #include 9 | 10 | bool isThirdParty(const char *str); 11 | 12 | bool isDataApp(const char *str); 13 | 14 | #endif //BREVENT_PATH_H 15 | -------------------------------------------------------------------------------- /src/main/jni/plt.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/2/16. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "plt.h" 11 | #include "common.h" 12 | #include "path.h" 13 | 14 | /* 15 | * reference: https://android.googlesource.com/platform/bionic/+/master/linker/linker_soinfo.cpp 16 | */ 17 | static uint32_t gnu_hash(const uint8_t *name) { 18 | uint32_t h = 5381; 19 | 20 | while (*name) { 21 | h += (h << 5) + *name++; 22 | } 23 | return h; 24 | } 25 | 26 | static uint32_t elf_hash(const uint8_t *name) { 27 | uint32_t h = 0, g; 28 | 29 | while (*name) { 30 | h = (h << 4) + *name++; 31 | g = h & 0xf0000000; 32 | h ^= g; 33 | h ^= g >> 24; 34 | } 35 | 36 | return h; 37 | } 38 | 39 | static ElfW(Dyn) *find_dyn_by_tag(ElfW(Dyn) *dyn, ElfW(Sxword) tag) { 40 | while (dyn->d_tag != DT_NULL) { 41 | if (dyn->d_tag == tag) { 42 | return dyn; 43 | } 44 | ++dyn; 45 | } 46 | return NULL; 47 | } 48 | 49 | static inline bool is_global(ElfW(Sym) *sym) { 50 | unsigned char stb = ELF_ST_BIND(sym->st_info); 51 | if (stb == STB_GLOBAL || stb == STB_WEAK) { 52 | return sym->st_shndx != SHN_UNDEF; 53 | } else { 54 | return false; 55 | } 56 | } 57 | 58 | static ElfW(Addr) * 59 | find_symbol(struct dl_phdr_info *info, ElfW(Dyn) *base_addr, const char *symbol) { 60 | ElfW(Dyn) *dyn; 61 | 62 | dyn = find_dyn_by_tag(base_addr, DT_SYMTAB); 63 | ElfW(Sym) *dynsym = (ElfW(Sym) *) (info->dlpi_addr + dyn->d_un.d_ptr); 64 | 65 | dyn = find_dyn_by_tag(base_addr, DT_STRTAB); 66 | char *dynstr = (char *) (info->dlpi_addr + dyn->d_un.d_ptr); 67 | 68 | dyn = find_dyn_by_tag(base_addr, DT_GNU_HASH); 69 | if (dyn != NULL) { 70 | ElfW(Word) *dt_gnu_hash = (ElfW(Word) *) (info->dlpi_addr + dyn->d_un.d_ptr); 71 | size_t gnu_nbucket_ = dt_gnu_hash[0]; 72 | uint32_t gnu_maskwords_ = dt_gnu_hash[2]; 73 | uint32_t gnu_shift2_ = dt_gnu_hash[3]; 74 | ElfW(Addr) *gnu_bloom_filter_ = (ElfW(Addr) *) (dt_gnu_hash + 4); 75 | uint32_t *gnu_bucket_ = (uint32_t *) (gnu_bloom_filter_ + gnu_maskwords_); 76 | uint32_t *gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - dt_gnu_hash[1]; 77 | 78 | --gnu_maskwords_; 79 | 80 | uint32_t hash = gnu_hash((uint8_t *) symbol); 81 | uint32_t h2 = hash >> gnu_shift2_; 82 | 83 | uint32_t bloom_mask_bits = sizeof(ElfW(Addr)) * 8; 84 | uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_; 85 | ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num]; 86 | 87 | if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & 88 | (bloom_word >> (h2 % bloom_mask_bits))) == 0) { 89 | return NULL; 90 | } 91 | 92 | uint32_t n = gnu_bucket_[hash % gnu_nbucket_]; 93 | 94 | if (n == 0) { 95 | return NULL; 96 | } 97 | 98 | do { 99 | ElfW(Sym) *sym = dynsym + n; 100 | if (((gnu_chain_[n] ^ hash) >> 1) == 0 101 | && is_global(sym) 102 | && strcmp(dynstr + sym->st_name, symbol) == 0) { 103 | ElfW(Addr) *symbol_sym = (ElfW(Addr) *) (info->dlpi_addr + sym->st_value); 104 | #ifdef DEBUG_PLT 105 | LOGI("found %s(gnu+%u) in %s, %p", symbol, n, info->dlpi_name, symbol_sym); 106 | #endif 107 | return symbol_sym; 108 | } 109 | } while ((gnu_chain_[n++] & 1) == 0); 110 | 111 | return NULL; 112 | } 113 | 114 | dyn = find_dyn_by_tag(base_addr, DT_HASH); 115 | if (dyn != NULL) { 116 | ElfW(Word) *dt_hash = (ElfW(Word) *) (info->dlpi_addr + dyn->d_un.d_ptr); 117 | size_t nbucket_ = dt_hash[0]; 118 | uint32_t *bucket_ = dt_hash + 2; 119 | uint32_t *chain_ = bucket_ + nbucket_; 120 | 121 | uint32_t hash = elf_hash((uint8_t *) (symbol)); 122 | for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) { 123 | ElfW(Sym) *sym = dynsym + n; 124 | if (is_global(sym) && 125 | strcmp(dynstr + sym->st_name, symbol) == 0) { 126 | ElfW(Addr) *symbol_sym = (ElfW(Addr) *) (info->dlpi_addr + sym->st_value); 127 | #ifdef DEBUG_PLT 128 | LOGI("found %s(elf+%u) in %s, %p", symbol, n, info->dlpi_name, symbol_sym); 129 | #endif 130 | return symbol_sym; 131 | } 132 | } 133 | 134 | return NULL; 135 | } 136 | 137 | return NULL; 138 | } 139 | 140 | #if defined(__LP64__) 141 | #define Elf_Rela ElfW(Rela) 142 | #define ELF_R_SYM ELF64_R_SYM 143 | #else 144 | #define Elf_Rela ElfW(Rel) 145 | #define ELF_R_SYM ELF32_R_SYM 146 | #endif 147 | 148 | #ifdef DEBUG_PLT 149 | #if defined(__x86_64__) 150 | #define R_JUMP_SLOT R_X86_64_JUMP_SLOT 151 | #define ELF_R_TYPE ELF64_R_TYPE 152 | #elif defined(__i386__) 153 | #define R_JUMP_SLOT R_386_JMP_SLOT 154 | #define ELF_R_TYPE ELF32_R_TYPE 155 | #elif defined(__arm__) 156 | #define R_JUMP_SLOT R_ARM_JUMP_SLOT 157 | #define ELF_R_TYPE ELF32_R_TYPE 158 | #elif defined(__aarch64__) 159 | #define R_JUMP_SLOT R_AARCH64_JUMP_SLOT 160 | #define ELF_R_TYPE ELF64_R_TYPE 161 | #else 162 | #error unsupported OS 163 | #endif 164 | #endif 165 | 166 | static ElfW(Addr) *find_plt(struct dl_phdr_info *info, ElfW(Dyn) *base_addr, const char *symbol) { 167 | ElfW(Dyn) *dyn = find_dyn_by_tag(base_addr, DT_JMPREL); 168 | if (dyn == NULL) { 169 | return NULL; 170 | } 171 | Elf_Rela *dynplt = (Elf_Rela *) (info->dlpi_addr + dyn->d_un.d_ptr); 172 | 173 | dyn = find_dyn_by_tag(base_addr, DT_SYMTAB); 174 | ElfW(Sym) *dynsym = (ElfW(Sym) *) (info->dlpi_addr + dyn->d_un.d_ptr); 175 | 176 | dyn = find_dyn_by_tag(base_addr, DT_STRTAB); 177 | char *dynstr = (char *) (info->dlpi_addr + dyn->d_un.d_ptr); 178 | 179 | dyn = find_dyn_by_tag(base_addr, DT_PLTRELSZ); 180 | if (dyn == NULL) { 181 | return NULL; 182 | } 183 | size_t count = dyn->d_un.d_val / sizeof(Elf_Rela); 184 | 185 | for (size_t i = 0; i < count; ++i) { 186 | Elf_Rela *plt = dynplt + i; 187 | #ifdef DEBUG_PLT 188 | if (ELF_R_TYPE(plt->r_info) != R_JUMP_SLOT) { 189 | LOGW("invalid type for plt+%zu in %s", i, info->dlpi_name); 190 | continue; 191 | } 192 | #endif 193 | size_t idx = ELF_R_SYM(plt->r_info); 194 | idx = dynsym[idx].st_name; 195 | if (strcmp(dynstr + idx, symbol) == 0) { 196 | ElfW(Addr) *symbol_plt = (ElfW(Addr) *) (info->dlpi_addr + plt->r_offset); 197 | #ifdef DEBUG_PLT 198 | ElfW(Addr) *symbol_plt_value = (ElfW(Addr) *) *symbol_plt; 199 | LOGI("found %s(plt+%zu) in %s, %p -> %p", symbol, i, info->dlpi_name, symbol_plt, 200 | symbol_plt_value); 201 | #endif 202 | return symbol_plt; 203 | } 204 | } 205 | 206 | return NULL; 207 | } 208 | 209 | static inline bool isso(const char *str) { 210 | if (str == NULL) { 211 | return false; 212 | } 213 | const char *dot = strrchr(str, '.'); 214 | return dot != NULL 215 | && *++dot == 's' 216 | && *++dot == 'o' 217 | && (*++dot == '\0' || *dot == '\r' || *dot == '\n'); 218 | } 219 | 220 | static inline bool should_check_plt(Symbol *symbol, struct dl_phdr_info *info) { 221 | const char *path = info->dlpi_name; 222 | if (symbol->check & PLT_CHECK_PLT_ALL) { 223 | return true; 224 | } else if (symbol->check & PLT_CHECK_PLT_APP) { 225 | return *path != '/' || isThirdParty(path); 226 | } else { 227 | return false; 228 | } 229 | } 230 | 231 | static int callback(struct dl_phdr_info *info, __unused size_t size, void *data) { 232 | if (!isso(info->dlpi_name)) { 233 | #ifdef DEBUG_PLT 234 | LOGW("ignore non-so: %s", info->dlpi_name); 235 | #endif 236 | return 0; 237 | } 238 | Symbol *symbol = (Symbol *) data; 239 | #if 0 240 | LOGI("Name: \"%s\" (%d segments)", info->dlpi_name, info->dlpi_phnum); 241 | #endif 242 | ++symbol->total; 243 | for (ElfW(Half) phdr_idx = 0; phdr_idx < info->dlpi_phnum; ++phdr_idx) { 244 | ElfW(Phdr) phdr = info->dlpi_phdr[phdr_idx]; 245 | if (phdr.p_type != PT_DYNAMIC) { 246 | continue; 247 | } 248 | ElfW(Dyn) *base_addr = (ElfW(Dyn) *) (info->dlpi_addr + phdr.p_vaddr); 249 | ElfW(Addr) *addr; 250 | addr = should_check_plt(symbol, info) ? find_plt(info, base_addr, symbol->symbol_name) : NULL; 251 | if (addr != NULL) { 252 | if (symbol->symbol_plt != NULL) { 253 | ElfW(Addr) *addr_value = (ElfW(Addr) *) *addr; 254 | ElfW(Addr) *symbol_plt_value = (ElfW(Addr) *) *symbol->symbol_plt; 255 | if (addr_value != symbol_plt_value) { 256 | #ifdef DEBUG_PLT 257 | LOGW("%s, plt %p -> %p != %p", symbol->symbol_name, addr, addr_value, 258 | symbol_plt_value); 259 | #endif 260 | return 1; 261 | } 262 | } 263 | symbol->symbol_plt = addr; 264 | if (symbol->check & PLT_CHECK_NAME) { 265 | if (symbol->size == 0) { 266 | symbol->size = 1; 267 | symbol->names = calloc(1, sizeof(char *)); 268 | } else { 269 | ++symbol->size; 270 | symbol->names = realloc(symbol->names, symbol->size * sizeof(char *)); 271 | } 272 | #ifdef DEBUG_PLT 273 | LOGI("[%d]: %s", symbol->size - 1, info->dlpi_name); 274 | #endif 275 | symbol->names[symbol->size - 1] = strdup(info->dlpi_name); 276 | } 277 | } 278 | addr = find_symbol(info, base_addr, symbol->symbol_name); 279 | if (addr != NULL) { 280 | symbol->symbol_sym = addr; 281 | if (symbol->check == PLT_CHECK_SYM_ONE) { 282 | return PLT_CHECK_SYM_ONE; 283 | } 284 | } 285 | if (symbol->symbol_plt != NULL && symbol->symbol_sym != NULL) { 286 | ElfW(Addr) *symbol_plt_value = (ElfW(Addr) *) *symbol->symbol_plt; 287 | // stop if unmatch 288 | if (symbol_plt_value != symbol->symbol_sym) { 289 | #ifdef DEBUG_PLT 290 | LOGW("%s, plt: %p -> %p != %p", symbol->symbol_name, symbol->symbol_plt, 291 | symbol_plt_value, symbol->symbol_sym); 292 | #endif 293 | return 1; 294 | } 295 | } 296 | } 297 | return 0; 298 | } 299 | 300 | void *plt_dlsym(const char *name, size_t *total) { 301 | Symbol symbol; 302 | memset(&symbol, 0, sizeof(Symbol)); 303 | if (total == NULL) { 304 | symbol.check = PLT_CHECK_SYM_ONE; 305 | } 306 | symbol.symbol_name = name; 307 | dl_iterate_phdr_symbol(&symbol); 308 | if (total != NULL) { 309 | *total = symbol.total; 310 | } 311 | return symbol.symbol_sym; 312 | } 313 | 314 | bool isPltHooked(const char *name, bool all) { 315 | Symbol symbol; 316 | memset(&symbol, 0, sizeof(Symbol)); 317 | symbol.check = all ? PLT_CHECK_PLT_ALL : PLT_CHECK_PLT_APP; 318 | symbol.symbol_name = name; 319 | return dl_iterate_phdr_symbol(&symbol) ? true : false; 320 | } 321 | 322 | /** 323 | * symbol->check PLT_CHECK_PLT | PLT_CHECK_NAME 324 | * @param symbol 325 | * @return 326 | */ 327 | int dl_iterate_phdr_symbol(Symbol *symbol) { 328 | int result; 329 | #ifdef DEBUG_PLT 330 | LOGI("start dl_iterate_phdr: %s", symbol->symbol_name); 331 | #endif 332 | #if __ANDROID_API__ >= 21 || !defined(__arm__) 333 | result = dl_iterate_phdr(callback, symbol); 334 | #else 335 | int (*dl_iterate_phdr)(int (*)(struct dl_phdr_info *, size_t, void *), void *); 336 | dl_iterate_phdr = dlsym(RTLD_NEXT, "dl_iterate_phdr"); 337 | if (dl_iterate_phdr != NULL) { 338 | result = dl_iterate_phdr(callback, symbol); 339 | } else { 340 | result = 0; 341 | void *handle = dlopen("libdl.so", RTLD_NOW); 342 | dl_iterate_phdr = dlsym(handle, "dl_iterate_phdr"); 343 | if (dl_iterate_phdr != NULL) { 344 | result = dl_iterate_phdr(callback, symbol); 345 | } else { 346 | LOGW("cannot dlsym dl_iterate_phdr"); 347 | } 348 | dlclose(handle); 349 | } 350 | #endif 351 | #ifdef DEBUG_PLT 352 | LOGI("complete dl_iterate_phdr: %s", symbol->symbol_name); 353 | #endif 354 | return result; 355 | } 356 | -------------------------------------------------------------------------------- /src/main/jni/plt.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/2/16. 3 | // 4 | 5 | #ifndef BREVENT_PLT_H 6 | #define BREVENT_PLT_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #define PLT_CHECK_PLT_APP ((unsigned short) 0x1u) 18 | #define PLT_CHECK_PLT_ALL ((unsigned short) 0x2u) 19 | #define PLT_CHECK_NAME ((unsigned short) 0x4u) 20 | #define PLT_CHECK_SYM_ONE ((unsigned short) 0x8u) 21 | 22 | typedef struct Symbol { 23 | unsigned short check; 24 | unsigned short size; 25 | size_t total; 26 | ElfW(Addr) *symbol_plt; 27 | ElfW(Addr) *symbol_sym; 28 | const char *symbol_name; 29 | char **names; 30 | } Symbol; 31 | 32 | int dl_iterate_phdr_symbol(Symbol *symbol); 33 | 34 | void *plt_dlsym(const char *name, size_t *total); 35 | 36 | bool isPltHooked(const char *name, bool all); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | #endif //BREVENT_PLT_H 43 | -------------------------------------------------------------------------------- /src/main/jni/pm.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/15. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "common.h" 10 | #include "pm.h" 11 | #ifndef DEBUG 12 | #ifdef DEBUG_PM 13 | #define DEBUG 14 | #endif 15 | #endif 16 | 17 | static inline void fill_android_os_ServiceManager(char v[]) { 18 | // android/os/ServiceManager 19 | static unsigned int m = 0; 20 | 21 | if (m == 0) { 22 | m = 23; 23 | } else if (m == 29) { 24 | m = 31; 25 | } 26 | 27 | v[0x0] = 'c'; 28 | v[0x1] = 'm'; 29 | v[0x2] = '`'; 30 | v[0x3] = 'w'; 31 | v[0x4] = 'i'; 32 | v[0x5] = 'n'; 33 | v[0x6] = 'l'; 34 | v[0x7] = '&'; 35 | v[0x8] = 'e'; 36 | v[0x9] = 'x'; 37 | v[0xa] = '#'; 38 | v[0xb] = '^'; 39 | v[0xc] = 'k'; 40 | v[0xd] = '}'; 41 | v[0xe] = 'f'; 42 | v[0xf] = 'x'; 43 | v[0x10] = 'q'; 44 | v[0x11] = 'v'; 45 | v[0x12] = 'Y'; 46 | v[0x13] = 't'; 47 | v[0x14] = 'x'; 48 | v[0x15] = 'a'; 49 | v[0x16] = 'f'; 50 | v[0x17] = 'g'; 51 | v[0x18] = 'q'; 52 | for (unsigned int i = 0; i < 0x19; ++i) { 53 | v[i] ^= ((i + 0x19) % m); 54 | } 55 | v[0x19] = '\0'; 56 | } 57 | 58 | static inline void fill_getService(char v[]) { 59 | // getService 60 | static unsigned int m = 0; 61 | 62 | if (m == 0) { 63 | m = 7; 64 | } else if (m == 11) { 65 | m = 13; 66 | } 67 | 68 | v[0x0] = 'd'; 69 | v[0x1] = 'a'; 70 | v[0x2] = 'q'; 71 | v[0x3] = 'U'; 72 | v[0x4] = 'e'; 73 | v[0x5] = 's'; 74 | v[0x6] = 't'; 75 | v[0x7] = 'j'; 76 | v[0x8] = 'g'; 77 | v[0x9] = '`'; 78 | for (unsigned int i = 0; i < 0xa; ++i) { 79 | v[i] ^= ((i + 0xa) % m); 80 | } 81 | v[0xa] = '\0'; 82 | } 83 | 84 | static inline void fill_getService_signature(char v[]) { 85 | // (Ljava/lang/String;)Landroid/os/IBinder; 86 | static unsigned int m = 0; 87 | 88 | if (m == 0) { 89 | m = 37; 90 | } else if (m == 41) { 91 | m = 43; 92 | } 93 | 94 | v[0x0] = '+'; 95 | v[0x1] = 'H'; 96 | v[0x2] = 'o'; 97 | v[0x3] = 'g'; 98 | v[0x4] = 'q'; 99 | v[0x5] = 'i'; 100 | v[0x6] = '&'; 101 | v[0x7] = 'f'; 102 | v[0x8] = 'j'; 103 | v[0x9] = 'b'; 104 | v[0xa] = 'j'; 105 | v[0xb] = '!'; 106 | v[0xc] = '\\'; 107 | v[0xd] = 'd'; 108 | v[0xe] = 'c'; 109 | v[0xf] = '{'; 110 | v[0x10] = '}'; 111 | v[0x11] = 's'; 112 | v[0x12] = '.'; 113 | v[0x13] = '?'; 114 | v[0x14] = '['; 115 | v[0x15] = 'y'; 116 | v[0x16] = 'w'; 117 | v[0x17] = '~'; 118 | v[0x18] = 'i'; 119 | v[0x19] = 's'; 120 | v[0x1a] = 't'; 121 | v[0x1b] = 'z'; 122 | v[0x1c] = '0'; 123 | v[0x1d] = 'O'; 124 | v[0x1e] = 'R'; 125 | v[0x1f] = '\r'; 126 | v[0x20] = 'j'; 127 | v[0x21] = 'f'; 128 | v[0x22] = 'i'; 129 | v[0x23] = 'o'; 130 | v[0x24] = 'f'; 131 | v[0x25] = 'f'; 132 | v[0x26] = 'v'; 133 | v[0x27] = '>'; 134 | for (unsigned int i = 0; i < 0x28; ++i) { 135 | v[i] ^= ((i + 0x28) % m); 136 | } 137 | v[0x28] = '\0'; 138 | } 139 | 140 | static inline void fill_package(char v[]) { 141 | // package 142 | static unsigned int m = 0; 143 | 144 | if (m == 0) { 145 | m = 5; 146 | } else if (m == 7) { 147 | m = 11; 148 | } 149 | 150 | v[0x0] = 'r'; 151 | v[0x1] = 'b'; 152 | v[0x2] = 'g'; 153 | v[0x3] = 'k'; 154 | v[0x4] = '`'; 155 | v[0x5] = 'e'; 156 | v[0x6] = 'f'; 157 | for (unsigned int i = 0; i < 0x7; ++i) { 158 | v[i] ^= ((i + 0x7) % m); 159 | } 160 | v[0x7] = '\0'; 161 | } 162 | 163 | static inline void fill_android_content_pm_IPackageManager$Stub(char v[]) { 164 | // android/content/pm/IPackageManager$Stub 165 | static unsigned int m = 0; 166 | 167 | if (m == 0) { 168 | m = 37; 169 | } else if (m == 41) { 170 | m = 43; 171 | } 172 | 173 | v[0x0] = 'c'; 174 | v[0x1] = 'm'; 175 | v[0x2] = '`'; 176 | v[0x3] = 'w'; 177 | v[0x4] = 'i'; 178 | v[0x5] = 'n'; 179 | v[0x6] = 'l'; 180 | v[0x7] = '&'; 181 | v[0x8] = 'i'; 182 | v[0x9] = 'd'; 183 | v[0xa] = 'b'; 184 | v[0xb] = 'y'; 185 | v[0xc] = 'k'; 186 | v[0xd] = 'a'; 187 | v[0xe] = 'd'; 188 | v[0xf] = '>'; 189 | v[0x10] = 'b'; 190 | v[0x11] = '~'; 191 | v[0x12] = ';'; 192 | v[0x13] = '\\'; 193 | v[0x14] = 'F'; 194 | v[0x15] = 'v'; 195 | v[0x16] = '{'; 196 | v[0x17] = 'r'; 197 | v[0x18] = '{'; 198 | v[0x19] = '|'; 199 | v[0x1a] = 'y'; 200 | v[0x1b] = 'P'; 201 | v[0x1c] = '\x7f'; 202 | v[0x1d] = 'q'; 203 | v[0x1e] = 'A'; 204 | v[0x1f] = 'F'; 205 | v[0x20] = 'G'; 206 | v[0x21] = 'Q'; 207 | v[0x22] = '\x00'; 208 | v[0x23] = 'S'; 209 | v[0x24] = 'u'; 210 | v[0x25] = 'w'; 211 | v[0x26] = 'a'; 212 | for (unsigned int i = 0; i < 0x27; ++i) { 213 | v[i] ^= ((i + 0x27) % m); 214 | } 215 | v[0x27] = '\0'; 216 | } 217 | 218 | static inline void fill_asInterface(char v[]) { 219 | // asInterface 220 | static unsigned int m = 0; 221 | 222 | if (m == 0) { 223 | m = 7; 224 | } else if (m == 11) { 225 | m = 13; 226 | } 227 | 228 | v[0x0] = 'e'; 229 | v[0x1] = 'v'; 230 | v[0x2] = 'O'; 231 | v[0x3] = 'n'; 232 | v[0x4] = 'u'; 233 | v[0x5] = 'g'; 234 | v[0x6] = 'q'; 235 | v[0x7] = 'b'; 236 | v[0x8] = 'd'; 237 | v[0x9] = 'e'; 238 | v[0xa] = 'e'; 239 | for (unsigned int i = 0; i < 0xb; ++i) { 240 | v[i] ^= ((i + 0xb) % m); 241 | } 242 | v[0xb] = '\0'; 243 | } 244 | 245 | static inline void fill_asInterface_signature(char v[]) { 246 | // (Landroid/os/IBinder;)Landroid/content/pm/IPackageManager; 247 | static unsigned int m = 0; 248 | 249 | if (m == 0) { 250 | m = 53; 251 | } else if (m == 59) { 252 | m = 61; 253 | } 254 | 255 | v[0x0] = '-'; 256 | v[0x1] = 'J'; 257 | v[0x2] = 'f'; 258 | v[0x3] = 'f'; 259 | v[0x4] = 'm'; 260 | v[0x5] = 'x'; 261 | v[0x6] = 'd'; 262 | v[0x7] = 'e'; 263 | v[0x8] = 'i'; 264 | v[0x9] = '!'; 265 | v[0xa] = '`'; 266 | v[0xb] = 'c'; 267 | v[0xc] = '>'; 268 | v[0xd] = '['; 269 | v[0xe] = 'Q'; 270 | v[0xf] = '}'; 271 | v[0x10] = '{'; 272 | v[0x11] = 'r'; 273 | v[0x12] = 'r'; 274 | v[0x13] = 'j'; 275 | v[0x14] = '"'; 276 | v[0x15] = '3'; 277 | v[0x16] = 'W'; 278 | v[0x17] = '}'; 279 | v[0x18] = 's'; 280 | v[0x19] = 'z'; 281 | v[0x1a] = 'm'; 282 | v[0x1b] = 'O'; 283 | v[0x1c] = 'H'; 284 | v[0x1d] = 'F'; 285 | v[0x1e] = '\x0c'; 286 | v[0x1f] = 'G'; 287 | v[0x20] = 'J'; 288 | v[0x21] = 'H'; 289 | v[0x22] = 'S'; 290 | v[0x23] = 'M'; 291 | v[0x24] = 'G'; 292 | v[0x25] = '^'; 293 | v[0x26] = '\x04'; 294 | v[0x27] = '\\'; 295 | v[0x28] = '@'; 296 | v[0x29] = '\x01'; 297 | v[0x2a] = 'f'; 298 | v[0x2b] = '`'; 299 | v[0x2c] = 'P'; 300 | v[0x2d] = 'Q'; 301 | v[0x2e] = 'X'; 302 | v[0x2f] = 'U'; 303 | v[0x30] = 'g'; 304 | v[0x31] = 'd'; 305 | v[0x32] = 'O'; 306 | v[0x33] = 'b'; 307 | v[0x34] = 'j'; 308 | v[0x35] = 'd'; 309 | v[0x36] = 'a'; 310 | v[0x37] = 'b'; 311 | v[0x38] = 'z'; 312 | v[0x39] = '2'; 313 | for (unsigned int i = 0; i < 0x3a; ++i) { 314 | v[i] ^= ((i + 0x3a) % m); 315 | } 316 | v[0x3a] = '\0'; 317 | } 318 | 319 | static inline void fill_getApplicationInfo(char v[]) { 320 | // getApplicationInfo 321 | static unsigned int m = 0; 322 | 323 | if (m == 0) { 324 | m = 17; 325 | } else if (m == 19) { 326 | m = 23; 327 | } 328 | 329 | v[0x0] = 'f'; 330 | v[0x1] = 'g'; 331 | v[0x2] = 'w'; 332 | v[0x3] = 'E'; 333 | v[0x4] = 'u'; 334 | v[0x5] = 'v'; 335 | v[0x6] = 'k'; 336 | v[0x7] = 'a'; 337 | v[0x8] = 'j'; 338 | v[0x9] = 'k'; 339 | v[0xa] = '\x7f'; 340 | v[0xb] = 'e'; 341 | v[0xc] = 'b'; 342 | v[0xd] = '`'; 343 | v[0xe] = 'F'; 344 | v[0xf] = '~'; 345 | v[0x10] = 'f'; 346 | v[0x11] = 'n'; 347 | for (unsigned int i = 0; i < 0x12; ++i) { 348 | v[i] ^= ((i + 0x12) % m); 349 | } 350 | v[0x12] = '\0'; 351 | } 352 | 353 | static inline void fill_getApplicationInfo_signature(char v[]) { 354 | // (Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo; 355 | static unsigned int m = 0; 356 | 357 | if (m == 0) { 358 | m = 53; 359 | } else if (m == 59) { 360 | m = 61; 361 | } 362 | 363 | v[0x0] = '-'; 364 | v[0x1] = 'J'; 365 | v[0x2] = 'm'; 366 | v[0x3] = 'i'; 367 | v[0x4] = '\x7f'; 368 | v[0x5] = 'k'; 369 | v[0x6] = '$'; 370 | v[0x7] = '`'; 371 | v[0x8] = 'l'; 372 | v[0x9] = '`'; 373 | v[0xa] = 'h'; 374 | v[0xb] = '?'; 375 | v[0xc] = 'B'; 376 | v[0xd] = 'f'; 377 | v[0xe] = 'a'; 378 | v[0xf] = '}'; 379 | v[0x10] = '{'; 380 | v[0x11] = 'q'; 381 | v[0x12] = ','; 382 | v[0x13] = 'Q'; 383 | v[0x14] = 'P'; 384 | v[0x15] = '3'; 385 | v[0x16] = 'W'; 386 | v[0x17] = '}'; 387 | v[0x18] = 's'; 388 | v[0x19] = 'z'; 389 | v[0x1a] = 'm'; 390 | v[0x1b] = 'O'; 391 | v[0x1c] = 'H'; 392 | v[0x1d] = 'F'; 393 | v[0x1e] = '\x0c'; 394 | v[0x1f] = 'G'; 395 | v[0x20] = 'J'; 396 | v[0x21] = 'H'; 397 | v[0x22] = 'S'; 398 | v[0x23] = 'M'; 399 | v[0x24] = 'G'; 400 | v[0x25] = '^'; 401 | v[0x26] = '\x04'; 402 | v[0x27] = '\\'; 403 | v[0x28] = '@'; 404 | v[0x29] = '\x01'; 405 | v[0x2a] = 'n'; 406 | v[0x2b] = '@'; 407 | v[0x2c] = 'A'; 408 | v[0x2d] = '^'; 409 | v[0x2e] = 'Z'; 410 | v[0x2f] = 'W'; 411 | v[0x30] = 'a'; 412 | v[0x31] = 'u'; 413 | v[0x32] = 'k'; 414 | v[0x33] = 'l'; 415 | v[0x34] = 'j'; 416 | v[0x35] = 'L'; 417 | v[0x36] = 'h'; 418 | v[0x37] = 'a'; 419 | v[0x38] = 'g'; 420 | v[0x39] = '2'; 421 | for (unsigned int i = 0; i < 0x3a; ++i) { 422 | v[i] ^= ((i + 0x3a) % m); 423 | } 424 | v[0x3a] = '\0'; 425 | } 426 | 427 | static inline void fill_sourceDir(char v[]) { 428 | // sourceDir 429 | static unsigned int m = 0; 430 | 431 | if (m == 0) { 432 | m = 7; 433 | } else if (m == 11) { 434 | m = 13; 435 | } 436 | 437 | v[0x0] = 'q'; 438 | v[0x1] = 'l'; 439 | v[0x2] = 'q'; 440 | v[0x3] = 'w'; 441 | v[0x4] = 'e'; 442 | v[0x5] = 'e'; 443 | v[0x6] = 'E'; 444 | v[0x7] = 'k'; 445 | v[0x8] = 'q'; 446 | for (unsigned int i = 0; i < 0x9; ++i) { 447 | v[i] ^= ((i + 0x9) % m); 448 | } 449 | v[0x9] = '\0'; 450 | } 451 | 452 | static inline void fill_sourceDir_signature(char v[]) { 453 | // Ljava/lang/String; 454 | static unsigned int m = 0; 455 | 456 | if (m == 0) { 457 | m = 17; 458 | } else if (m == 19) { 459 | m = 23; 460 | } 461 | 462 | v[0x0] = 'M'; 463 | v[0x1] = 'h'; 464 | v[0x2] = 'b'; 465 | v[0x3] = 'r'; 466 | v[0x4] = 'd'; 467 | v[0x5] = ')'; 468 | v[0x6] = 'k'; 469 | v[0x7] = 'i'; 470 | v[0x8] = 'g'; 471 | v[0x9] = 'm'; 472 | v[0xa] = '$'; 473 | v[0xb] = '_'; 474 | v[0xc] = 'y'; 475 | v[0xd] = '|'; 476 | v[0xe] = 'f'; 477 | v[0xf] = '~'; 478 | v[0x10] = 'g'; 479 | v[0x11] = ':'; 480 | for (unsigned int i = 0; i < 0x12; ++i) { 481 | v[i] ^= ((i + 0x12) % m); 482 | } 483 | v[0x12] = '\0'; 484 | } 485 | 486 | static inline void fill_android_os_BinderProxy(char v[]) { 487 | // android/os/BinderProxy 488 | static unsigned int m = 0; 489 | 490 | if (m == 0) { 491 | m = 19; 492 | } else if (m == 23) { 493 | m = 29; 494 | } 495 | 496 | v[0x0] = 'b'; 497 | v[0x1] = 'j'; 498 | v[0x2] = 'a'; 499 | v[0x3] = 't'; 500 | v[0x4] = 'h'; 501 | v[0x5] = 'a'; 502 | v[0x6] = 'm'; 503 | v[0x7] = '%'; 504 | v[0x8] = 'd'; 505 | v[0x9] = '\x7f'; 506 | v[0xa] = '"'; 507 | v[0xb] = 'L'; 508 | v[0xc] = 'f'; 509 | v[0xd] = '~'; 510 | v[0xe] = 'u'; 511 | v[0xf] = 'w'; 512 | v[0x10] = 'r'; 513 | v[0x11] = 'Q'; 514 | v[0x12] = 'p'; 515 | v[0x13] = 'l'; 516 | v[0x14] = '|'; 517 | v[0x15] = '|'; 518 | for (unsigned int i = 0; i < 0x16; ++i) { 519 | v[i] ^= ((i + 0x16) % m); 520 | } 521 | v[0x16] = '\0'; 522 | } 523 | 524 | static inline void fill_invalid_package_manager_path_s(char v[]) { 525 | // invalid package manager, path: %s 526 | static unsigned int m = 0; 527 | 528 | if (m == 0) { 529 | m = 31; 530 | } else if (m == 37) { 531 | m = 41; 532 | } 533 | 534 | v[0x0] = 'k'; 535 | v[0x1] = 'm'; 536 | v[0x2] = 'r'; 537 | v[0x3] = 'd'; 538 | v[0x4] = 'j'; 539 | v[0x5] = 'n'; 540 | v[0x6] = 'l'; 541 | v[0x7] = ')'; 542 | v[0x8] = 'z'; 543 | v[0x9] = 'j'; 544 | v[0xa] = 'o'; 545 | v[0xb] = 'f'; 546 | v[0xc] = 'o'; 547 | v[0xd] = 'h'; 548 | v[0xe] = 'u'; 549 | v[0xf] = '1'; 550 | v[0x10] = '\x7f'; 551 | v[0x11] = 'r'; 552 | v[0x12] = 'z'; 553 | v[0x13] = 't'; 554 | v[0x14] = 'q'; 555 | v[0x15] = 'r'; 556 | v[0x16] = 'j'; 557 | v[0x17] = '5'; 558 | v[0x18] = ':'; 559 | v[0x19] = 'k'; 560 | v[0x1a] = '}'; 561 | v[0x1b] = 'i'; 562 | v[0x1c] = 'v'; 563 | v[0x1d] = ':'; 564 | v[0x1e] = '!'; 565 | v[0x1f] = '\''; 566 | v[0x20] = 'p'; 567 | for (unsigned int i = 0; i < 0x21; ++i) { 568 | v[i] ^= ((i + 0x21) % m); 569 | } 570 | v[0x21] = '\0'; 571 | } 572 | 573 | char *getPath(JNIEnv *env, int uid, const char *packageName) { 574 | char *path = NULL; 575 | char v1[0x80], v2[0x80]; 576 | 577 | if (packageName == NULL) { 578 | return NULL; 579 | } 580 | fill_android_os_ServiceManager(v2); // 0x19 + 1 581 | jclass classServiceManager = (*env)->FindClass(env, v2); 582 | if (classServiceManager == NULL) { 583 | #ifdef DEBUG 584 | LOGW("cannot find ServiceManager"); 585 | #endif 586 | (*env)->ExceptionClear(env); 587 | return NULL; 588 | } 589 | debug(env, "ServiceManager: %s", classServiceManager); 590 | 591 | fill_getService(v1); // 0xa + 1 592 | fill_getService_signature(v2); // 0x28 + 1 593 | jmethodID method = (*env)->GetStaticMethodID(env, classServiceManager, v1, v2); 594 | #ifdef DEBUG 595 | LOGI("ServiceManager.getService: %p", method); 596 | #endif 597 | if (method == NULL) { 598 | (*env)->ExceptionClear(env); 599 | goto cleanClassServiceManager; 600 | } 601 | 602 | fill_package(v2); // 0x8 + 1 603 | jstring stringPackage = (*env)->NewStringUTF(env, v2); 604 | jobject service = (*env)->CallStaticObjectMethod(env, classServiceManager, method, 605 | stringPackage); 606 | if (service == NULL) { 607 | #ifdef DEBUG 608 | LOGW("cannot find package service"); 609 | #endif 610 | if ((*env)->ExceptionCheck(env)) { 611 | #ifdef DEBUG 612 | (*env)->ExceptionDescribe(env); 613 | #endif 614 | (*env)->ExceptionClear(env); 615 | } 616 | goto cleanStringPackage; 617 | } 618 | #ifdef DEBUG 619 | debug(env, "package service: %s", service); 620 | #endif 621 | 622 | fill_android_content_pm_IPackageManager$Stub(v2); // 0x27 + 1 623 | jclass classIPackageManager$Stub = (*env)->FindClass(env, v2); 624 | debug(env, "IPackageManager$Stub: %s", classIPackageManager$Stub); 625 | if (classIPackageManager$Stub == NULL) { 626 | #ifdef DEBUG 627 | LOGW("cannot find IPackageManager$Stub"); 628 | #endif 629 | (*env)->ExceptionClear(env); 630 | goto cleanService; 631 | } 632 | 633 | fill_asInterface(v1); 634 | fill_asInterface_signature(v2); 635 | method = (*env)->GetStaticMethodID(env, classIPackageManager$Stub, v1, v2); 636 | #ifdef DEBUG 637 | LOGI("asInterface: %p", method); 638 | #endif 639 | if (method == NULL) { 640 | (*env)->ExceptionClear(env); 641 | goto cleanClassIPackageManager$Stub; 642 | } 643 | 644 | jobject IPackageManager = (*env)->CallStaticObjectMethod(env, classIPackageManager$Stub, method, 645 | service); 646 | if (IPackageManager == NULL) { 647 | #ifdef DEBUG 648 | LOGW("cannot call asInterface"); 649 | #endif 650 | if ((*env)->ExceptionCheck(env)) { 651 | #ifdef DEBUG 652 | (*env)->ExceptionDescribe(env); 653 | #endif 654 | (*env)->ExceptionClear(env); 655 | } 656 | goto cleanClassIPackageManager$Stub; 657 | } 658 | debug(env, "IPackageManager: %s", IPackageManager); 659 | 660 | // Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo; 661 | jclass classIPackageManager = (*env)->GetObjectClass(env, IPackageManager); 662 | debug(env, "classIPackageManager: %s", classIPackageManager); 663 | fill_getApplicationInfo(v1); 664 | fill_getApplicationInfo_signature(v2); 665 | method = (*env)->GetMethodID(env, classIPackageManager, v1, v2); 666 | if (method == NULL) { 667 | (*env)->ExceptionClear(env); 668 | v2[19] = 'J'; 669 | method = (*env)->GetMethodID(env, classIPackageManager, v1, v2); 670 | } 671 | #ifdef DEBUG 672 | LOGI("getApplicationInfo: %p", method); 673 | #endif 674 | if (method == NULL) { 675 | (*env)->ExceptionClear(env); 676 | goto cleanClassIPackageManager; 677 | } 678 | 679 | jstring stringPackageName = (*env)->NewStringUTF(env, packageName); 680 | int userId = uid / 100000; 681 | jobject applicationInfo = (*env)->CallObjectMethod(env, IPackageManager, method, 682 | stringPackageName, 0, userId); 683 | #ifdef DEBUG 684 | LOGI("getApplicationInfo(%s, %d, %d): %p", packageName, 0, userId, applicationInfo); 685 | #endif 686 | debug(env, "applicationInfo: %s", applicationInfo); 687 | if (applicationInfo == NULL) { 688 | if ((*env)->ExceptionCheck(env)) { 689 | #ifdef DEBUG 690 | (*env)->ExceptionDescribe(env); 691 | #endif 692 | (*env)->ExceptionClear(env); 693 | } 694 | goto cleanStringPackageName; 695 | } 696 | 697 | jclass classApplicationInfo = (*env)->GetObjectClass(env, applicationInfo); 698 | debug(env, "class applicationInfo: %s", classApplicationInfo); 699 | 700 | fill_sourceDir(v1); 701 | fill_sourceDir_signature(v2); 702 | jfieldID field = (*env)->GetFieldID(env, classApplicationInfo, v1, v2); 703 | #ifdef DEBUG 704 | LOGI("sourceDir: %p", field); 705 | #endif 706 | if (field == NULL) { 707 | (*env)->ExceptionClear(env); 708 | goto cleanClassApplicationInfo; 709 | } 710 | jstring sourceDir = (*env)->GetObjectField(env, applicationInfo, field); 711 | debug(env, "sourceDir: %s", sourceDir); 712 | const char *chars = (*env)->GetStringUTFChars(env, sourceDir, NULL); 713 | 714 | fill_android_os_BinderProxy(v2); // 0x16 + 1 715 | jclass classBinderProxy = (*env)->FindClass(env, v2); 716 | if (classBinderProxy == NULL) { 717 | #ifdef DEBUG 718 | LOGW("cannot find BinderProxy"); 719 | #endif 720 | (*env)->ExceptionClear(env); 721 | } else { 722 | debug(env, "BinderProxy: %s", classBinderProxy); 723 | } 724 | 725 | jclass serviceClass = (*env)->GetObjectClass(env, service); 726 | debug(env, "service class: %s", serviceClass); 727 | if (classBinderProxy != NULL && !(*env)->IsSameObject(env, serviceClass, classBinderProxy)) { 728 | fill_invalid_package_manager_path_s(v2); 729 | LOGW(v2, chars); 730 | } 731 | path = strdup(chars); 732 | 733 | #ifdef DEBUG 734 | LOGI("path: %s", chars); 735 | #endif 736 | 737 | (*env)->DeleteLocalRef(env, serviceClass); 738 | if (classBinderProxy != NULL) { 739 | (*env)->DeleteLocalRef(env, classBinderProxy); 740 | } 741 | (*env)->ReleaseStringUTFChars(env, sourceDir, chars); 742 | (*env)->DeleteLocalRef(env, sourceDir); 743 | cleanClassApplicationInfo: 744 | (*env)->DeleteLocalRef(env, classApplicationInfo); 745 | (*env)->DeleteLocalRef(env, applicationInfo); 746 | cleanStringPackageName: 747 | (*env)->DeleteLocalRef(env, stringPackageName); 748 | cleanClassIPackageManager: 749 | (*env)->DeleteLocalRef(env, classIPackageManager); 750 | (*env)->DeleteLocalRef(env, IPackageManager); 751 | cleanClassIPackageManager$Stub: 752 | (*env)->DeleteLocalRef(env, classIPackageManager$Stub); 753 | cleanService: 754 | (*env)->DeleteLocalRef(env, service); 755 | cleanStringPackage: 756 | (*env)->DeleteLocalRef(env, stringPackage); 757 | cleanClassServiceManager: 758 | (*env)->DeleteLocalRef(env, classServiceManager); 759 | 760 | return path; 761 | } 762 | -------------------------------------------------------------------------------- /src/main/jni/pm.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2019/3/15. 3 | // 4 | 5 | #ifndef BREVENT_PM_H 6 | #define BREVENT_PM_H 7 | 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | char *getPath(JNIEnv *env, int uid, const char *name); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif //BREVENT_PM_H 21 | -------------------------------------------------------------------------------- /src/main/jni/xposed-nop.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2022/5/6. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "xposed-nop.h" 10 | #include "classloader.h" 11 | #include "common.h" 12 | 13 | #ifdef DEBUG_XPOSED 14 | #define debugObject logObject 15 | #else 16 | #define debugObject(...) do {} while(0) 17 | #endif 18 | 19 | static inline void fill_dalvik_system_InMemoryDexClassLoader(char v[]) { 20 | // dalvik/system/InMemoryDexClassLoader 21 | static unsigned int m = 0; 22 | 23 | if (m == 0) { 24 | m = 31; 25 | } else if (m == 37) { 26 | m = 41; 27 | } 28 | 29 | v[0x0] = 'a'; 30 | v[0x1] = 'g'; 31 | v[0x2] = 'k'; 32 | v[0x3] = '~'; 33 | v[0x4] = '`'; 34 | v[0x5] = 'a'; 35 | v[0x6] = '$'; 36 | v[0x7] = '\x7f'; 37 | v[0x8] = 't'; 38 | v[0x9] = '}'; 39 | v[0xa] = '{'; 40 | v[0xb] = 'u'; 41 | v[0xc] = '|'; 42 | v[0xd] = '='; 43 | v[0xe] = 'Z'; 44 | v[0xf] = 'z'; 45 | v[0x10] = 'X'; 46 | v[0x11] = 's'; 47 | v[0x12] = 'z'; 48 | v[0x13] = 'w'; 49 | v[0x14] = 'k'; 50 | v[0x15] = 'c'; 51 | v[0x16] = '_'; 52 | v[0x17] = 'y'; 53 | v[0x18] = 'e'; 54 | v[0x19] = ']'; 55 | v[0x1a] = 'l'; 56 | v[0x1b] = '`'; 57 | v[0x1c] = 'q'; 58 | v[0x1d] = 'p'; 59 | v[0x1e] = 'H'; 60 | v[0x1f] = 'j'; 61 | v[0x20] = 'g'; 62 | v[0x21] = 'c'; 63 | v[0x22] = 'm'; 64 | v[0x23] = '{'; 65 | for (unsigned int i = 0; i < 0x24; ++i) { 66 | v[i] ^= ((i + 0x24) % m); 67 | } 68 | v[0x24] = '\0'; 69 | } 70 | 71 | static inline void fill_InMemoryDexClassLoader_init(char v[]) { 72 | // 73 | static unsigned int m = 0; 74 | 75 | if (m == 0) { 76 | m = 5; 77 | } else if (m == 7) { 78 | m = 11; 79 | } 80 | 81 | v[0x0] = '='; 82 | v[0x1] = 'k'; 83 | v[0x2] = 'm'; 84 | v[0x3] = 'm'; 85 | v[0x4] = 't'; 86 | v[0x5] = '?'; 87 | for (unsigned int i = 0; i < 0x6; ++i) { 88 | v[i] ^= ((i + 0x6) % m); 89 | } 90 | v[0x6] = '\0'; 91 | } 92 | 93 | static inline void fill_InMemoryDexClassLoader_init_signature(char v[]) { 94 | // (Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V 95 | static unsigned int m = 0; 96 | 97 | if (m == 0) { 98 | m = 43; 99 | } else if (m == 47) { 100 | m = 53; 101 | } 102 | 103 | v[0x0] = ','; 104 | v[0x1] = 'I'; 105 | v[0x2] = 'l'; 106 | v[0x3] = 'f'; 107 | v[0x4] = '~'; 108 | v[0x5] = 'h'; 109 | v[0x6] = '%'; 110 | v[0x7] = 'e'; 111 | v[0x8] = 'e'; 112 | v[0x9] = 'b'; 113 | v[0xa] = '!'; 114 | v[0xb] = 'M'; 115 | v[0xc] = 'i'; 116 | v[0xd] = 'e'; 117 | v[0xe] = 'w'; 118 | v[0xf] = 'Q'; 119 | v[0x10] = 'a'; 120 | v[0x11] = 's'; 121 | v[0x12] = 'p'; 122 | v[0x13] = 'r'; 123 | v[0x14] = 'j'; 124 | v[0x15] = '"'; 125 | v[0x16] = 'V'; 126 | v[0x17] = 'q'; 127 | v[0x18] = '}'; 128 | v[0x19] = 'k'; 129 | v[0x1a] = '\x7f'; 130 | v[0x1b] = '0'; 131 | v[0x1c] = 'L'; 132 | v[0x1d] = '@'; 133 | v[0x1e] = 'L'; 134 | v[0x1f] = 'D'; 135 | v[0x20] = '\x0b'; 136 | v[0x21] = 'f'; 137 | v[0x22] = 'J'; 138 | v[0x23] = 'F'; 139 | v[0x24] = '['; 140 | v[0x25] = 'Z'; 141 | v[0x26] = 'f'; 142 | v[0x27] = 'o'; 143 | v[0x28] = '`'; 144 | v[0x29] = 'f'; 145 | v[0x2a] = 'f'; 146 | v[0x2b] = 'v'; 147 | v[0x2c] = '>'; 148 | v[0x2d] = '/'; 149 | v[0x2e] = 'Q'; 150 | for (unsigned int i = 0; i < 0x2f; ++i) { 151 | v[i] ^= ((i + 0x2f) % m); 152 | } 153 | v[0x2f] = '\0'; 154 | } 155 | 156 | static inline void fill_NoHook(char v[]) { 157 | // $$Hook 158 | static unsigned int m = 0; 159 | 160 | if (m == 0) { 161 | m = 5; 162 | } else if (m == 7) { 163 | m = 11; 164 | } 165 | 166 | v[0x0] = '%'; 167 | v[0x1] = '&'; 168 | v[0x2] = 'K'; 169 | v[0x3] = 'k'; 170 | v[0x4] = 'o'; 171 | v[0x5] = 'j'; 172 | for (unsigned int i = 0; i < 0x6; ++i) { 173 | v[i] ^= ((i + 0x6) % m); 174 | } 175 | v[0x6] = '\0'; 176 | } 177 | 178 | static inline void fill_hookMethod_template_signature(char v[]) { 179 | // (Ljava/lang/reflect/Member;L%s;)L%s$Unhook; 180 | static unsigned int m = 0; 181 | 182 | if (m == 0) { 183 | m = 41; 184 | } else if (m == 43) { 185 | m = 47; 186 | } 187 | 188 | v[0x0] = '*'; 189 | v[0x1] = 'O'; 190 | v[0x2] = 'n'; 191 | v[0x3] = 'd'; 192 | v[0x4] = 'p'; 193 | v[0x5] = 'f'; 194 | v[0x6] = '\''; 195 | v[0x7] = 'e'; 196 | v[0x8] = 'k'; 197 | v[0x9] = 'e'; 198 | v[0xa] = 'k'; 199 | v[0xb] = '"'; 200 | v[0xc] = '|'; 201 | v[0xd] = 'j'; 202 | v[0xe] = 'v'; 203 | v[0xf] = '}'; 204 | v[0x10] = 'w'; 205 | v[0x11] = 'p'; 206 | v[0x12] = '`'; 207 | v[0x13] = ':'; 208 | v[0x14] = '['; 209 | v[0x15] = 'r'; 210 | v[0x16] = 'u'; 211 | v[0x17] = '{'; 212 | v[0x18] = '\x7f'; 213 | v[0x19] = 'i'; 214 | v[0x1a] = '\''; 215 | v[0x1b] = 'Q'; 216 | v[0x1c] = ';'; 217 | v[0x1d] = 'l'; 218 | v[0x1e] = '\x1b'; 219 | v[0x1f] = '\x08'; 220 | v[0x20] = 'n'; 221 | v[0x21] = '\x06'; 222 | v[0x22] = 'W'; 223 | v[0x23] = '\x01'; 224 | v[0x24] = 's'; 225 | v[0x25] = 'I'; 226 | v[0x26] = '@'; 227 | v[0x27] = 'o'; 228 | v[0x28] = 'n'; 229 | v[0x29] = 'i'; 230 | v[0x2a] = '8'; 231 | for (unsigned int i = 0; i < 0x2b; ++i) { 232 | v[i] ^= ((i + 0x2b) % m); 233 | } 234 | v[0x2b] = '\0'; 235 | } 236 | 237 | static inline void fill_xposedBridge_template(char v[]) { 238 | // %s/XposedBridge 239 | static unsigned int m = 0; 240 | 241 | if (m == 0) { 242 | m = 13; 243 | } else if (m == 17) { 244 | m = 19; 245 | } 246 | 247 | v[0x0] = '\''; 248 | v[0x1] = 'p'; 249 | v[0x2] = '+'; 250 | v[0x3] = ']'; 251 | v[0x4] = 'v'; 252 | v[0x5] = 'h'; 253 | v[0x6] = '{'; 254 | v[0x7] = 'l'; 255 | v[0x8] = 'n'; 256 | v[0x9] = 'I'; 257 | v[0xa] = '~'; 258 | v[0xb] = 'i'; 259 | v[0xc] = 'e'; 260 | v[0xd] = 'e'; 261 | v[0xe] = 'f'; 262 | for (unsigned int i = 0; i < 0xf; ++i) { 263 | v[i] ^= ((i + 0xf) % m); 264 | } 265 | v[0xf] = '\0'; 266 | } 267 | 268 | static inline void fill_hookMethod(char v[]) { 269 | // hookMethod 270 | static unsigned int m = 0; 271 | 272 | if (m == 0) { 273 | m = 7; 274 | } else if (m == 11) { 275 | m = 13; 276 | } 277 | 278 | v[0x0] = 'k'; 279 | v[0x1] = 'k'; 280 | v[0x2] = 'j'; 281 | v[0x3] = 'm'; 282 | v[0x4] = 'M'; 283 | v[0x5] = 'd'; 284 | v[0x6] = 'v'; 285 | v[0x7] = 'k'; 286 | v[0x8] = 'k'; 287 | v[0x9] = 'a'; 288 | for (unsigned int i = 0; i < 0xa; ++i) { 289 | v[i] ^= ((i + 0xa) % m); 290 | } 291 | v[0xa] = '\0'; 292 | } 293 | 294 | jobject xposedNop(JNIEnv *env, jclass xposedClassXcMethodHook) { 295 | jobject noHook = nullptr; 296 | if (getSdk() < __ANDROID_API_O__) { 297 | return noHook; 298 | } 299 | #ifdef DEBUG_XPOSED 300 | LOGI("methodClassGetName: %p", methodClassGetName); 301 | #endif 302 | debugObject(env, "xposedClassXcMethodHook: %s", xposedClassXcMethodHook); 303 | auto xposedClassNameAsString = (jstring) env->CallObjectMethod(xposedClassXcMethodHook, methodClassGetName); 304 | debugObject(env, "xposedClassNameAsString: %s", xposedClassNameAsString); 305 | const char *xposedClassNameAsChars = env->GetStringUTFChars(xposedClassNameAsString, nullptr); 306 | char *xposedClassName = strdup(xposedClassNameAsChars); 307 | #ifdef DEBUG_XPOSED 308 | LOGI("xposedClassName: %s", xposedClassName); 309 | #endif 310 | env->ReleaseStringUTFChars(xposedClassNameAsString, xposedClassNameAsChars); 311 | env->DeleteLocalRef(xposedClassNameAsString); 312 | uint8_t bytes[] = { 313 | 0x7b, 0x7a, 0x67, 0x15, 0x2f, 0x2c, 0x2a, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 314 | 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 315 | 0xbb, 0x1e, 0x1f, 0x1f, 0x6f, 0x1f, 0x1f, 0x1f, 0x67, 0x49, 0x2b, 0x0d, 0x1f, 0x1f, 0x1f, 0x1f, 316 | 0x1f, 0x1f, 0x1f, 0x1f, 0x37, 0x1e, 0x1f, 0x1f, 0x1b, 0x1f, 0x1f, 0x1f, 0x6f, 0x1f, 0x1f, 0x1f, 317 | 0x1c, 0x1f, 0x1f, 0x1f, 0x9f, 0x1f, 0x1f, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f, 0x93, 0x1f, 0x1f, 0x1f, 318 | 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1d, 0x1f, 0x1f, 0x1f, 0x87, 0x1f, 0x1f, 0x1f, 319 | 0x1e, 0x1f, 0x1f, 0x1f, 0xb7, 0x1f, 0x1f, 0x1f, 0xc3, 0x1f, 0x1f, 0x1f, 0xd7, 0x1f, 0x1f, 0x1f, 320 | 0xff, 0x1f, 0x1f, 0x1f, 0xf7, 0x1f, 0x1f, 0x1f, 0xed, 0x1f, 0x1f, 0x1f, 0x05, 0x1e, 0x1f, 0x1f, 321 | 0x1e, 0x1f, 0x1f, 0x1f, 0x1d, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 322 | 0x1d, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 323 | 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f, 324 | 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xe0, 0xe0, 0xe0, 0xe0, 0x1f, 0x1f, 0x1f, 0x1f, 325 | 0x02, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1e, 0x1f, 0x1e, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f, 326 | 0x1f, 0x1f, 0x1f, 0x1f, 0x1b, 0x1f, 0x1f, 0x1f, 0x6f, 0x0f, 0x1e, 0x1f, 0x1f, 0x1f, 0x11, 0x1f, 327 | 0x19, 0x23, 0x76, 0x71, 0x76, 0x6b, 0x21, 0x1f, 0x17, 0x53, 0x51, 0x70, 0x57, 0x70, 0x70, 0x74, 328 | 0x24, 0x1f, 0x39, 0x53, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 329 | 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 330 | 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x24, 0x1f, 0x1e, 0x49, 0x1f, 0x1f, 0x1f, 0x1e, 331 | 0x1f, 0x1f, 0x9e, 0x9f, 0x1b, 0xd7, 0x1e, 0x1f, 0x15, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 332 | 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f, 0x1b, 0x1f, 0x1f, 0x1f, 333 | 0x6f, 0x1f, 0x1f, 0x1f, 0x1d, 0x1f, 0x1f, 0x1f, 0x1c, 0x1f, 0x1f, 0x1f, 0x9f, 0x1f, 0x1f, 0x1f, 334 | 0x1c, 0x1f, 0x1f, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f, 0x93, 0x1f, 0x1f, 0x1f, 0x1a, 0x1f, 0x1f, 0x1f, 335 | 0x1d, 0x1f, 0x1f, 0x1f, 0x87, 0x1f, 0x1f, 0x1f, 0x19, 0x1f, 0x1f, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f, 336 | 0xb7, 0x1f, 0x1f, 0x1f, 0x1e, 0x3f, 0x1f, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f, 0xd7, 0x1f, 0x1f, 0x1f, 337 | 0x1d, 0x3f, 0x1f, 0x1f, 0x1b, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x3f, 0x1f, 0x1f, 338 | 0x1e, 0x1f, 0x1f, 0x1f, 0x02, 0x1e, 0x1f, 0x1f, 0x1f, 0x0f, 0x1f, 0x1f, 0x1e, 0x1f, 0x1f, 0x1f, 339 | 0x37, 0x1e, 0x1f, 0x1f 340 | }; 341 | for (size_t i = 0; i < sizeof(bytes); ++i) { 342 | bytes[i] ^= 0x1f; 343 | } 344 | char *x = xposedClassName; 345 | while (*x) { 346 | if (*x == '.') { 347 | *x = '/'; 348 | } 349 | ++x; 350 | } 351 | if (x - xposedClassName != 36) { 352 | goto clean; 353 | } 354 | { 355 | #ifdef DEBUG_XPOSED 356 | LOGI("xposedClassName with /: %s, dex length: %lu", xposedClassName, sizeof(bytes)); 357 | #endif 358 | char v[0x40], v2[0x40]; 359 | fill_NoHook(v); 360 | memcpy(bytes + 0xea, v, 0x6); 361 | jstring classNoHookAsString = env->NewStringUTF(v); 362 | memcpy(bytes + 0xf4, xposedClassName, x - xposedClassName); 363 | uint32_t adler_checksum; 364 | adler_checksum = adler32(0L, Z_NULL, 0); 365 | adler_checksum = adler32(adler_checksum, bytes + 32, sizeof(bytes) - 32); 366 | memcpy(bytes + 28, &adler_checksum, 0x4); 367 | struct timespec tv; 368 | clock_gettime(CLOCK_REALTIME, &tv); 369 | memcpy(bytes + 12, &tv, sizeof(tv) > 20 ? 20 : sizeof(tv)); 370 | adler_checksum = adler32(0L, Z_NULL, 0); 371 | adler_checksum = adler32(adler_checksum, bytes + 12, sizeof(bytes) - 12); 372 | memcpy(bytes + 8, &adler_checksum, 0x4); 373 | auto byteBuffer = env->NewDirectByteBuffer(bytes, sizeof(bytes)); 374 | debugObject(env, "byteBuffer: %s", byteBuffer); 375 | auto classLoader = env->CallObjectMethod(xposedClassXcMethodHook, methodClassGetClassLoader); 376 | debugObject(env, "classLoader: %s", classLoader); 377 | fill_dalvik_system_InMemoryDexClassLoader(v); 378 | jclass classInMemoryDexClassLoader = env->FindClass(v); 379 | debugObject(env, "InMemoryDexClassLoader: %s", classInMemoryDexClassLoader); 380 | fill_InMemoryDexClassLoader_init(v); 381 | fill_InMemoryDexClassLoader_init_signature(v2); 382 | jmethodID methodInMemoryDexClassLoaderInit = env->GetMethodID(classInMemoryDexClassLoader, v, v2); 383 | #ifdef DEBUG_XPOSED 384 | LOGI("methodInMemoryDexClassLoaderInit: %p", methodInMemoryDexClassLoaderInit); 385 | #endif 386 | jobject classNoHookClassLoader = env->NewObject(classInMemoryDexClassLoader, methodInMemoryDexClassLoaderInit, byteBuffer, classLoader); 387 | debugObject(env, "classNoHookClassLoader: %s", classNoHookClassLoader); 388 | debugObject(env, "classNoHookAsString: %s", classNoHookAsString); 389 | jclass classNoHook = (jclass) env->CallObjectMethod(classNoHookClassLoader, methodClassLoaderLoadClass, classNoHookAsString); 390 | debugObject(env, "classNoHook: %s", classNoHook); 391 | noHook = env->AllocObject(classNoHook); 392 | debugObject(env, "noHook: %s", noHook); 393 | if (methodNop != nullptr) { 394 | char signature[0x80]; 395 | fill_hookMethod_template_signature(v); 396 | sprintf(signature, v, xposedClassName, xposedClassName); 397 | char *lastSlash = strrchr(xposedClassName, '/'); 398 | *lastSlash = '\0'; 399 | char name[0x40]; 400 | fill_xposedBridge_template(v); 401 | sprintf(name, v, xposedClassName); 402 | #ifdef DEBUG_XPOSED 403 | LOGI("name: %s, signature: %s", name, signature); 404 | #endif 405 | jstring classXposedBridgeAsString = env->NewStringUTF(name); 406 | jclass classXposedBridge = (jclass) env->CallObjectMethod(classLoader, methodClassLoaderLoadClass, classXposedBridgeAsString); 407 | if (env->ExceptionCheck()) { 408 | env->ExceptionClear(); 409 | } 410 | debugObject(env, "XposedBridge: %s", classXposedBridge); 411 | jmethodID hookMethod = nullptr; 412 | if (classXposedBridge != nullptr) { 413 | fill_hookMethod(v); 414 | hookMethod = env->GetStaticMethodID(classXposedBridge, v, signature); 415 | } 416 | if (env->ExceptionCheck()) { 417 | env->ExceptionClear(); 418 | } 419 | #ifdef DEBUG_XPOSED 420 | LOGI("hookMethod: %p", hookMethod); 421 | #endif 422 | if (hookMethod != nullptr) { 423 | env->CallStaticVoidMethod(classXposedBridge, hookMethod, nullptr, nullptr); 424 | if (env->ExceptionCheck()) { 425 | #ifdef DEBUG_XPOSED 426 | env->ExceptionDescribe(); 427 | #endif 428 | env->ExceptionClear(); 429 | } 430 | memcpy(hookMethod, methodNop, artMethodSize); 431 | } 432 | env->DeleteLocalRef(classXposedBridge); 433 | env->DeleteLocalRef(classXposedBridgeAsString); 434 | } 435 | env->DeleteLocalRef(classNoHook); 436 | env->DeleteLocalRef(classNoHookAsString); 437 | env->DeleteLocalRef(classNoHookClassLoader); 438 | env->DeleteLocalRef(classInMemoryDexClassLoader); 439 | env->DeleteLocalRef(classLoader); 440 | env->DeleteLocalRef(byteBuffer); 441 | } 442 | clean: 443 | free(xposedClassName); 444 | return noHook; 445 | } -------------------------------------------------------------------------------- /src/main/jni/xposed-nop.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Thom on 2022/5/6. 3 | // 4 | 5 | #ifndef BREVENT_XPOSED_NOP_H 6 | #define BREVENT_XPOSED_NOP_H 7 | 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | jobject xposedNop(JNIEnv *env, jclass xposedClassXcMethodHook); 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | #endif //BREVENT_XPOSED_NOP_H 21 | -------------------------------------------------------------------------------- /src/main/res/values-zh-rCN/genuine.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | genuine: 仅供测试 5 | false: 系统修改 6 | fake: 签名错误 7 | overlay: 资源覆盖 8 | odex: 文件篡改 9 | dex: 应用注入 10 | proxy: 虚拟框架 11 | error: 检查错误 12 | fatal: 严重劫持 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/res/values-zh-rCN/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 请勿破解或自动化测试。 3 | 4 | -------------------------------------------------------------------------------- /src/main/res/values-zh-rTW/genuine.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | genuine: 僅供測試 5 | false: 系統修改 6 | fake: 簽名錯誤 7 | overlay: 資源覆蓋 8 | odex: 文件篡改 9 | dex: 程式侵入 10 | proxy: 虛擬框架 11 | error: 檢查錯誤 12 | fatal: 嚴重劫持 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/res/values-zh-rTW/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 請勿破解或自動化測試。 3 | 4 | -------------------------------------------------------------------------------- /src/main/res/values/genuine.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | genuine: pass 5 | false: modified 6 | fake: signature 7 | overlay: third-party resources 8 | odex: odex tampering 9 | dex: third-party inject 10 | proxy: virtual platform 11 | error: cannot check 12 | fatal: inline hook 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Don\'t crack, nor mock! 3 | 4 | --------------------------------------------------------------------------------