├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── lib │ └── XposedBridgeApi-54.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── smartdone │ │ └── dexdump │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── xposed_init │ ├── java │ │ └── com │ │ │ └── smartdone │ │ │ └── dexdump │ │ │ ├── Appinfo.java │ │ │ ├── Config.java │ │ │ ├── DensityUtil.java │ │ │ ├── Dumpper.java │ │ │ ├── Main.java │ │ │ ├── MainActivity.java │ │ │ └── PermisionUtils.java │ ├── jni │ │ ├── Android.mk │ │ ├── Application.mk │ │ ├── dlopen.c │ │ ├── dlopen.h │ │ ├── dump.cpp │ │ ├── dump.h │ │ ├── include │ │ │ └── inlineHook.h │ │ ├── inlineHook.c │ │ ├── relocate.c │ │ └── relocate.h │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ └── item.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── smartdone │ └── dexdump │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dexdump 2 | 3 | # 已经用纯frida实现了,frida版本的会去遍历所有的类,并且加载,对于抽类的,能够修复那种执行的时候把代码填充回dex的 4 | [https://github.com/smartdone/Frida-Scripts/tree/master/unpack](https://github.com/smartdone/Frida-Scripts/tree/master/unpack) 5 | > 还是frida方便,环境配置简单,喜欢请点star哦 6 | 7 | ## 感谢 8 | - `ele7enxxh`大佬的[Android-Inline-Hook](https://github.com/ele7enxxh/Android-Inline-Hook) 9 | - `rrrfff`大佬的[ndk_dlopen](https://github.com/rrrfff/ndk_dlopen) 10 | 11 | # Usage 12 | 13 | 1. 把libhook.so复制到/data/local/tmp下面,并且更改权限为777 (`chmod 777 /data/local/tmp/libhook.so`) 14 | 2. 安装xposed插件 15 | 3. 激活插件并且重启手机 16 | 4. 在xposed插件的界面里面勾选你需要脱壳的应用 17 | 5. 干掉你需要脱壳的应用的进程,然后重新启动应用 (`kill -9 {pid}`) 18 | 6. 如果脱壳成功的话,就会在应用的`/data/data/{packagename}`下面写入dex文件 19 | 7. `grep {activity name} -r *` 筛选出dex 20 | 21 | # Notice 22 | 23 | > 在5.x,6.0目前正常。 24 | 25 | - 根据[@jwchen119](https://github.com/jwchen119)大佬的建议,写的6.0 26 | 27 | - 7.x应该有问题,刚弄了一个7.x的设备,最近跟新一下 28 | 29 | - 4.4及以下的也的Dalvik也没有试,可以正常使用了会在这个地方声明 30 | 31 | - 感谢大家 32 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion '26.0.2' 6 | defaultConfig { 7 | applicationId "com.smartdone.dexdump" 8 | minSdkVersion 15 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | 14 | externalNativeBuild { 15 | ndkBuild { 16 | abiFilters "armeabi", "armeabi-v7a" 17 | } 18 | } 19 | 20 | 21 | } 22 | sourceSets.main { 23 | jni.srcDirs 'src/main/jni' 24 | } 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | externalNativeBuild { 32 | ndkBuild { 33 | path 'src/main/jni/Android.mk' 34 | } 35 | } 36 | } 37 | 38 | dependencies { 39 | compile fileTree(include: ['*.jar'], dir: 'libs') 40 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 41 | exclude group: 'com.android.support', module: 'support-annotations' 42 | }) 43 | compile 'com.android.support:appcompat-v7:26.+' 44 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 45 | testCompile 'junit:junit:4.12' 46 | provided files('lib/XposedBridgeApi-54.jar') 47 | } 48 | -------------------------------------------------------------------------------- /app/lib/XposedBridgeApi-54.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smartdone/dexdump/7cc4a427b0e9377829de020ecf037942eb74f0c6/app/lib/XposedBridgeApi-54.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/smartdone/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/smartdone/dexdump/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.smartdone.dexdump; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.smartdone.dexdump", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 19 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | com.smartdone.dexdump.Main -------------------------------------------------------------------------------- /app/src/main/java/com/smartdone/dexdump/Appinfo.java: -------------------------------------------------------------------------------- 1 | package com.smartdone.dexdump; 2 | 3 | import android.graphics.drawable.Drawable; 4 | 5 | /** 6 | * Created by smartdone on 2017/7/2. 7 | */ 8 | 9 | public class Appinfo { 10 | private String appName; 11 | private String appPackage; 12 | private boolean isChecked; 13 | private Drawable icon; 14 | 15 | public Drawable getIcon() { 16 | return icon; 17 | } 18 | 19 | public void setIcon(Drawable icon) { 20 | this.icon = icon; 21 | } 22 | 23 | public Appinfo() { 24 | 25 | } 26 | 27 | public Appinfo(String appName, String appPackage) { 28 | this.appName = appName; 29 | this.appPackage = appPackage; 30 | } 31 | 32 | public String getAppName() { 33 | return appName; 34 | } 35 | 36 | public void setAppName(String appName) { 37 | this.appName = appName; 38 | } 39 | 40 | public String getAppPackage() { 41 | return appPackage; 42 | } 43 | 44 | public void setAppPackage(String appPackage) { 45 | this.appPackage = appPackage; 46 | } 47 | 48 | public boolean isChecked() { 49 | return isChecked; 50 | } 51 | 52 | public void setChecked(boolean checked) { 53 | this.isChecked = checked; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/smartdone/dexdump/Config.java: -------------------------------------------------------------------------------- 1 | package com.smartdone.dexdump; 2 | 3 | import android.os.Environment; 4 | import android.util.Log; 5 | 6 | import org.json.JSONArray; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.io.FileOutputStream; 12 | import java.io.InputStreamReader; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * Created by smartdone on 2017/7/2. 18 | */ 19 | 20 | public class Config { 21 | private static final String TAG = "DEX_DUMP"; 22 | private static final String FILENAME = Environment.getExternalStorageDirectory().getPath() + File.separator + "dumdex.js"; 23 | 24 | private static void writeConfig(String s) { 25 | try { 26 | FileOutputStream fout = new FileOutputStream(FILENAME); 27 | fout.write(s.getBytes("utf-8")); 28 | fout.flush(); 29 | fout.close(); 30 | Log.e("DEX_DUMP", "write file"); 31 | } catch (Exception e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | 36 | public static void addOne(String name) { 37 | List ori = getConfig(); 38 | if(ori == null) { 39 | JSONArray jsonArray = new JSONArray(); 40 | jsonArray.put(name); 41 | writeConfig(jsonArray.toString()); 42 | }else { 43 | ori.add(name); 44 | JSONArray jsonArray = new JSONArray(); 45 | for(String o : ori) { 46 | jsonArray.put(o); 47 | } 48 | writeConfig(jsonArray.toString()); 49 | } 50 | } 51 | 52 | public static void removeOne(String name) { 53 | List ori = getConfig(); 54 | if(ori != null) { 55 | for(int i = 0; i < ori.size(); i++) { 56 | if(ori.get(i).equals(name)) { 57 | ori.remove(i); 58 | } 59 | } 60 | JSONArray jsonArray = new JSONArray(); 61 | for(String s : ori) { 62 | jsonArray.put(s); 63 | } 64 | writeConfig(jsonArray.toString()); 65 | } 66 | } 67 | 68 | public static List getConfig() { 69 | File file = new File(FILENAME); 70 | if (file.exists() && file.canRead()) { 71 | try { 72 | BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file))); 73 | String line = br.readLine(); 74 | JSONArray jsonArray = new JSONArray(line); 75 | List apps = new ArrayList<>(); 76 | for(int i = 0; i < jsonArray.length(); i++) { 77 | apps.add(jsonArray.getString(i)); 78 | } 79 | br.close(); 80 | // Log.e("DEX_DUMP", "需要hook的列表: " + line); 81 | return apps; 82 | } catch (Exception e) { 83 | e.printStackTrace(); 84 | } 85 | } else { 86 | try { 87 | if (!file.exists()) 88 | file.createNewFile(); 89 | if(!file.canRead()) 90 | Log.e(TAG, "文件没有读取权限"); 91 | } catch (Exception e) { 92 | e.printStackTrace(); 93 | } 94 | } 95 | return null; 96 | } 97 | 98 | public static boolean contains(List lists, String name) { 99 | if(lists == null) { 100 | return false; 101 | } 102 | for(String l : lists) { 103 | if(l.equals(name)) { 104 | return true; 105 | } 106 | } 107 | return false; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /app/src/main/java/com/smartdone/dexdump/DensityUtil.java: -------------------------------------------------------------------------------- 1 | package com.smartdone.dexdump; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * Created by root on 2017/7/5. 7 | */ 8 | 9 | public class DensityUtil { 10 | /** 11 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 12 | */ 13 | public static int dip2px(Context context, float dpValue) { 14 | final float scale = context.getResources().getDisplayMetrics().density; 15 | return (int) (dpValue * scale + 0.5f); 16 | } 17 | 18 | /** 19 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 20 | */ 21 | public static int px2dip(Context context, float pxValue) { 22 | final float scale = context.getResources().getDisplayMetrics().density; 23 | return (int) (pxValue / scale + 0.5f); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/smartdone/dexdump/Dumpper.java: -------------------------------------------------------------------------------- 1 | package com.smartdone.dexdump; 2 | 3 | /** 4 | * Created by smartdone on 2017/7/1. 5 | */ 6 | 7 | public class Dumpper { 8 | public static native void dump(); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/smartdone/dexdump/Main.java: -------------------------------------------------------------------------------- 1 | package com.smartdone.dexdump; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import java.util.List; 7 | 8 | import de.robv.android.xposed.IXposedHookLoadPackage; 9 | import de.robv.android.xposed.XC_MethodHook; 10 | import de.robv.android.xposed.XposedBridge; 11 | import de.robv.android.xposed.XposedHelpers; 12 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 13 | 14 | /** 15 | * Created by smartdone on 2017/7/1. 16 | */ 17 | 18 | public class Main implements IXposedHookLoadPackage { 19 | private static final String TAG = "DEX_DUMP"; 20 | @Override 21 | public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { 22 | 23 | List hooklist = Config.getConfig(); 24 | 25 | if(!Config.contains(hooklist, loadPackageParam.packageName)) 26 | return; 27 | 28 | XposedBridge.log("对" + loadPackageParam.packageName + "进行处理"); 29 | Log.e(TAG, "开始处理: " + loadPackageParam.packageName); 30 | 31 | try{ 32 | System.load("/data/local/tmp/libhook.so"); 33 | }catch (Exception e) { 34 | Log.e(TAG, "加载动态库失败:" + e.getMessage()); 35 | } 36 | Log.e(TAG, "加载动态库成功"); 37 | XposedHelpers.findAndHookMethod("android.app.Application", loadPackageParam.classLoader, "attach", Context.class, new XC_MethodHook() { 38 | private Context context; 39 | @Override 40 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 41 | super.beforeHookedMethod(param); 42 | Dumpper.dump(); 43 | } 44 | 45 | @Override 46 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 47 | super.afterHookedMethod(param); 48 | } 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/smartdone/dexdump/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.smartdone.dexdump; 2 | 3 | import android.content.Intent; 4 | import android.content.pm.ApplicationInfo; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.PackageManager; 7 | import android.content.pm.ResolveInfo; 8 | import android.graphics.PixelFormat; 9 | import android.graphics.drawable.BitmapDrawable; 10 | import android.graphics.drawable.Drawable; 11 | import android.support.v7.app.AppCompatActivity; 12 | import android.os.Bundle; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.BaseAdapter; 17 | import android.widget.CheckBox; 18 | import android.widget.CompoundButton; 19 | import android.widget.ImageView; 20 | import android.widget.ListView; 21 | import android.widget.TextView; 22 | import android.graphics.Bitmap; 23 | import android.graphics.Canvas; 24 | import android.graphics.Matrix; 25 | 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | public class MainActivity extends AppCompatActivity { 31 | 32 | static { 33 | System.loadLibrary("hook"); 34 | } 35 | 36 | private ListView listView; 37 | private AppAdapter adapter; 38 | private List appinfos; 39 | private List selected; 40 | 41 | @Override 42 | protected void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_main); 45 | PermisionUtils.verifyStoragePermissions(this); 46 | Dumpper.dump(); 47 | selected = Config.getConfig(); 48 | appinfos = new ArrayList<>(); 49 | 50 | listView = findViewById(R.id.applist); 51 | adapter = new AppAdapter(appinfos); 52 | listView.setAdapter(adapter); 53 | getInstallAppList(); 54 | adapter.notifyDataSetChanged(); 55 | 56 | } 57 | 58 | 59 | private void getInstallAppList() { 60 | try { 61 | if(getApiLevel() <= 23) { 62 | List packageInfos = getPackageManager().getInstalledPackages(0); 63 | for (PackageInfo packageInfo : packageInfos) { 64 | if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 65 | Appinfo info = new Appinfo(); 66 | info.setIcon(zoomDrawable(packageInfo.applicationInfo.loadIcon(getPackageManager()), DensityUtil.dip2px(this, 128), DensityUtil.dip2px(this, 128))); 67 | info.setAppName(packageInfo.applicationInfo.loadLabel(getPackageManager()).toString()); 68 | info.setAppPackage(packageInfo.packageName); 69 | if (Config.contains(selected, info.getAppPackage())) { 70 | info.setChecked(true); 71 | } else { 72 | info.setChecked(false); 73 | } 74 | appinfos.add(info); 75 | } 76 | } 77 | } else { 78 | Intent startupIntent = new Intent(Intent.ACTION_MAIN); 79 | startupIntent.addCategory(Intent.CATEGORY_LAUNCHER); 80 | final PackageManager pm = getPackageManager(); 81 | List activities = pm.queryIntentActivities(startupIntent, 0); 82 | for(ResolveInfo resolveInfo : activities) { 83 | Appinfo appinfo = new Appinfo(); 84 | appinfo.setIcon(zoomDrawable(resolveInfo.loadIcon(pm), DensityUtil.dip2px(this, 128), DensityUtil.dip2px(this, 128))); 85 | appinfo.setAppName(resolveInfo.loadLabel(pm).toString()); 86 | appinfo.setAppPackage(resolveInfo.activityInfo.packageName); 87 | 88 | if (Config.contains(selected, appinfo.getAppPackage())) { 89 | appinfo.setChecked(true); 90 | } else { 91 | appinfo.setChecked(false); 92 | } 93 | appinfos.add(appinfo); 94 | } 95 | } 96 | // adapter.notifyDataSetChanged(); 97 | } catch (Exception e) { 98 | e.printStackTrace(); 99 | } 100 | } 101 | 102 | private int getApiLevel() { 103 | return Integer.parseInt(android.os.Build.VERSION.SDK); 104 | } 105 | 106 | class AppAdapter extends BaseAdapter { 107 | 108 | private List appinfos; 109 | 110 | private AppAdapter(List appinfos) { 111 | this.appinfos = appinfos; 112 | } 113 | 114 | @Override 115 | public int getCount() { 116 | return appinfos.size(); 117 | } 118 | 119 | @Override 120 | public Object getItem(int i) { 121 | return appinfos.get(i); 122 | } 123 | 124 | @Override 125 | public long getItemId(int i) { 126 | return i; 127 | } 128 | 129 | @Override 130 | public View getView(int i, View view, ViewGroup viewGroup) { 131 | View v = LayoutInflater.from(MainActivity.this).inflate(R.layout.item, null); 132 | final int posi = i; 133 | final TextView appname = v.findViewById(R.id.tv_appname); 134 | appname.setText(appinfos.get(i).getAppName()); 135 | TextView appPackage = v.findViewById(R.id.tv_apppackage); 136 | appPackage.setText(appinfos.get(i).getAppPackage()); 137 | CheckBox checkBox = v.findViewById(R.id.cb_select); 138 | ImageView icon = v.findViewById(R.id.iv_icon); 139 | icon.setImageDrawable(appinfos.get(i).getIcon()); 140 | if (appinfos.get(i).isChecked()) { 141 | checkBox.setChecked(true); 142 | } else { 143 | checkBox.setChecked(false); 144 | } 145 | checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 146 | @Override 147 | public void onCheckedChanged(CompoundButton compoundButton, boolean b) { 148 | if (b) { 149 | Config.addOne(appinfos.get(posi).getAppPackage()); 150 | } else { 151 | Config.removeOne(appinfos.get(posi).getAppPackage()); 152 | } 153 | } 154 | }); 155 | return v; 156 | } 157 | } 158 | 159 | private Drawable zoomDrawable(Drawable drawable, int w, int h) { 160 | int width = drawable.getIntrinsicWidth(); 161 | int height = drawable.getIntrinsicHeight(); 162 | Bitmap oldbmp = drawableToBitmap(drawable); 163 | Matrix matrix = new Matrix(); 164 | float scaleWidth = ((float) w / width); 165 | float scaleHeight = ((float) h / height); 166 | matrix.postScale(scaleWidth, scaleHeight); 167 | Bitmap newbmp = Bitmap.createBitmap(oldbmp, 0, 0, width, height, 168 | matrix, true); 169 | return new BitmapDrawable(null, newbmp); 170 | } 171 | 172 | private Bitmap drawableToBitmap(Drawable drawable) { 173 | int width = drawable.getIntrinsicWidth(); 174 | int height = drawable.getIntrinsicHeight(); 175 | Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 176 | : Bitmap.Config.RGB_565; 177 | Bitmap bitmap = Bitmap.createBitmap(width, height, config); 178 | Canvas canvas = new Canvas(bitmap); 179 | drawable.setBounds(0, 0, width, height); 180 | drawable.draw(canvas); 181 | return bitmap; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /app/src/main/java/com/smartdone/dexdump/PermisionUtils.java: -------------------------------------------------------------------------------- 1 | package com.smartdone.dexdump; 2 | 3 | /** 4 | * 来源 http://blog.csdn.net/wi2rfl78/article/details/78314286 5 | */ 6 | 7 | import android.Manifest; 8 | import android.app.Activity; 9 | import android.content.pm.PackageManager; 10 | import android.support.v4.app.ActivityCompat; 11 | 12 | public class PermisionUtils { 13 | 14 | // Storage Permissions 15 | private static final int REQUEST_EXTERNAL_STORAGE = 1; 16 | private static String[] PERMISSIONS_STORAGE = { 17 | Manifest.permission.READ_EXTERNAL_STORAGE, 18 | Manifest.permission.WRITE_EXTERNAL_STORAGE}; 19 | 20 | /** 21 | * Checks if the app has permission to write to device storage 22 | * If the app does not has permission then the user will be prompted to 23 | * grant permissions 24 | * 25 | * @param activity 26 | */ 27 | public static void verifyStoragePermissions(Activity activity) { 28 | // Check if we have write permission 29 | int permission = ActivityCompat.checkSelfPermission(activity, 30 | Manifest.permission.WRITE_EXTERNAL_STORAGE); 31 | 32 | if (permission != PackageManager.PERMISSION_GRANTED) { 33 | // We don't have permission so prompt the user 34 | ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, 35 | REQUEST_EXTERNAL_STORAGE); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2009 The Android Open Source Project 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # 16 | # 17 | LOCAL_PATH := $(call my-dir) 18 | 19 | include $(CLEAR_VARS) 20 | LOCAL_MODULE := hook 21 | LOCAL_SRC_FILES := inlineHook.c relocate.c dlopen.c dump.cpp 22 | LOCAL_LDLIBS := -llog 23 | include $(BUILD_SHARED_LIBRARY) 24 | -------------------------------------------------------------------------------- /app/src/main/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi armeabi-v7a 2 | APP_PIE:= true 3 | APP_STL := stlport_static -------------------------------------------------------------------------------- /app/src/main/jni/dlopen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * @author : rrrfff@foxmail.com 4 | * https://github.com/rrrfff/ndk_dlopen 5 | * 6 | */ 7 | #include "dlopen.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define LOG_TAG "ndk_dlopen" 17 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 18 | 19 | static volatile int SDK_INT = 0; 20 | static void *quick_on_stack_back; 21 | static union 22 | { 23 | void *generic_stub; 24 | void *(*quick_on_stack_replace)(const void *param1, const void *param2, 25 | const void *fake_trampoline, const void *called); 26 | } STUBS; 27 | 28 | void JNIEXPORT ndk_init(JNIEnv *env) 29 | { 30 | if (SDK_INT <= 0) { 31 | char sdk[PROP_VALUE_MAX]; 32 | __system_property_get("ro.build.version.sdk", sdk); 33 | SDK_INT = atoi(sdk); 34 | LOGI("SDK_INT = %d", SDK_INT); 35 | if (SDK_INT >= 24) { 36 | static __attribute__((__aligned__(PAGE_SIZE))) uint8_t __insns[PAGE_SIZE]; 37 | STUBS.generic_stub = __insns; 38 | mprotect(__insns, sizeof(__insns), PROT_READ | PROT_WRITE | PROT_EXEC); 39 | 40 | // we are currently hijacking "FatalError" as a fake system-call trampoline 41 | uintptr_t pv = (uintptr_t)(*env)->FatalError; 42 | uintptr_t pu = (pv | (PAGE_SIZE - 1)) + 1u; 43 | uintptr_t pd = (pv & ~(PAGE_SIZE - 1)); 44 | mprotect((void *)pd, pv + 8u >= pu ? PAGE_SIZE * 2u : PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC); 45 | quick_on_stack_back = (void *)pv; 46 | 47 | #if defined(__i386__) 48 | /* 49 | DEFINE_FUNCTION art_quick_on_stack_replace 50 | movl 12(REG_VAR(esp)), REG_VAR(eax) 51 | movl (REG_VAR(esp)), REG_VAR(edx) 52 | movl REG_VAR(eax), (REG_VAR(esp)) 53 | movl REG_VAR(edx), 12(REG_VAR(esp)) 54 | pushl 16(REG_VAR(esp)) 55 | ret 56 | END_FUNCTION art_quick_on_stack_replace 57 | */ 58 | memcpy(__insns, "\x8B\x44\x24\x0C\x8B\x14\x24\x89\x04\x24\x89\x54\x24\x0C\xFF\x74\x24\x10\xC3", 19); 59 | /* 60 | DEFINE_FUNCTION art_quick_on_stack_back 61 | push 8(REG_VAR(esp)) 62 | ret 63 | END_FUNCTION art_quick_on_stack_back 64 | */ 65 | memcpy(quick_on_stack_back, "\xC3\xFF\x74\x24\x08\xC3", 6); 66 | quick_on_stack_back = (void *)(pv + 1); // inserts `ret` at first 67 | #elif defined(__x86_64__) 68 | // rdi, rsi, rdx, rcx, r8, r9 69 | /* 70 | 0x0000000000000000: 52 push rdx 71 | 0x0000000000000001: FF E1 jmp rcx 72 | */ 73 | memcpy(__insns, "\x52\xFF\xE1", 3); 74 | /* 75 | 0x0000000000000000: C3 ret 76 | */ 77 | memcpy(quick_on_stack_back, "\xC3", 1); 78 | #elif defined(__aarch64__) 79 | // x0~x7 80 | /* 81 | 0x0000000000000000: FD 7B BF A9 stp x29, x30, [sp, #-0x10]! 82 | 0x0000000000000004: FD 03 00 91 mov x29, sp 83 | 0x0000000000000008: FE 03 02 AA mov x30, x2 84 | 0x000000000000000C: 60 00 1F D6 br x3 85 | */ 86 | memcpy(__insns, "\xFD\x7B\xBF\xA9\xFD\x03\x00\x91\xFE\x03\x02\xAA\x60\x00\x1F\xD6", 16); 87 | /* 88 | 0x0000000000000000: FD 7B C1 A8 ldp x29, x30, [sp], #0x10 89 | 0x0000000000000004: C0 03 5F D6 ret 90 | */ 91 | memcpy(quick_on_stack_back, "\xFD\x7B\xC1\xA8\xC0\x03\x5F\xD6", 8); 92 | #elif defined(__arm__) 93 | // r0~r3 94 | /* 95 | 0x0000000000000000: 04 E0 2D E5 str lr, [sp, #-4]! 96 | 0x0000000000000004: 02 E0 A0 E1 mov lr, r2 97 | 0x0000000000000008: 13 FF 2F E1 bx r3 98 | */ 99 | memcpy(__insns, "\x04\xE0\x2D\xE5\x02\xE0\xA0\xE1\x13\xFF\x2F\xE1", 12); 100 | if ((pv & 1u) != 0u) { // Thumb 101 | /* 102 | 0x0000000000000000: 08 BC pop {r3} 103 | 0x0000000000000002: 18 47 bx r3 104 | */ 105 | memcpy((void *)(pv - 1), "\x08\xBC\x18\x47", 4); 106 | } else { 107 | /* 108 | 0x0000000000000000: 04 30 9D E4 pop {r3} 109 | 0x0000000000000004: 13 FF 2F E1 bx r3 110 | */ 111 | memcpy(quick_on_stack_back, "\x04\x30\x9D\xE4\x13\xFF\x2F\xE1", 8); 112 | } //if 113 | #else 114 | # error "not supported" 115 | #endif 116 | LOGI("init done! quick_on_stack_replace = %p, quick_on_stack_back = %p", 117 | STUBS.generic_stub, quick_on_stack_back); 118 | } //if 119 | } //if 120 | } 121 | 122 | void *JNIEXPORT ndk_dlopen(const char *filename, int flag) 123 | { 124 | if (SDK_INT >= 24) { 125 | #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__) 126 | return STUBS.quick_on_stack_replace(filename, (void *)flag, 127 | quick_on_stack_back, dlopen); 128 | #else 129 | # error "not supported" 130 | #endif 131 | } //if 132 | 133 | return dlopen(filename, flag); 134 | } 135 | 136 | int JNIEXPORT ndk_dlclose(void *handle) 137 | { 138 | if (SDK_INT >= 24) { 139 | #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__) 140 | return (int)STUBS.quick_on_stack_replace(handle, NULL, 141 | quick_on_stack_back, dlclose); 142 | #else 143 | # error "not supported" 144 | #endif 145 | } //if 146 | 147 | return dlclose(handle); 148 | } 149 | 150 | const char *JNIEXPORT ndk_dlerror(void) 151 | { 152 | if (SDK_INT >= 24) { 153 | #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__) 154 | return STUBS.quick_on_stack_replace(NULL, NULL, 155 | quick_on_stack_back, dlerror); 156 | #else 157 | # error "not supported" 158 | #endif 159 | } //if 160 | 161 | return dlerror(); 162 | } 163 | 164 | void *JNIEXPORT ndk_dlsym(void *handle, const char *symbol) 165 | { 166 | if (SDK_INT >= 24) { 167 | #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__) 168 | return STUBS.quick_on_stack_replace(handle, symbol, 169 | quick_on_stack_back, dlsym); 170 | 171 | #else 172 | # error "not supported" 173 | #endif 174 | } //if 175 | 176 | return dlsym(handle, symbol); 177 | } 178 | 179 | int JNIEXPORT ndk_dladdr(const void *ddr, Dl_info *info) 180 | { 181 | if (SDK_INT >= 24) { 182 | #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__) 183 | return (int)STUBS.quick_on_stack_replace(ddr, info, 184 | quick_on_stack_back, dladdr); 185 | #else 186 | # error "not supported" 187 | #endif 188 | } //if 189 | 190 | return dladdr(ddr, info); 191 | } 192 | -------------------------------------------------------------------------------- /app/src/main/jni/dlopen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * @author : rrrfff@foxmail.com 4 | * https://github.com/rrrfff/ndk_dlopen 5 | * 6 | */ 7 | #pragma once 8 | #include 9 | #include 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | void ndk_init(JNIEnv *env); 16 | void *ndk_dlopen(const char *filename, int flag); 17 | int ndk_dlclose(void *handle); 18 | const char *ndk_dlerror(void); 19 | void *ndk_dlsym(void *handle, const char *symbol); 20 | int ndk_dladdr(const void *ddr, Dl_info *info); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | -------------------------------------------------------------------------------- /app/src/main/jni/dump.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by 袁东明 on 2017/7/1. 3 | // 4 | 5 | extern "C" { 6 | #include "include/inlineHook.h" 7 | #include "dlopen.h" 8 | } 9 | 10 | #include "dump.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | 21 | #define TAG "DEX_DUMP" 22 | 23 | int apiLevel(); 24 | 25 | void getProcessName(int pid, char *name, int len); 26 | 27 | void dumpFileName(char *name, int len, const char *pname, int dexlen); 28 | 29 | static char pname[256]; 30 | 31 | int apiLevel() { 32 | char version[10]; 33 | __system_property_get("ro.build.version.sdk", version); 34 | __android_log_print(ANDROID_LOG_INFO, TAG, "api level %s", version); 35 | int sdk = atoi(version); 36 | return sdk; 37 | } 38 | 39 | void getProcessName(int pid, char *name, int len) { 40 | int fp = open("/proc/self/cmdline", O_RDONLY); 41 | memset(name, 0, len); 42 | read(fp, name, len); 43 | close(fp); 44 | } 45 | 46 | void dumpFileName(char *name, int len, const char *pname, int dexlen) { 47 | time_t now; 48 | struct tm *timenow; 49 | time(&now); 50 | timenow = localtime(&now); 51 | memset(name, 0, len); 52 | sprintf(name, "/data/data/%s/dump_size_%u_time_%d_%d_%d_%d_%d_%d.dex", pname, dexlen, 53 | timenow->tm_year + 1900, 54 | timenow->tm_mon + 1, 55 | timenow->tm_mday, 56 | timenow->tm_hour, 57 | timenow->tm_min, 58 | timenow->tm_sec); 59 | } 60 | 61 | void writeToFile(const char *pname, u_int8_t *data, size_t length) { 62 | char dname[1024]; 63 | dumpFileName(dname, sizeof(dname), pname, length); 64 | __android_log_print(ANDROID_LOG_ERROR, TAG, "dump dex file name is : %s", dname); 65 | __android_log_print(ANDROID_LOG_ERROR, TAG, "start dump"); 66 | int dex = open(dname, O_CREAT | O_WRONLY, 0644); 67 | if (dex < 0) { 68 | __android_log_print(ANDROID_LOG_ERROR, TAG, "open or create file error"); 69 | return; 70 | } 71 | int ret = write(dex, data, length); 72 | if (ret < 0) { 73 | __android_log_print(ANDROID_LOG_ERROR, TAG, "write file error"); 74 | } else { 75 | __android_log_print(ANDROID_LOG_ERROR, TAG, "dump dex file success `%s`", dname); 76 | } 77 | close(dex); 78 | } 79 | 80 | art::DexFile *(*old_openmemory)(const byte *base, size_t size, const std::string &location, 81 | uint32_t location_checksum, art::MemMap *mem_map, 82 | const art::OatDexFile *oat_dex_file, std::string *error_msg) = NULL; 83 | 84 | art::DexFile *new_openmemory(const byte *base, size_t size, const std::string &location, 85 | uint32_t location_checksum, art::MemMap *mem_map, 86 | const art::OatDexFile *oat_dex_file, std::string *error_msg) { 87 | __android_log_print(ANDROID_LOG_ERROR, TAG, "art::DexFile::OpenMemory is called"); 88 | writeToFile(pname, (uint8_t *) base, size); 89 | return (*old_openmemory)(base, size, location, location_checksum, mem_map, oat_dex_file, 90 | error_msg); 91 | } 92 | 93 | 94 | art::DexFile *(*old_openmemory_23)(void* DexFile_thiz,char* base,int size,void* location, 95 | void* location_checksum,void* mem_map,void* oat_dex_file,void* error_meessage) = NULL; 96 | 97 | art::DexFile *new_openmemory_23(void* DexFile_thiz,char* base,int size,void* location, 98 | void* location_checksum,void* mem_map,void* oat_dex_file,void* error_meessage) { 99 | writeToFile(pname, (u_int8_t *)base, size); 100 | return (*old_openmemory_23)(DexFile_thiz, base, size, location, location_checksum, mem_map, 101 | oat_dex_file, error_meessage); 102 | } 103 | 104 | DexFile* (*old_dexFileParse)(const u1 *data, size_t length, int flags) = NULL; 105 | 106 | DexFile* new_dexFileParse(const u1 *data, size_t length, int flags) { 107 | writeToFile(pname, (u_int8_t *)data, length); 108 | return (*old_dexFileParse)(data, length, flags); 109 | } 110 | 111 | DvmDex* (*old_dvmDexFileOpenPartial)(const void* addr, int len, DvmDex** ppDvmDex) = NULL; 112 | 113 | DvmDex* new_dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex) { 114 | writeToFile(pname, (u_int8_t *)addr, len); 115 | return (*old_dvmDexFileOpenPartial)(addr, len, ppDvmDex); 116 | } 117 | 118 | void hook_dvm() { 119 | void *handle = dlopen("libdvm.so", RTLD_GLOBAL | RTLD_LAZY); 120 | if(handle == NULL) { 121 | __android_log_print(ANDROID_LOG_ERROR, TAG, "Error: unable to find the SO : libdvm.so"); 122 | return; 123 | } 124 | void *addr = dlsym(handle, "_Z12dexFileParsePKhji"); 125 | if(addr == NULL) { 126 | __android_log_print(ANDROID_LOG_ERROR, TAG, "Unable find symbol _Z12dexFileParsePKhji"); 127 | return; 128 | } 129 | if (registerInlineHook((uint32_t) addr, (uint32_t) new_dexFileParse, 130 | (uint32_t **) &old_dexFileParse) != ELE7EN_OK) { 131 | __android_log_print(ANDROID_LOG_ERROR, TAG, "register hook failed"); 132 | return; 133 | } 134 | 135 | if (inlineHook((uint32_t) addr) != ELE7EN_OK) { 136 | __android_log_print(ANDROID_LOG_ERROR, TAG, "register hook failed"); 137 | return; 138 | } 139 | 140 | addr = dlsym(handle, "_Z21dvmDexFileOpenPartialPKviPP6DvmDex"); 141 | if(addr == NULL) { 142 | __android_log_print(ANDROID_LOG_ERROR, TAG, "Unable find symbol _Z21dvmDexFileOpenPartialPKviPP6DvmDex"); 143 | return; 144 | } 145 | if (registerInlineHook((uint32_t) addr, (uint32_t) new_dvmDexFileOpenPartial, 146 | (uint32_t **) &old_dvmDexFileOpenPartial) != ELE7EN_OK) { 147 | __android_log_print(ANDROID_LOG_ERROR, TAG, "register hook failed"); 148 | return; 149 | } 150 | 151 | if (inlineHook((uint32_t) addr) != ELE7EN_OK) { 152 | __android_log_print(ANDROID_LOG_ERROR, TAG, "register hook failed"); 153 | return; 154 | } 155 | 156 | } 157 | 158 | void hook_21_22() { 159 | void *handle = dlopen("libart.so", RTLD_GLOBAL | RTLD_LAZY); 160 | if (handle == NULL) { 161 | __android_log_print(ANDROID_LOG_ERROR, TAG, "Error: unable to find the SO : libart.so"); 162 | return; 163 | } 164 | void *addr = dlsym(handle, 165 | "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"); 166 | if (addr == NULL) { 167 | __android_log_print(ANDROID_LOG_ERROR, TAG, 168 | "Error: unable to find the Symbol : _ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"); 169 | return; 170 | } 171 | 172 | if (registerInlineHook((uint32_t) addr, (uint32_t) new_openmemory, 173 | (uint32_t **) &old_openmemory) != ELE7EN_OK) { 174 | __android_log_print(ANDROID_LOG_ERROR, TAG, "register hook failed"); 175 | return; 176 | } 177 | 178 | if (inlineHook((uint32_t) addr) != ELE7EN_OK) { 179 | __android_log_print(ANDROID_LOG_ERROR, TAG, "register hook failed"); 180 | return; 181 | } 182 | 183 | __android_log_print(ANDROID_LOG_INFO, TAG, "register hook success"); 184 | } 185 | 186 | void hook_23_plus() { 187 | void *handle = ndk_dlopen("libart.so", RTLD_NOW); 188 | if (handle == NULL) { 189 | __android_log_print(ANDROID_LOG_ERROR, TAG, "Error: unable to find the SO : libart.so"); 190 | return; 191 | } 192 | void *addr = ndk_dlsym(handle, 193 | "_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"); 194 | if (addr == NULL) { 195 | __android_log_print(ANDROID_LOG_ERROR, TAG, 196 | "Error: unable to find the Symbol : _ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_"); 197 | return; 198 | } 199 | 200 | if (registerInlineHook((uint32_t) addr, (uint32_t) new_openmemory_23, 201 | (uint32_t **) &old_openmemory_23) != ELE7EN_OK) { 202 | __android_log_print(ANDROID_LOG_ERROR, TAG, "register hook failed"); 203 | return; 204 | } 205 | 206 | if (inlineHook((uint32_t) addr) != ELE7EN_OK) { 207 | __android_log_print(ANDROID_LOG_ERROR, TAG, "register hook failed"); 208 | return; 209 | } 210 | 211 | __android_log_print(ANDROID_LOG_INFO, TAG, "register hook success"); 212 | } 213 | 214 | 215 | JNIEXPORT void JNICALL Java_com_smartdone_dexdump_Dumpper_dump(JNIEnv *env, jclass clazz) { 216 | getProcessName(getpid(), pname, sizeof(pname)); 217 | int api = apiLevel(); 218 | if(api < 21){ 219 | hook_dvm(); 220 | } else if(api < 23) { 221 | hook_21_22(); 222 | } else { 223 | ndk_init(env); 224 | hook_23_plus(); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /app/src/main/jni/dump.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by 袁东明 on 2017/7/1. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #ifndef DEXDUMP_DUMP_H 10 | #define DEXDUMP_DUMP_H 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | JNIEXPORT void JNICALL Java_com_smartdone_dexdump_Dumpper_dump(JNIEnv *, jclass); 17 | typedef uint8_t byte; 18 | namespace art { 19 | 20 | class OatFile; 21 | 22 | class DexFile; 23 | 24 | class OatDexFile; 25 | 26 | class MemMap; 27 | } 28 | 29 | typedef uint8_t u1; 30 | typedef uint16_t u2; 31 | typedef uint32_t u4; 32 | typedef uint64_t u8; 33 | typedef int8_t s1; 34 | typedef int16_t s2; 35 | typedef int32_t s4; 36 | typedef int64_t s8; 37 | 38 | enum { kSHA1DigestLen = 20, 39 | kSHA1DigestOutputLen = kSHA1DigestLen*2 +1 }; 40 | 41 | struct DexHeader { 42 | u1 magic[8]; /* includes version number */ 43 | u4 checksum; /* adler32 checksum */ 44 | u1 signature[kSHA1DigestLen]; /* SHA-1 hash */ 45 | u4 fileSize; /* length of entire file */ 46 | u4 headerSize; /* offset to start of next section */ 47 | u4 endianTag; 48 | u4 linkSize; 49 | u4 linkOff; 50 | u4 mapOff; 51 | u4 stringIdsSize; 52 | u4 stringIdsOff; 53 | u4 typeIdsSize; 54 | u4 typeIdsOff; 55 | u4 protoIdsSize; 56 | u4 protoIdsOff; 57 | u4 fieldIdsSize; 58 | u4 fieldIdsOff; 59 | u4 methodIdsSize; 60 | u4 methodIdsOff; 61 | u4 classDefsSize; 62 | u4 classDefsOff; 63 | u4 dataSize; 64 | u4 dataOff; 65 | }; 66 | 67 | struct DexOptHeader { 68 | u1 magic[8]; /* includes version number */ 69 | 70 | u4 dexOffset; /* file offset of DEX header */ 71 | u4 dexLength; 72 | u4 depsOffset; /* offset of optimized DEX dependency table */ 73 | u4 depsLength; 74 | u4 optOffset; /* file offset of optimized data tables */ 75 | u4 optLength; 76 | 77 | u4 flags; /* some info flags */ 78 | u4 checksum; /* adler32 checksum covering deps/opt */ 79 | 80 | /* pad for 64-bit alignment if necessary */ 81 | }; 82 | 83 | 84 | struct DexStringId { 85 | u4 stringDataOff; /* file offset to string_data_item */ 86 | }; 87 | 88 | struct DexTypeId { 89 | u4 descriptorIdx; /* index into stringIds list for type descriptor */ 90 | }; 91 | 92 | struct DexFieldId { 93 | u2 classIdx; /* index into typeIds list for defining class */ 94 | u2 typeIdx; /* index into typeIds for field type */ 95 | u4 nameIdx; /* index into stringIds for field name */ 96 | }; 97 | 98 | struct DexMethodId { 99 | u2 classIdx; /* index into typeIds list for defining class */ 100 | u2 protoIdx; /* index into protoIds for method prototype */ 101 | u4 nameIdx; /* index into stringIds for method name */ 102 | }; 103 | 104 | struct DexProtoId { 105 | u4 shortyIdx; /* index into stringIds for shorty descriptor */ 106 | u4 returnTypeIdx; /* index into typeIds list for return type */ 107 | u4 parametersOff; /* file offset to type_list for parameter types */ 108 | }; 109 | 110 | struct DexClassDef { 111 | u4 classIdx; /* index into typeIds for this class */ 112 | u4 accessFlags; 113 | u4 superclassIdx; /* index into typeIds for superclass */ 114 | u4 interfacesOff; /* file offset to DexTypeList */ 115 | u4 sourceFileIdx; /* index into stringIds for source file name */ 116 | u4 annotationsOff; /* file offset to annotations_directory_item */ 117 | u4 classDataOff; /* file offset to class_data_item */ 118 | u4 staticValuesOff; /* file offset to DexEncodedArray */ 119 | }; 120 | 121 | struct DexLink { 122 | u1 bleargh; 123 | }; 124 | 125 | struct DexClassLookup { 126 | int size; // total size, including "size" 127 | int numEntries; // size of table[]; always power of 2 128 | struct { 129 | u4 classDescriptorHash; // class descriptor hash code 130 | int classDescriptorOffset; // in bytes, from start of DEX 131 | int classDefOffset; // in bytes, from start of DEX 132 | } table[1]; 133 | }; 134 | 135 | struct DexFile { 136 | /* directly-mapped "opt" header */ 137 | const DexOptHeader* pOptHeader; 138 | 139 | /* pointers to directly-mapped structs and arrays in base DEX */ 140 | const DexHeader* pHeader; 141 | const DexStringId* pStringIds; 142 | const DexTypeId* pTypeIds; 143 | const DexFieldId* pFieldIds; 144 | const DexMethodId* pMethodIds; 145 | const DexProtoId* pProtoIds; 146 | const DexClassDef* pClassDefs; 147 | const DexLink* pLinkData; 148 | 149 | /* 150 | * These are mapped out of the "auxillary" section, and may not be 151 | * included in the file. 152 | */ 153 | const DexClassLookup* pClassLookup; 154 | const void* pRegisterMapPool; // RegisterMapClassPool 155 | 156 | /* points to start of DEX file data */ 157 | const u1* baseAddr; 158 | 159 | /* track memory overhead for auxillary structures */ 160 | int overhead; 161 | 162 | /* additional app-specific data structures associated with the DEX */ 163 | //void* auxData; 164 | }; 165 | 166 | struct MemMapping { 167 | void* addr; /* start of data */ 168 | size_t length; /* length of data */ 169 | 170 | void* baseAddr; /* page-aligned base address */ 171 | size_t baseLength; /* length of mapping */ 172 | }; 173 | 174 | struct DvmDex { 175 | /* pointer to the DexFile we're associated with */ 176 | DexFile* pDexFile; 177 | 178 | /* clone of pDexFile->pHeader (it's used frequently enough) */ 179 | const DexHeader* pHeader; 180 | 181 | /* interned strings; parallel to "stringIds" */ 182 | struct StringObject** pResStrings; 183 | 184 | /* resolved classes; parallel to "typeIds" */ 185 | struct ClassObject** pResClasses; 186 | 187 | /* resolved methods; parallel to "methodIds" */ 188 | struct Method** pResMethods; 189 | 190 | /* resolved instance fields; parallel to "fieldIds" */ 191 | /* (this holds both InstField and StaticField) */ 192 | struct Field** pResFields; 193 | 194 | /* interface method lookup cache */ 195 | struct AtomicCache* pInterfaceCache; 196 | 197 | /* shared memory region with file contents */ 198 | bool isMappedReadOnly; 199 | MemMapping memMap; 200 | 201 | jobject dex_object; 202 | 203 | /* lock ensuring mutual exclusion during updates */ 204 | pthread_mutex_t modLock; 205 | }; 206 | 207 | struct RawDexFile { 208 | char* cacheFileName; 209 | DvmDex* pDvmDex; 210 | }; 211 | 212 | 213 | #ifdef __cplusplus 214 | } 215 | #endif 216 | 217 | #endif //DEXDUMP_DUMP_H 218 | -------------------------------------------------------------------------------- /app/src/main/jni/include/inlineHook.h: -------------------------------------------------------------------------------- 1 | #ifndef _INLINEHOOK_H 2 | #define _INLINEHOOK_H 3 | 4 | #include 5 | 6 | enum ele7en_status { 7 | ELE7EN_ERROR_UNKNOWN = -1, 8 | ELE7EN_OK = 0, 9 | ELE7EN_ERROR_NOT_INITIALIZED, 10 | ELE7EN_ERROR_NOT_EXECUTABLE, 11 | ELE7EN_ERROR_NOT_REGISTERED, 12 | ELE7EN_ERROR_NOT_HOOKED, 13 | ELE7EN_ERROR_ALREADY_REGISTERED, 14 | ELE7EN_ERROR_ALREADY_HOOKED, 15 | ELE7EN_ERROR_SO_NOT_FOUND, 16 | ELE7EN_ERROR_FUNCTION_NOT_FOUND 17 | }; 18 | 19 | enum ele7en_status registerInlineHook(uint32_t target_addr, uint32_t new_addr, uint32_t **proto_addr); 20 | enum ele7en_status inlineUnHook(uint32_t target_addr); 21 | void inlineUnHookAll(); 22 | enum ele7en_status inlineHook(uint32_t target_addr); 23 | void inlineHookAll(); 24 | 25 | 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /app/src/main/jni/inlineHook.c: -------------------------------------------------------------------------------- 1 | /* 2 | thumb16 thumb32 arm32 inlineHook 3 | author: ele7enxxh 4 | mail: ele7enxxh@qq.com 5 | website: ele7enxxh.com 6 | modified time: 2015-01-23 7 | created time: 2015-11-30 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | // #include 17 | #include 18 | #include 19 | 20 | #include "relocate.h" 21 | #include "include/inlineHook.h" 22 | 23 | #ifndef PAGE_SIZE 24 | #define PAGE_SIZE 4096 25 | #endif 26 | 27 | #define PAGE_START(addr) (~(PAGE_SIZE - 1) & (addr)) 28 | #define SET_BIT0(addr) (addr | 1) 29 | #define CLEAR_BIT0(addr) (addr & 0xFFFFFFFE) 30 | #define TEST_BIT0(addr) (addr & 1) 31 | 32 | #define ACTION_ENABLE 0 33 | #define ACTION_DISABLE 1 34 | 35 | enum hook_status { 36 | REGISTERED, 37 | HOOKED, 38 | }; 39 | 40 | struct inlineHookItem { 41 | uint32_t target_addr; 42 | uint32_t new_addr; 43 | uint32_t **proto_addr; 44 | void *orig_instructions; 45 | int orig_boundaries[4]; 46 | int trampoline_boundaries[20]; 47 | int count; 48 | void *trampoline_instructions; 49 | int length; 50 | int status; 51 | int mode; 52 | }; 53 | 54 | struct inlineHookInfo { 55 | struct inlineHookItem item[1024]; 56 | int size; 57 | }; 58 | 59 | static struct inlineHookInfo info = {0}; 60 | 61 | static int getAllTids(pid_t pid, pid_t *tids) 62 | { 63 | char dir_path[32]; 64 | DIR *dir; 65 | int i; 66 | struct dirent *entry; 67 | pid_t tid; 68 | 69 | if (pid < 0) { 70 | snprintf(dir_path, sizeof(dir_path), "/proc/self/task"); 71 | } 72 | else { 73 | snprintf(dir_path, sizeof(dir_path), "/proc/%d/task", pid); 74 | } 75 | 76 | dir = opendir(dir_path); 77 | if (dir == NULL) { 78 | return 0; 79 | } 80 | 81 | i = 0; 82 | while((entry = readdir(dir)) != NULL) { 83 | tid = atoi(entry->d_name); 84 | if (tid != 0 && tid != getpid()) { 85 | tids[i++] = tid; 86 | } 87 | } 88 | closedir(dir); 89 | return i; 90 | } 91 | 92 | static bool doProcessThreadPC(struct inlineHookItem *item, struct pt_regs *regs, int action) 93 | { 94 | int offset; 95 | int i; 96 | 97 | switch (action) 98 | { 99 | case ACTION_ENABLE: 100 | offset = regs->ARM_pc - CLEAR_BIT0(item->target_addr); 101 | for (i = 0; i < item->count; ++i) { 102 | if (offset == item->orig_boundaries[i]) { 103 | regs->ARM_pc = (uint32_t) item->trampoline_instructions + item->trampoline_boundaries[i]; 104 | return true; 105 | } 106 | } 107 | break; 108 | case ACTION_DISABLE: 109 | offset = regs->ARM_pc - (int) item->trampoline_instructions; 110 | for (i = 0; i < item->count; ++i) { 111 | if (offset == item->trampoline_boundaries[i]) { 112 | regs->ARM_pc = CLEAR_BIT0(item->target_addr) + item->orig_boundaries[i]; 113 | return true; 114 | } 115 | } 116 | break; 117 | } 118 | 119 | return false; 120 | } 121 | 122 | static void processThreadPC(pid_t tid, struct inlineHookItem *item, int action) 123 | { 124 | struct pt_regs regs; 125 | 126 | if (ptrace(PTRACE_GETREGS, tid, NULL, ®s) == 0) { 127 | if (item == NULL) { 128 | int pos; 129 | 130 | for (pos = 0; pos < info.size; ++pos) { 131 | if (doProcessThreadPC(&info.item[pos], ®s, action) == true) { 132 | break; 133 | } 134 | } 135 | } 136 | else { 137 | doProcessThreadPC(item, ®s, action); 138 | } 139 | 140 | ptrace(PTRACE_SETREGS, tid, NULL, ®s); 141 | } 142 | } 143 | 144 | static pid_t freeze(struct inlineHookItem *item, int action) 145 | { 146 | int count; 147 | pid_t tids[1024]; 148 | pid_t pid; 149 | 150 | pid = -1; 151 | count = getAllTids(getpid(), tids); 152 | if (count > 0) { 153 | pid = fork(); 154 | 155 | if (pid == 0) { 156 | int i; 157 | 158 | for (i = 0; i < count; ++i) { 159 | if (ptrace(PTRACE_ATTACH, tids[i], NULL, NULL) == 0) { 160 | waitpid(tids[i], NULL, WUNTRACED); 161 | processThreadPC(tids[i], item, action); 162 | } 163 | } 164 | 165 | raise(SIGSTOP); 166 | 167 | for (i = 0; i < count; ++i) { 168 | ptrace(PTRACE_DETACH, tids[i], NULL, NULL); 169 | } 170 | 171 | raise(SIGKILL); 172 | } 173 | 174 | else if (pid > 0) { 175 | waitpid(pid, NULL, WUNTRACED); 176 | } 177 | } 178 | 179 | return pid; 180 | } 181 | 182 | static void unFreeze(pid_t pid) 183 | { 184 | if (pid < 0) { 185 | return; 186 | } 187 | 188 | kill(pid, SIGCONT); 189 | wait(NULL); 190 | } 191 | 192 | static bool isExecutableAddr(uint32_t addr) 193 | { 194 | FILE *fp; 195 | char line[1024]; 196 | uint32_t start; 197 | uint32_t end; 198 | 199 | fp = fopen("/proc/self/maps", "r"); 200 | if (fp == NULL) { 201 | return false; 202 | } 203 | 204 | while (fgets(line, sizeof(line), fp)) { 205 | if (strstr(line, "r-xp")) { 206 | start = strtoul(strtok(line, "-"), NULL, 16); 207 | end = strtoul(strtok(NULL, " "), NULL, 16); 208 | if (addr >= start && addr <= end) { 209 | fclose(fp); 210 | return true; 211 | } 212 | } 213 | } 214 | 215 | fclose(fp); 216 | 217 | return false; 218 | } 219 | 220 | static struct inlineHookItem *findInlineHookItem(uint32_t target_addr) 221 | { 222 | int i; 223 | 224 | for (i = 0; i < info.size; ++i) { 225 | if (info.item[i].target_addr == target_addr) { 226 | return &info.item[i]; 227 | } 228 | } 229 | 230 | return NULL; 231 | } 232 | 233 | static struct inlineHookItem *addInlineHookItem() { 234 | struct inlineHookItem *item; 235 | 236 | if (info.size >= 1024) { 237 | return NULL; 238 | } 239 | 240 | item = &info.item[info.size]; 241 | ++info.size; 242 | 243 | return item; 244 | } 245 | 246 | static void deleteInlineHookItem(int pos) 247 | { 248 | info.item[pos] = info.item[info.size - 1]; 249 | --info.size; 250 | } 251 | 252 | enum ele7en_status registerInlineHook(uint32_t target_addr, uint32_t new_addr, uint32_t **proto_addr) 253 | { 254 | struct inlineHookItem *item; 255 | 256 | if (!isExecutableAddr(target_addr) || !isExecutableAddr(new_addr)) { 257 | return ELE7EN_ERROR_NOT_EXECUTABLE; 258 | } 259 | 260 | item = findInlineHookItem(target_addr); 261 | if (item != NULL) { 262 | if (item->status == REGISTERED) { 263 | return ELE7EN_ERROR_ALREADY_REGISTERED; 264 | } 265 | else if (item->status == HOOKED) { 266 | return ELE7EN_ERROR_ALREADY_HOOKED; 267 | } 268 | else { 269 | return ELE7EN_ERROR_UNKNOWN; 270 | } 271 | } 272 | 273 | item = addInlineHookItem(); 274 | 275 | item->target_addr = target_addr; 276 | item->new_addr = new_addr; 277 | item->proto_addr = proto_addr; 278 | 279 | item->length = TEST_BIT0(item->target_addr) ? 12 : 8; 280 | item->orig_instructions = malloc(item->length); 281 | memcpy(item->orig_instructions, (void *) CLEAR_BIT0(item->target_addr), item->length); 282 | 283 | item->trampoline_instructions = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 284 | relocateInstruction(item->target_addr, item->orig_instructions, item->length, item->trampoline_instructions, item->orig_boundaries, item->trampoline_boundaries, &item->count); 285 | 286 | item->status = REGISTERED; 287 | 288 | return ELE7EN_OK; 289 | } 290 | 291 | static void doInlineUnHook(struct inlineHookItem *item, int pos) 292 | { 293 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_WRITE | PROT_EXEC); 294 | memcpy((void *) CLEAR_BIT0(item->target_addr), item->orig_instructions, item->length); 295 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_EXEC); 296 | munmap(item->trampoline_instructions, PAGE_SIZE); 297 | free(item->orig_instructions); 298 | 299 | deleteInlineHookItem(pos); 300 | 301 | cacheflush(CLEAR_BIT0(item->target_addr), CLEAR_BIT0(item->target_addr) + item->length, 0); 302 | } 303 | 304 | enum ele7en_status inlineUnHook(uint32_t target_addr) 305 | { 306 | int i; 307 | 308 | for (i = 0; i < info.size; ++i) { 309 | if (info.item[i].target_addr == target_addr && info.item[i].status == HOOKED) { 310 | pid_t pid; 311 | 312 | pid = freeze(&info.item[i], ACTION_DISABLE); 313 | 314 | doInlineUnHook(&info.item[i], i); 315 | 316 | unFreeze(pid); 317 | 318 | return ELE7EN_OK; 319 | } 320 | } 321 | 322 | return ELE7EN_ERROR_NOT_HOOKED; 323 | } 324 | 325 | void inlineUnHookAll() 326 | { 327 | pid_t pid; 328 | int i; 329 | 330 | pid = freeze(NULL, ACTION_DISABLE); 331 | 332 | for (i = 0; i < info.size; ++i) { 333 | if (info.item[i].status == HOOKED) { 334 | doInlineUnHook(&info.item[i], i); 335 | --i; 336 | } 337 | } 338 | 339 | unFreeze(pid); 340 | } 341 | 342 | static void doInlineHook(struct inlineHookItem *item) 343 | { 344 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_WRITE | PROT_EXEC); 345 | 346 | if (item->proto_addr != NULL) { 347 | *(item->proto_addr) = TEST_BIT0(item->target_addr) ? (uint32_t *) SET_BIT0((uint32_t) item->trampoline_instructions) : item->trampoline_instructions; 348 | } 349 | 350 | if (TEST_BIT0(item->target_addr)) { 351 | int i; 352 | 353 | i = 0; 354 | if (CLEAR_BIT0(item->target_addr) % 4 != 0) { 355 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xBF00; // NOP 356 | } 357 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF8DF; 358 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF000; // LDR.W PC, [PC] 359 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr & 0xFFFF; 360 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr >> 16; 361 | } 362 | else { 363 | ((uint32_t *) (item->target_addr))[0] = 0xe51ff004; // LDR PC, [PC, #-4] 364 | ((uint32_t *) (item->target_addr))[1] = item->new_addr; 365 | } 366 | 367 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_EXEC); 368 | 369 | item->status = HOOKED; 370 | 371 | cacheflush(CLEAR_BIT0(item->target_addr), CLEAR_BIT0(item->target_addr) + item->length, 0); 372 | } 373 | 374 | enum ele7en_status inlineHook(uint32_t target_addr) 375 | { 376 | int i; 377 | struct inlineHookItem *item; 378 | 379 | item = NULL; 380 | for (i = 0; i < info.size; ++i) { 381 | if (info.item[i].target_addr == target_addr) { 382 | item = &info.item[i]; 383 | break; 384 | } 385 | } 386 | 387 | if (item == NULL) { 388 | return ELE7EN_ERROR_NOT_REGISTERED; 389 | } 390 | 391 | if (item->status == REGISTERED) { 392 | pid_t pid; 393 | 394 | pid = freeze(item, ACTION_ENABLE); 395 | 396 | doInlineHook(item); 397 | 398 | unFreeze(pid); 399 | 400 | return ELE7EN_OK; 401 | } 402 | else if (item->status == HOOKED) { 403 | return ELE7EN_ERROR_ALREADY_HOOKED; 404 | } 405 | else { 406 | return ELE7EN_ERROR_UNKNOWN; 407 | } 408 | } 409 | 410 | void inlineHookAll() 411 | { 412 | pid_t pid; 413 | int i; 414 | 415 | pid = freeze(NULL, ACTION_ENABLE); 416 | 417 | for (i = 0; i < info.size; ++i) { 418 | if (info.item[i].status == REGISTERED) { 419 | doInlineHook(&info.item[i]); 420 | } 421 | } 422 | 423 | unFreeze(pid); 424 | } 425 | -------------------------------------------------------------------------------- /app/src/main/jni/relocate.c: -------------------------------------------------------------------------------- 1 | /* 2 | relocate instruction 3 | author: ele7enxxh 4 | mail: ele7enxxh@qq.com 5 | website: ele7enxxh.com 6 | modified time: 2016-10-17 7 | created time: 2015-01-17 8 | */ 9 | 10 | #include "relocate.h" 11 | 12 | #define ALIGN_PC(pc) (pc & 0xFFFFFFFC) 13 | 14 | enum INSTRUCTION_TYPE { 15 | // B