├── .gitignore ├── README.md ├── build.gradle ├── libs └── XposedBridgeApi-54.jar ├── nop.java ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── assets └── xposed_init ├── java └── com │ └── xpu01 │ ├── DialogItemAdapter.java │ ├── Japp.java │ ├── Main.java │ ├── Xpu01hook.java │ └── x64.java └── res ├── drawable-hdpi └── ic_launcher.png ├── drawable-mdpi └── ic_launcher.png ├── drawable-xhdpi └── ic_launcher.png ├── drawable-xxhdpi └── ic_launcher.png ├── drawable ├── about.png ├── modules.png └── settings.png ├── layout ├── drill.xml ├── elog.xml ├── hook.xml ├── item_app.xml ├── ktest_dialog.xml ├── main.xml └── setting.xml ├── menu └── menu.xml ├── values-v21 └── styles.xml ├── values-v23 └── styles.xml └── values ├── color.xml ├── strings.xml └── styles.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Build and Release Folders 2 | bin-debug/ 3 | bin-release/ 4 | [Oo]bj/ 5 | [Bb]in/ 6 | 7 | # Other files and folders 8 | .settings/ 9 | 10 | # Executables 11 | *.swf 12 | *.air 13 | *.ipa 14 | *.apk 15 | 16 | # Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` 17 | # should NOT be excluded as they contain compiler settings and other important 18 | # information for Eclipse / Flash Builder. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xpu01 2 | 基于Xposed框架安卓脱壳软件 3 | #### 实现与使用 4 | 通过反射调用getDex方法取得com.android.dex.Dex类(获取dex),不支持8.0系统以上版本,高系统可以配合虚拟机来使用。 5 | 6 | 7 | #### 关于类抽取(nop)填充还原代码实现 8 | 请看nop.java 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 21 5 | buildToolsVersion "21.1.0" 6 | 7 | defaultConfig { 8 | applicationId "com.xpu01" 9 | minSdkVersion 14 10 | targetSdkVersion 21 11 | versionCode 4 12 | versionName "1.3" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | } 25 | -------------------------------------------------------------------------------- /libs/XposedBridgeApi-54.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eksenior/xpu01/152ac26e4578e38090792f080f0f01e5a7e5c4b8/libs/XposedBridgeApi-54.jar -------------------------------------------------------------------------------- /nop.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | private String Ldex(Context mContext,String stord)throws Throwable{//脱壳 保存目录, 脱多个dex 带遍历类 nop 回填 4 | String[] strArr; 5 | Log("遍历dump 加载Dex数组..."); 6 | ArrayList arrayList = new ArrayList(); 7 | ClassLoader classLoader = mContext.getClassLoader(); 8 | Field declaredField = classLoader.getClass().getSuperclass().getDeclaredField("pathList"); 9 | declaredField.setAccessible(true); 10 | Object[] objArr2 = (Object[]) fsd(declaredField.get(classLoader), "dexElements"); 11 | //publishProgress(objArr2); 12 | Method declaredMethod3 = classLoader.getClass().getSuperclass().getSuperclass().getDeclaredMethod("findClass", new Class[]{String.class}); 13 | declaredMethod3.setAccessible(true); 14 | for (Object obj : objArr2) { 15 | Object d = fsd(obj, "dexFile"); 16 | if (d != null) { 17 | Log("遍历类..."); 18 | Method declaredMethod4 = d.getClass().getDeclaredMethod("entries", new Class[0]); 19 | if (!(declaredMethod4.invoke(d, new Object[0]) == null || (strArr = (String[]) fsd(declaredMethod4.invoke(d, new Object[0]), "mNameList")) == null || strArr.length == 0)) { 20 | Log("提取Dex"); 21 | for (String str4 : strArr) { 22 | try { 23 | Class cls2 = (Class) declaredMethod3.invoke(classLoader, new Object[]{str4}); 24 | Object invoke2 = cls2.getClass().getDeclaredMethod("getDex", new Class[0]).invoke(cls2, new Object[0]); 25 | if (!arrayList.contains(invoke2)) { 26 | arrayList.add(invoke2); 27 | } 28 | } catch (Throwable th2) { 29 | } 30 | } 31 | continue; 32 | } 33 | } 34 | } 35 | if (arrayList.size() == 0) { 36 | return "找不到Dex!"; 37 | } 38 | File file = new File(stord); 39 | file.mkdirs(); 40 | int i = 1; 41 | Iterator it = arrayList.iterator(); 42 | while (true) { 43 | int i2 = i; 44 | if (it.hasNext()) { 45 | Object next = it.next(); 46 | byte[] bArr2 = (byte[]) next.getClass().getDeclaredMethod("getBytes", new Class[0]).invoke(next, new Object[0]); 47 | RandomAccessFile randomAccessFile2 = new RandomAccessFile(new File(file, "Dexdump" + (i2 == 1 ? "" : Integer.valueOf(i2)) + ".dex"), "rw"); 48 | 49 | randomAccessFile2.write(bArr2); 50 | randomAccessFile2.close(); 51 | i = i2 + 1; 52 | } else { 53 | return "脱壳成功,共写出 " + (i2 - 1) + " 个dex,文件夹位于 "+ stord; 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /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 C:\tools\adt-bundle-windows-x86_64-20131030\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 | -------------------------------------------------------------------------------- /src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 18 | 21 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | com.xpu01.Xpu01hook -------------------------------------------------------------------------------- /src/main/java/com/xpu01/DialogItemAdapter.java: -------------------------------------------------------------------------------- 1 | package com.xpu01; 2 | 3 | /** 4 | * ListAdapter 5 | */ 6 | import android.content.*; 7 | import android.view.*; 8 | import android.widget.*; 9 | import com.xpu01.*; 10 | import java.util.*; 11 | 12 | public class DialogItemAdapter extends BaseAdapter 13 | { 14 | 15 | //这里可以传递个对象,用来控制不同的item的效果 16 | //比如每个item的背景资源,选中样式等 17 | public List list; 18 | LayoutInflater inflater; 19 | 20 | public DialogItemAdapter(Context context, List list) { //接数据 21 | this.list = list; 22 | inflater = LayoutInflater.from(context); 23 | } 24 | 25 | @Override 26 | public int getCount() { 27 | return list.size(); 28 | } 29 | 30 | @Override 31 | public String getItem(int i) { 32 | if (i == getCount() || list == null) { 33 | return null; 34 | } 35 | return list.get(i); 36 | } 37 | 38 | @Override 39 | public long getItemId(int i) { 40 | return i; 41 | } 42 | 43 | @Override 44 | public View getView(int position, View convertView, ViewGroup viewGroup) { 45 | ViewHolder holder; 46 | if (convertView == null) { 47 | holder = new ViewHolder(); 48 | convertView = inflater.inflate(R.layout.item_app, null); 49 | holder.typeTextview = (TextView) convertView.findViewById(R.id.tv_app_name); 50 | holder.typeImageView = (ImageView) convertView.findViewById(R.id.iv_app); 51 | convertView.setTag(holder); 52 | } else { 53 | holder = (ViewHolder) convertView.getTag(); 54 | } 55 | holder.typeTextview.setText(Japp.appname(getItem(position))); //添加文字 56 | holder.typeImageView.setImageBitmap(Japp.applcon(getItem(position))); //添加图标 57 | 58 | return convertView; 59 | } 60 | 61 | 62 | public static class ViewHolder 63 | { 64 | public TextView typeTextview; 65 | 66 | public ImageView typeImageView; 67 | 68 | } 69 | 70 | 71 | 72 | 73 | 74 | 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/main/java/com/xpu01/Japp.java: -------------------------------------------------------------------------------- 1 | package com.xpu01; 2 | 3 | import android.content.pm.*; 4 | import android.graphics.*; 5 | import android.graphics.drawable.*; 6 | import java.io.*; 7 | import java.util.*; 8 | import android.content.pm.PackageManager.*; 9 | 10 | public class Japp 11 | { 12 | 13 | public static String[] getpacka() { //取第三方已安装应用 14 | List packageInfos = Main.getContext().getPackageManager().getInstalledPackages(0); 15 | List list = new ArrayList(); 16 | for (int i = 0; i < packageInfos.size(); i++) { 17 | PackageInfo pInfo = (PackageInfo) packageInfos.get(i); 18 | if ((pInfo.applicationInfo.flags & 1) == 0) { 19 | list.add(pInfo.packageName); 20 | } 21 | } 22 | return (String[]) list.toArray(new String[list.size()]); 23 | } 24 | 25 | 26 | public static String[] sygetpacka() {//取所有应用包名 27 | List var1 = Main.getContext().getPackageManager().getInstalledPackages(0); 28 | String[] var2 = new String[var1.size()]; 29 | 30 | for(int var0 = 0; var0 < var1.size(); ++var0) { 31 | var2[var0] = ((PackageInfo)var1.get(var0)).packageName; 32 | } 33 | 34 | return var2; 35 | } 36 | 37 | 38 | public static Bitmap applcon(String packagename) {//取应用图标 39 | try { 40 | PackageManager pm = Main.getContext().getPackageManager(); 41 | return drawableToBitmap(pm.getApplicationInfo(packagename, 0).loadIcon(pm)); 42 | } catch (PackageManager.NameNotFoundException e) { 43 | e.printStackTrace(); 44 | return null; 45 | } 46 | } 47 | 48 | public static String appname(String packagename) {//取应用名称 49 | try { 50 | PackageManager pm = Main.getContext().getPackageManager(); 51 | return pm.getApplicationInfo(packagename, 0).loadLabel(pm).toString(); 52 | } catch (PackageManager.NameNotFoundException e) { 53 | e.printStackTrace(); 54 | return ""; 55 | } 56 | } 57 | 58 | public static String qversion(String packagename) {//取应用版本 59 | try { 60 | return Main.getContext().getPackageManager().getPackageInfo(packagename, 0).versionName; 61 | } catch (PackageManager.NameNotFoundException e) { 62 | e.printStackTrace(); 63 | return "0"; 64 | } 65 | } 66 | 67 | public static String qsign(String packagename) { //取应用签名 68 | try { 69 | return Main.getContext().getPackageManager().getPackageInfo(packagename, 64).signatures[0].toCharsString(); 70 | } catch (PackageManager.NameNotFoundException e) { 71 | e.printStackTrace(); 72 | return ""; 73 | } 74 | } 75 | 76 | 77 | public static String qsignz(String packagename) {//获取 签名值 78 | try 79 | { 80 | PackageInfo a = Main.getContext().getPackageManager().getPackageInfo(packagename, PackageManager.GET_SIGNATURES); //packagename 包名 81 | Signature[] b = a.signatures; 82 | Signature c = b[0]; 83 | 84 | return String.valueOf(c.hashCode()); 85 | } 86 | catch (PackageManager.NameNotFoundException e) 87 | { 88 | return null; 89 | } 90 | } 91 | 92 | public static boolean whet_app(String packageName) { //判断手机是否安装某个应用 93 | if(packageName == null){ 94 | return false; 95 | } 96 | PackageManager packageManager = Main.getContext().getPackageManager();// 获取packagemanager 97 | List pinfo = packageManager.getInstalledPackages(0);// 获取所有已安装程序的包信息 98 | if (pinfo != null) { 99 | for (int i = 0; i < pinfo.size(); i++) { 100 | String pn = pinfo.get(i).packageName; 101 | if (packageName.equals(pn)) { 102 | return true; 103 | } 104 | } 105 | } 106 | return false; 107 | } 108 | 109 | 110 | 111 | public static String qapklj(String packagename) {//获取 已知包名apk的安装路径 112 | try 113 | { 114 | return Main.getContext().getPackageManager().getApplicationInfo(packagename, 0).sourceDir; 115 | } 116 | catch (PackageManager.NameNotFoundException e) 117 | { 118 | return null; 119 | } 120 | 121 | } 122 | 123 | 124 | public static String dataDir(String packagename) {//获取应用 data数据路径 125 | try 126 | { 127 | return Main.getContext().getPackageManager().getApplicationInfo(packagename, 0).dataDir; 128 | } 129 | catch (PackageManager.NameNotFoundException e) 130 | { 131 | return null; 132 | } 133 | 134 | } 135 | 136 | 137 | private static Bitmap drawableToBitmap(Drawable paramDrawable) { 138 | int i = paramDrawable.getIntrinsicWidth(); 139 | int j = paramDrawable.getIntrinsicHeight(); 140 | Bitmap localBitmap = Bitmap.createBitmap(i, j, paramDrawable.getOpacity() != -1 ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); 141 | Canvas localCanvas = new Canvas(localBitmap); 142 | paramDrawable.setBounds(0, 0, i, j); 143 | paramDrawable.draw(localCanvas); 144 | return localBitmap; 145 | } 146 | 147 | public static String uid(String packagename) {//获取应用 data数据路径 148 | 149 | try { 150 | PackageManager pm = Main.getContext().getPackageManager(); 151 | ApplicationInfo ai = pm.getApplicationInfo(packagename, 0); 152 | return Integer.toString(ai.uid,10); 153 | } catch (NameNotFoundException e) { 154 | } 155 | return null; 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/com/xpu01/Main.java: -------------------------------------------------------------------------------- 1 | package com.xpu01; 2 | 3 | import android.app.*; 4 | import android.content.*; 5 | import android.content.pm.*; 6 | import android.graphics.*; 7 | import android.net.*; 8 | import android.os.*; 9 | import android.view.*; 10 | import android.view.View.*; 11 | import android.widget.*; 12 | import android.widget.CompoundButton.*; 13 | import java.io.*; 14 | import java.util.*; 15 | 16 | import android.view.View.OnClickListener; 17 | import android.util.*; 18 | 19 | public class Main extends Activity 20 | { 21 | private static Main INSTANCE; 22 | private static String apackage = null; //包名 23 | private final static String noapp = "未选择应用"; //final 是不能被修改赋值 24 | public static String sdexsd = Environment.getExternalStorageDirectory().getPath() + "/Xpu01"; 25 | 26 | Button bu,bu1,bu2,bu3; 27 | ImageView appIcon; 28 | TextView tname,tpackage,tversion,tsign,tsize,treinforce,tdatadirectory,tapkdirectory,tmd5apk,tcrc32apk,tmd5sign,tapkuid; 29 | LinearLayout logView; 30 | List apps; 31 | 32 | private SharedPreferences configs_ini; 33 | 34 | 35 | public Main() { //第一个初始化 36 | INSTANCE = this; 37 | } 38 | 39 | @Override 40 | protected void onCreate(Bundle savedInstanceState) 41 | { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.main); 44 | 45 | configs_ini = getSharedPreferences("configs",MODE_WORLD_READABLE); //设置为MODE_WORLD_READABLE xp才可以读 46 | 47 | appIcon = (ImageView)findViewById(R.id.aIcon); 48 | 49 | tname = (TextView)findViewById(R.id.aname); 50 | tpackage = (TextView)findViewById(R.id.apackage); 51 | tversion = (TextView)findViewById(R.id.aversion); 52 | tsign = (TextView)findViewById(R.id.asign); 53 | tapkuid = (TextView)findViewById(R.id.apkuid); 54 | treinforce = (TextView)findViewById(R.id.areinforce); 55 | tsize = (TextView)findViewById(R.id.asize); 56 | tmd5apk = (TextView)findViewById(R.id.md5apk); 57 | tcrc32apk = (TextView)findViewById(R.id.crc32apk); 58 | tmd5sign = (TextView)findViewById(R.id.md5sign); 59 | tdatadirectory = (TextView)findViewById(R.id.datadir); 60 | tapkdirectory = (TextView)findViewById(R.id.apkdir); 61 | 62 | 63 | appIcon.setOnClickListener( new OnClickListener() { 64 | @Override 65 | public void onClick(View view) { 66 | initAppst(); 67 | 68 | }}); 69 | 70 | bu = (Button) findViewById( R.id.adump); //脱壳 打开另一个应用 71 | //bu.setEnabled(false);//安按钮不可用 72 | bu.setOnClickListener( new OnClickListener() { 73 | @Override 74 | public void onClick(View view) { 75 | 76 | if(Switch2bool("dump")){ 77 | dk(); 78 | 79 | }else if(Switch2bool("edump")){ 80 | 81 | dk(); 82 | 83 | }else{ 84 | if(apackage != null){ 85 | 86 | configs_ini.edit().putString("packagename",apackage).commit(); 87 | Intent intent = getPackageManager().getLaunchIntentForPackage(apackage); 88 | startActivity(intent); 89 | }else{ 90 | Toast(noapp); 91 | } 92 | } 93 | 94 | } 95 | }); 96 | 97 | 98 | bu1 = (Button) findViewById( R.id.adeta); //跳转信息界面 99 | //bu1.setEnabled(false);//安按钮不可用 100 | bu1.setOnClickListener( new OnClickListener() { 101 | @Override 102 | public void onClick(View view) { 103 | if(apackage != null){ 104 | //打开应用信息界面 105 | Intent mIntent = new Intent(); 106 | mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 107 | if (Build.VERSION.SDK_INT >= 9) { 108 | mIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); 109 | mIntent.setData(Uri.fromParts("package", apackage, null));//getPackageName() 是取当前应用 我替换成Package 110 | } else if (Build.VERSION.SDK_INT <= 8) { 111 | mIntent.setAction(Intent.ACTION_VIEW); 112 | mIntent.setClassName("com.android.settings", "com.android.setting.InstalledAppDetails"); 113 | mIntent.putExtra("com.android.settings.ApplicationPkgName", apackage); 114 | } 115 | startActivity(mIntent); 116 | 117 | }else{ 118 | Toast(noapp); 119 | 120 | } 121 | 122 | 123 | } 124 | }); 125 | 126 | 127 | bu2 = (Button) findViewById( R.id.ahook); 128 | bu2.setOnClickListener( new OnClickListener() { 129 | @Override 130 | public void onClick(View view) { 131 | hook_dialog(); 132 | 133 | } 134 | }); 135 | 136 | bu3 = (Button) findViewById( R.id.aelog); 137 | bu3.setOnClickListener( new OnClickListener() { 138 | @Override 139 | public void onClick(View view) { 140 | elog_dialog(); 141 | 142 | } 143 | }); 144 | 145 | 146 | 147 | if(Switch2bool("dump")){ 148 | bu.setText("DUMP"); 149 | }else if(Switch2bool("edump")){ 150 | bu.setText("DUMP"); 151 | } 152 | 153 | 154 | create_sd(); 155 | 156 | if(!isRunInXposed()){//不等于 157 | 158 | AlertDialog.Builder dlg = new AlertDialog.Builder(this); 159 | dlg.setTitle("温馨提示"); 160 | dlg.setMessage("您尚未激活此模块、核心Hook功能将无法使用!"); 161 | dlg.setPositiveButton("OK",null); 162 | 163 | dlg.setNegativeButton("模块管理",new DialogInterface.OnClickListener() { 164 | @Override 165 | public void onClick(DialogInterface dialogInterface, int i) { 166 | 167 | try { 168 | Intent intent = new Intent(); 169 | intent.setComponent(new ComponentName("de.robv.android.xposed.installer", "de.robv.android.xposed.installer.WelcomeActivity")); 170 | startActivity(intent); 171 | } catch (Throwable th) { 172 | Toast("启动失败,可能未安装Xposed或者权限不足∶" + th.toString()); 173 | } 174 | 175 | 176 | }}); 177 | 178 | dlg.show(); 179 | } 180 | 181 | 182 | } 183 | 184 | 185 | 186 | public static Main getContext() { 187 | return INSTANCE; 188 | } 189 | 190 | 191 | //创建标题菜单 192 | @Override 193 | public boolean onCreateOptionsMenu(Menu menu) 194 | { 195 | // Inflate main_menu.xml 196 | MenuInflater inflater = getMenuInflater(); 197 | inflater.inflate(R.menu.menu, menu); 198 | 199 | 200 | 201 | return true; 202 | } 203 | 204 | @Override 205 | public boolean onOptionsItemSelected(MenuItem item) 206 | { 207 | switch (item.getItemId()) 208 | { 209 | 210 | 211 | case R.id.setup: 212 | 213 | setup_dialog(); 214 | 215 | return true; 216 | 217 | case R.id.drill: 218 | drill_dialog(); 219 | return true; 220 | 221 | case R.id.about: 222 | 223 | AlertDialog.Builder dlg = new AlertDialog.Builder(this); 224 | dlg.setTitle("关于"); 225 | dlg.setMessage("Xpu01(v1.2)\nby-mail:7029974@qq.com."); 226 | dlg.setIcon(R.drawable.about); 227 | dlg.setNegativeButton("关闭",null); 228 | 229 | dlg.setPositiveButton("更新",new DialogInterface.OnClickListener() { 230 | @Override 231 | public void onClick(DialogInterface dialogInterface, int i) { 232 | 233 | Uri uri = Uri.parse("https://www.lanzous.com/b0ce6cvri"); 234 | Intent intent = new Intent(Intent.ACTION_VIEW, uri); 235 | startActivity(intent); 236 | 237 | 238 | }}); 239 | 240 | dlg.show(); 241 | return true; 242 | 243 | 244 | case R.id.exit: 245 | System.exit(0); 246 | return true; 247 | 248 | } 249 | return super.onOptionsItemSelected(item); 250 | } 251 | //菜单结束 252 | 253 | private boolean isRunInXposed(){ //判断框架有没有激活 254 | return false; 255 | } 256 | 257 | public void dk() { 258 | 259 | if(apackage != null){ 260 | 261 | configs_ini.edit().putString("packagename",apackage).commit(); 262 | if(Switch2bool("cdump")){ 263 | configs_ini.edit().putString("exp",Environment.getExternalStorageDirectory().getPath()).commit(); 264 | Toast("DUMP输出文件路径:" + Environment.getExternalStorageDirectory().getPath() +"/ 目录下"); 265 | }else{ 266 | configs_ini.edit().putString("exp",Japp.dataDir(apackage)).commit(); 267 | Toast( "DUMP输出文件路径:" + Japp.dataDir(apackage) +"/ 目录下"); 268 | } 269 | 270 | 271 | Intent intent = getPackageManager().getLaunchIntentForPackage(apackage); 272 | startActivity(intent); 273 | }else{ 274 | Toast(noapp); 275 | } 276 | 277 | } 278 | 279 | 280 | 281 | private void setup_dialog() { //设置弹窗 282 | 283 | AlertDialog.Builder dlg = new AlertDialog.Builder(this); 284 | final View gview = getLayoutInflater().inflate(R.layout.setting,null);//获取自定义对话框资源 285 | //获取自定义dialog下的确定和取消按钮资源 286 | //Button okBtnInDialog=(Button)view.findViewById(R.id.register_dialog_ok_btn); 287 | //Button cancelBtnInDialog=(Button)view.findViewById(R.id.register_dialog_cancel_btn); 288 | 289 | Switch sa = (Switch)gview.findViewById( R.id.setting_sysapp); //加载系统应用 290 | sa.setChecked(Switch2bool("sysapp"));//开关状态 291 | sa.setOnCheckedChangeListener(new OnCheckedChangeListener() { 292 | 293 | @Override 294 | public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) { 295 | // TODO Auto-generated method stub 296 | if (isChecked) { 297 | //打开状态 298 | configs_ini.edit().putString("sysapp","true").commit(); 299 | 300 | } else { 301 | //关闭状态 302 | configs_ini.edit().putString("sysapp","false").commit(); 303 | 304 | } 305 | } 306 | }); 307 | 308 | 309 | Switch sb = (Switch)gview.findViewById( R.id.setting_checksh); //加载系统应用 310 | sb.setChecked(Switch2bool("checksh"));//开关状态 311 | sb.setOnCheckedChangeListener(new OnCheckedChangeListener() { 312 | 313 | @Override 314 | public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) { 315 | // TODO Auto-generated method stub 316 | if (isChecked) { 317 | //打开状态 318 | configs_ini.edit().putString("checksh","true").commit(); 319 | 320 | } else { 321 | //关闭状态 322 | configs_ini.edit().putString("checksh","false").commit(); 323 | 324 | } 325 | } 326 | }); 327 | 328 | 329 | Switch se = (Switch)gview.findViewById( R.id.setting_xlog); 330 | se.setChecked(Switch2bool("xlog"));//开关状态 331 | se.setOnCheckedChangeListener(new OnCheckedChangeListener() { 332 | 333 | @Override 334 | public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) { 335 | // TODO Auto-generated method stub 336 | if (isChecked) { 337 | //打开状态 338 | configs_ini.edit().putString("xlog","true").commit(); 339 | 340 | } else { 341 | //关闭状态 342 | configs_ini.edit().putString("xlog","false").commit(); 343 | 344 | } 345 | } 346 | }); 347 | 348 | Switch sf = (Switch)gview.findViewById( R.id.setting_cdump); 349 | sf.setChecked(Switch2bool("cdump"));//开关状态 350 | sf.setOnCheckedChangeListener(new OnCheckedChangeListener() { 351 | 352 | @Override 353 | public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) { 354 | // TODO Auto-generated method stub 355 | if (isChecked) { 356 | //打开状态 357 | configs_ini.edit().putString("cdump","true").commit(); 358 | 359 | } else { 360 | //关闭状态 361 | configs_ini.edit().putString("cdump","false").commit(); 362 | 363 | } 364 | } 365 | }); 366 | 367 | dlg.setTitle("设置"); 368 | dlg.setIcon(R.drawable.settings); 369 | dlg.setView(gview); 370 | dlg.create(); 371 | dlg.setPositiveButton("关闭",null); 372 | dlg.show(); 373 | 374 | } 375 | 376 | 377 | public boolean Switch2bool(String key){ //key是表的key值 378 | if(configs_ini.getString(key,"").equals("true")){ 379 | return true; 380 | } 381 | return false; 382 | } 383 | 384 | 385 | private void drill_dialog() { //模块弹窗 386 | AlertDialog.Builder dlg = new AlertDialog.Builder(this); 387 | final View gview = getLayoutInflater().inflate(R.layout.drill,null);//获取自定义对话框资源 388 | //获取自定义dialog下的确定和取消按钮资源 389 | //Button okBtnInDialog=(Button)view.findViewById(R.id.register_dialog_ok_btn); 390 | //Button cancelBtnInDialog=(Button)view.findViewById(R.id.register_dialog_cancel_btn); 391 | 392 | Button extractapk = (Button) gview.findViewById( R.id.extract_apk); //提取apk 393 | extractapk.setOnClickListener( new OnClickListener() { 394 | @Override 395 | public void onClick(View view) { 396 | if(Japp.whet_app(apackage)){ 397 | if(x64.copy_file(Japp.qapklj(apackage),sdexsd + "/apks/" + Japp.appname(apackage) + "_" + Japp.qversion(apackage) + ".apk")){ 398 | Toast("提取成功:" + sdexsd + "/apks/" + Japp.appname(apackage) + "_" + Japp.qversion(apackage) + ".apk"); 399 | }else{ 400 | Toast("提取失败"); 401 | File file = new File(sdexsd + "/apks"); //如果文件夹不存在则创建 402 | if (!file .exists() && !file.isDirectory()){ 403 | file.mkdir(); //创建文件夹 404 | 405 | } 406 | } 407 | }else{ 408 | Toast(noapp); 409 | } 410 | 411 | } 412 | }); 413 | 414 | Button signbase = (Button) gview.findViewById( R.id.sign_base); //复制Base64加密后的签名值 415 | signbase.setOnClickListener( new OnClickListener() { 416 | @Override 417 | public void onClick(View view) { 418 | if(Japp.whet_app(apackage)){ 419 | try 420 | { 421 | String sign = new String(Base64.encode(Japp.qsign(apackage).getBytes("UTF-8"), 0)); 422 | ClipboardManager manager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); 423 | manager.setText(sign); 424 | Toast("已复制到剪切板"); 425 | 426 | } 427 | catch (UnsupportedEncodingException e) 428 | {} 429 | }else{ 430 | Toast(noapp); 431 | } 432 | 433 | } 434 | }); 435 | 436 | Button buninstall = (Button) gview.findViewById( R.id.uninstall); //复制Base64加密后的签名值 437 | buninstall.setOnClickListener( new OnClickListener() { 438 | @Override 439 | public void onClick(View view) { 440 | if(Japp.whet_app(apackage)){ 441 | //卸载应用程序 442 | Intent intent = new Intent(); 443 | intent.setAction(Intent.ACTION_DELETE); 444 | intent.setData(Uri.parse("package:" + apackage)); 445 | startActivity(intent); 446 | 447 | }else{ 448 | Toast(noapp); 449 | } 450 | 451 | } 452 | }); 453 | 454 | 455 | Button bktest = (Button) gview.findViewById( R.id.ktest); //复制Base64加密后的签名值 456 | bktest.setOnClickListener( new OnClickListener() { 457 | @Override 458 | public void onClick(View view) { 459 | if(Japp.whet_app(apackage)){ 460 | ktest_dialog(); 461 | 462 | }else{ 463 | Toast(noapp); 464 | } 465 | 466 | } 467 | }); 468 | 469 | 470 | dlg.setTitle("模块"); 471 | dlg.setIcon(R.drawable.modules); 472 | dlg.setView(gview); 473 | dlg.create(); 474 | dlg.setPositiveButton("关闭",null); 475 | dlg.show(); 476 | 477 | } 478 | 479 | private void hook_dialog() { //hook弹窗 480 | AlertDialog.Builder dlg = new AlertDialog.Builder(this); 481 | final View gview = getLayoutInflater().inflate(R.layout.hook,null);//获取自定义对话框资源 482 | //获取自定义dialog下的确定和取消按钮资源 483 | //Button okBtnInDialog=(Button)view.findViewById(R.id.register_dialog_ok_btn); 484 | //Button cancelBtnInDialog=(Button)view.findViewById(R.id.register_dialog_cancel_btn); 485 | 486 | final Switch es = (Switch)gview.findViewById( R.id.hook_edump); //2脱壳 487 | 488 | final Switch s = (Switch)gview.findViewById( R.id.hook_dump); //脱壳 489 | s.setChecked(Switch2bool("dump"));//开关状态 490 | s.setOnCheckedChangeListener(new OnCheckedChangeListener() { 491 | 492 | @Override 493 | public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) { 494 | // TODO Auto-generated method stub 495 | if (isChecked) { 496 | //打开状态 497 | if(!checkSupport()){ 498 | s.setChecked(false); 499 | Toast("这台设备不支持DUMP脱壳"); 500 | }else{ 501 | es.setChecked(false); 502 | configs_ini.edit().putString("edump","false").commit(); 503 | 504 | bu.setText("DUMP"); 505 | configs_ini.edit().putString("dump","true").commit(); 506 | } 507 | 508 | } else { 509 | //关闭状态 510 | bu.setText("OPEN"); 511 | configs_ini.edit().putString("dump","false").commit(); 512 | 513 | } 514 | } 515 | }); 516 | 517 | 518 | 519 | es.setChecked(Switch2bool("edump"));//开关状态 520 | es.setOnCheckedChangeListener(new OnCheckedChangeListener() { 521 | 522 | @Override 523 | public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) { 524 | // TODO Auto-generated method stub 525 | if (isChecked) { 526 | //打开状态 527 | if(!checkSupport()){ 528 | es.setChecked(false); 529 | Toast("这台设备不支持DUMP脱壳"); 530 | }else{ 531 | s.setChecked(false); 532 | configs_ini.edit().putString("dump","false").commit(); 533 | 534 | bu.setText("DUMP"); 535 | configs_ini.edit().putString("edump","true").commit(); 536 | } 537 | 538 | } else { 539 | //关闭状态 540 | bu.setText("OPEN"); 541 | configs_ini.edit().putString("edump","false").commit(); 542 | 543 | } 544 | } 545 | }); 546 | 547 | 548 | 549 | Switch sa = (Switch)gview.findViewById( R.id.hook_gvpn); //屏蔽抓包检测 550 | sa.setChecked(Switch2bool("gvpu"));//开关状态 551 | sa.setOnCheckedChangeListener(new OnCheckedChangeListener() { 552 | 553 | @Override 554 | public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) { 555 | // TODO Auto-generated method stub 556 | if (isChecked) { 557 | //打开状态 558 | configs_ini.edit().putString("gvpu","true").commit(); 559 | 560 | } else { 561 | //关闭状态 562 | configs_ini.edit().putString("gvpu","false").commit(); 563 | 564 | } 565 | } 566 | }); 567 | 568 | 569 | Switch sb = (Switch)gview.findViewById( R.id.hook_dwtc); //全局定位弹窗 570 | sb.setChecked(Switch2bool("dwtc"));//开关状态 571 | sb.setOnCheckedChangeListener(new OnCheckedChangeListener() { 572 | 573 | @Override 574 | public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) { 575 | // TODO Auto-generated method stub 576 | if (isChecked) { 577 | //打开状态 578 | configs_ini.edit().putString("dwtc","true").commit(); 579 | 580 | } else { 581 | //关闭状态 582 | configs_ini.edit().putString("dwtc","false").commit(); 583 | 584 | } 585 | } 586 | }); 587 | 588 | dlg.setTitle("HookTools"); 589 | dlg.setView(gview); 590 | dlg.create(); 591 | //dlg.setPositiveButton("关闭",null); 592 | dlg.show(); 593 | 594 | } 595 | 596 | private void elog_dialog() { //log弹窗 597 | AlertDialog.Builder dlg = new AlertDialog.Builder(this); 598 | final View gview = getLayoutInflater().inflate(R.layout.elog,null);//获取自定义对话框资源 599 | //获取自定义dialog下的确定和取消按钮资源 600 | //Button okBtnInDialog=(Button)view.findViewById(R.id.register_dialog_ok_btn); 601 | //Button cancelBtnInDialog=(Button)view.findViewById(R.id.register_dialog_cancel_btn); 602 | 603 | logView = (LinearLayout) gview.findViewById(R.id.logView); 604 | Xpu01_log();//添加日志 605 | 606 | 607 | dlg.setTitle("日志"); 608 | dlg.setView(gview); 609 | dlg.create(); 610 | dlg.setPositiveButton("关闭",null); 611 | 612 | dlg.setNegativeButton("删除",new DialogInterface.OnClickListener() { 613 | 614 | @Override 615 | public void onClick(DialogInterface dialogInterface, int i) { 616 | 617 | if(delete_log()){ 618 | Toast("删除成功"); 619 | } else { 620 | Toast("日志为空"); 621 | } 622 | 623 | 624 | }}); 625 | 626 | dlg.show(); 627 | 628 | } 629 | 630 | 631 | public void create_sd() { //创建文件夹 632 | File file = new File(sdexsd);//如果文件夹不存在则创建 633 | if (!file .exists() && !file.isDirectory()){ 634 | file.mkdir(); //创建文件夹 635 | new File(sdexsd + "/apks").mkdir(); //创建文件夹 636 | new File(sdexsd + "/.temp").mkdir(); //创建文件夹 637 | } 638 | } 639 | 640 | 641 | private void logView(String txt) { //添加标签显示ui打印日志 642 | TextView textView = new TextView(this); 643 | textView.setText(txt); 644 | textView.setTextColor(Color.parseColor("#ffffff")); 645 | 646 | textView.setTextIsSelectable(true); 647 | logView.addView(textView); 648 | 649 | } 650 | 651 | 652 | public void Xpu01_log(){ //读取日志 653 | File file = new File("/sdcard/Xpu01/Xpu01.log"); 654 | try{ 655 | BufferedReader br = new BufferedReader(new FileReader(file));//构造一个BufferedReader类来读取文件 656 | String s = null; 657 | while((s = br.readLine())!=null){//使用readLine方法,一次读一行 658 | logView(s); 659 | //result.append(System.lineSeparator()+s); 660 | } 661 | br.close(); 662 | }catch(Exception e){ 663 | e.printStackTrace(); 664 | } 665 | 666 | 667 | } 668 | 669 | public boolean delete_log() { // 删除日志文件 670 | File file = new File("/sdcard/Xpu01/Xpu01.log"); 671 | if (file.exists() && !file.isDirectory() && file.delete()) { 672 | return true; 673 | } 674 | return false; 675 | } 676 | 677 | private void Toast(String str) { 678 | 679 | Toast.makeText(Main.this, str, Toast.LENGTH_SHORT).show(); 680 | 681 | } 682 | 683 | private boolean checkSupport() { //判断支不支持dex 684 | boolean bool = true; 685 | try { 686 | Class.forName("com.android.dex.Dex"); 687 | } catch (ClassNotFoundException classNotFoundException) { 688 | bool = false; 689 | } 690 | return bool; 691 | } 692 | 693 | 694 | 695 | private void initAppst() { 696 | /* 697 | List list = new ArrayList(); 698 | list.add("a1"); 699 | list.add("a2"); 700 | */ 701 | 702 | final String[] items; 703 | 704 | if(Switch2bool("sysapp") == true){ 705 | items = Japp.sygetpacka(); 706 | }else{ 707 | items = Japp.getpacka(); 708 | } 709 | 710 | List listde = Arrays.asList(items);//数组转list 711 | 712 | DialogItemAdapter adapter = new DialogItemAdapter(this ,listde); //传数据 713 | AlertDialog alertDialog = new AlertDialog 714 | .Builder(this) 715 | .setSingleChoiceItems(adapter, 0, new DialogInterface.OnClickListener() { 716 | @Override 717 | public void onClick(DialogInterface dialog, int which) { 718 | 719 | apackage = items[which]; 720 | 721 | appIcon.setImageBitmap(Japp.applcon(items[which])); 722 | 723 | tname.setText("应用名:" + Japp.appname(items[which])); 724 | tpackage.setText("包名:" +items[which]); 725 | tversion.setText("版本:" + Japp.qversion(items[which])); 726 | tsign.setText("签名值:" +Japp.qsignz(items[which])); 727 | tapkuid.setText("应用UID:" + Japp.uid(items[which])); 728 | 729 | if(Switch2bool("checksh")){ 730 | treinforce.setText("加固状态:" + x64.checksh(Japp.qapklj(items[which]))); 731 | } else { 732 | treinforce.setText("加固状态:没有开启识别" ); 733 | } 734 | tsize.setText("安装包大小:" + x64.readableFileSize(x64.fisize(Japp.qapklj(items[which])))); 735 | 736 | tmd5apk.setText("应用MD5:" + x64.getMd5ByFile(Japp.qapklj(items[which]))); 737 | tcrc32apk.setText("应用RCR:" + x64.tohex(Long.parseLong(x64.getcrc32(Japp.qapklj(items[which]))))); 738 | tmd5sign.setText("MD5签名值:" + x64.strMd5(Japp.qsign(items[which]))); 739 | tdatadirectory.setText("数据目录:" + Japp.dataDir(items[which])); 740 | tapkdirectory.setText("APK目录:" + Japp.qapklj(items[which])); 741 | 742 | //Toast(items[which]); 743 | //Toast(String.valueOf(which)); 744 | dialog.dismiss(); 745 | } 746 | }).create(); 747 | 748 | alertDialog.setTitle("应用列表"); 749 | alertDialog.show(); 750 | 751 | } 752 | 753 | private void ktest_dialog() { //信息校验弹窗 754 | AlertDialog.Builder dlg = new AlertDialog.Builder(this); 755 | final View gview = getLayoutInflater().inflate(R.layout.ktest_dialog,null);//获取自定义对话框资源 756 | //获取自定义dialog下的确定和取消按钮资源 757 | //Button okBtnInDialog=(Button)view.findViewById(R.id.register_dialog_ok_btn); 758 | //Button cancelBtnInDialog=(Button)view.findViewById(R.id.register_dialog_cancel_btn); 759 | 760 | final EditText eewjmdu=(EditText) gview.findViewById(R.id.ewjmdu); 761 | 762 | final TextView tdbx=(TextView) gview.findViewById(R.id.dbx); 763 | final EditText eeqmmdu=(EditText) gview.findViewById(R.id.eqmmdu); 764 | 765 | tdbx.setTextColor(gview.getResources().getColor(R.color.mediumseagreen)); 766 | tdbx.setText("校验对象∶" + Japp.appname(apackage)); 767 | 768 | Button bwjmdu = (Button) gview.findViewById( R.id.wjmdu); //复制Base64加密后的签名值 769 | bwjmdu.setOnClickListener( new OnClickListener() { 770 | @Override 771 | public void onClick(View view) { 772 | if(Japp.whet_app(apackage)){ 773 | 774 | if(eewjmdu.getText().toString().length()!=32){ 775 | Toast("文件MD5值填写错误"); 776 | return;//结束该方法 777 | } 778 | 779 | if(x64.getMd5ByFile(Japp.qapklj(apackage)).equals(eewjmdu.getText().toString())){ 780 | tdbx.setTextColor(gview.getResources().getColor(R.color.mediumseagreen)); 781 | tdbx.setText("文件MD5值匹配"); 782 | }else{ 783 | tdbx.setTextColor(gview.getResources().getColor(R.color.deeppink)); 784 | tdbx.setText("文件MD5值不匹配"); 785 | } 786 | 787 | }else{ 788 | Toast(noapp); 789 | } 790 | 791 | } 792 | }); 793 | 794 | 795 | Button bqmmdu = (Button) gview.findViewById( R.id.qmmdu); //复制Base64加密后的签名值 796 | bqmmdu.setOnClickListener( new OnClickListener() { 797 | @Override 798 | public void onClick(View view) { 799 | if(Japp.whet_app(apackage)){ 800 | 801 | if(eeqmmdu.getText().toString().length()!=32){ 802 | Toast("MD5签名值填写错误"); 803 | return;//结束该方法 804 | } 805 | 806 | if(x64.strMd5(Japp.qsign((apackage))).equals(eeqmmdu.getText().toString())){ 807 | tdbx.setTextColor(gview.getResources().getColor(R.color.mediumseagreen)); 808 | tdbx.setText("MD5签名值匹配"); 809 | }else{ 810 | tdbx.setTextColor(gview.getResources().getColor(R.color.deeppink)); 811 | tdbx.setText("MD5签名值不匹配"); 812 | } 813 | 814 | }else{ 815 | Toast(noapp); 816 | } 817 | 818 | } 819 | }); 820 | 821 | 822 | dlg.setTitle("信息校验"); 823 | dlg.setView(gview); 824 | dlg.create(); 825 | dlg.setPositiveButton("关闭",null); 826 | 827 | dlg.show(); 828 | 829 | } 830 | 831 | 832 | 833 | } 834 | 835 | 836 | -------------------------------------------------------------------------------- /src/main/java/com/xpu01/Xpu01hook.java: -------------------------------------------------------------------------------- 1 | package com.xpu01; 2 | 3 | import android.app.*; 4 | import android.content.pm.*; 5 | import android.widget.*; 6 | import de.robv.android.xposed.*; 7 | import de.robv.android.xposed.callbacks.*; 8 | import java.io.*; 9 | import java.lang.reflect.*; 10 | import java.util.*; 11 | 12 | public class Xpu01hook implements IXposedHookLoadPackage 13 | { 14 | XSharedPreferences pini; //XSharedPreferences类去加载指定路径下得xml文件来获取返回数据 15 | 16 | Method c = null; //getDex 17 | Class p; 18 | Method o; 19 | 20 | 21 | 22 | @Override 23 | public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam p1) 24 | { 25 | 26 | if(p1.packageName.equals("com.xpu01")){//判断包名是否存在 27 | XposedHelpers.findAndHookMethod("com.xpu01.Main",p1.classLoader, "isRunInXposed", new XC_MethodReplacement() { 28 | 29 | @Override 30 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { //替换方法 31 | 32 | return new Boolean(true); 33 | } 34 | }); 35 | } 36 | 37 | pini = new XSharedPreferences("com.xpu01","configs"); //包名 文件名 38 | pini.reload(); //初始化加载数据 39 | 40 | dwtc(p1.packageName);//定位弹窗 41 | 42 | if(p1.packageName.equals(pini.getString("packagename",null))){//判断包名是否存在 43 | dump(p1);//脱壳 44 | edump();//脱壳2 45 | vpn(); //屏蔽VPN 46 | //dwtc(p1.packageName);//定位弹窗 47 | }else{ 48 | //XposedBridge.log("ydll_No package name");//包名不存在 49 | return;//返回 50 | } 51 | 52 | 53 | 54 | } 55 | 56 | 57 | 58 | private void vpn()//hook vpn方法 59 | { 60 | if(!pini.getString("gvpu","").equals("true")) //开关 判断是否开启 !取反 等于 就是不等于 真等于 61 | return; 62 | 63 | 64 | try { 65 | XposedHelpers.findAndHookMethod(Class.forName("java.net.NetworkInterface"), //类名 66 | "getName", // hook方法名 67 | new XC_MethodHook() { 68 | 69 | @Override 70 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { //方法前执行 71 | super.beforeHookedMethod(param); 72 | 73 | //这样设置函数的返回值 74 | param.setResult("rmnet_data1"); 75 | } 76 | }); 77 | 78 | } catch (Throwable e) { 79 | throw new NoClassDefFoundError(e.getMessage()); 80 | } 81 | 82 | } 83 | 84 | 85 | 86 | 87 | 88 | private void dwtc(final String pack)//hook 定位弹窗方法 89 | { 90 | if(!pini.getString("dwtc","").equals("true")) //开关 判断是否开启 !取反 等于 就是不等于 真等于 91 | return; 92 | 93 | 94 | try { 95 | XposedHelpers.findAndHookMethod(Dialog.class, //类名 96 | "show", // hook方法名 97 | new XC_MethodHook() { 98 | 99 | @Override 100 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { //方法前执行 101 | printStack(pack, "Dialog"); 102 | } 103 | }); 104 | 105 | } catch (Throwable e) { 106 | throw new NoClassDefFoundError(e.getMessage()); 107 | } 108 | 109 | 110 | 111 | try { 112 | XposedHelpers.findAndHookMethod(Toast.class, //类名 113 | "show", // hook方法名 114 | new XC_MethodHook() { 115 | 116 | @Override 117 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { //方法前执行 118 | printStack(pack, "show"); 119 | } 120 | }); 121 | 122 | } catch (Throwable e) { 123 | throw new NoClassDefFoundError(e.getMessage()); 124 | } 125 | 126 | 127 | } 128 | 129 | 130 | 131 | private void printStack(String pack, String name) 132 | { 133 | StringBuilder localStringBuilder = new StringBuilder(); 134 | localStringBuilder.append(">>>>[Xpu01弹窗定位]> "); 135 | localStringBuilder.append(pack); 136 | localStringBuilder.append(" -> "); 137 | localStringBuilder.append(name); 138 | localStringBuilder.append(".show()"); 139 | Xpu01_log(localStringBuilder.toString()); 140 | 141 | boolean canPrint = false; 142 | for (StackTraceElement element : new Exception().getStackTrace()) { 143 | if (!canPrint) { 144 | if (element.getMethodName().equals("show")) { 145 | canPrint = true; 146 | } else { 147 | } 148 | } 149 | StringBuilder stringBuilder = new StringBuilder(); 150 | stringBuilder.append("> "); 151 | stringBuilder.append(element); 152 | Xpu01_log(stringBuilder.toString()); 153 | 154 | } 155 | Xpu01_log("<<<<<<<<<[结束]<"); 156 | 157 | 158 | 159 | } 160 | 161 | 162 | 163 | private void dump(final XC_LoadPackage.LoadPackageParam pa){//hook dump脱壳 164 | 165 | if(!pini.getString("dump","").equals("true")) //总开关 判断是否开启 !取反 等于 就是不等于 真等于 166 | return; 167 | 168 | 169 | dump_i(); 170 | 171 | de.robv.android.xposed.XposedHelpers.findAndHookMethod( "java.lang.ClassLoader" , pa.classLoader,"loadClass" ,String.class ,boolean.class, 172 | 173 | new XC_MethodHook () 174 | { 175 | 176 | @Override 177 | protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) //执行方法后执行 178 | { 179 | 180 | 181 | 182 | //XposedBridge.log( " after hook : " ) ; 183 | boolean isSystemClass=true; 184 | Class cls =(Class)param.getResult(); 185 | if(cls==null)return; 186 | 187 | 188 | try 189 | { 190 | Class.forName(cls.getName(),false,Xpu01hook.class.getClassLoader().getSystemClassLoader()); 191 | //.loadClass(); 192 | } 193 | catch (ClassNotFoundException e) 194 | { 195 | 196 | isSystemClass=false;} 197 | 198 | if(isSystemClass) 199 | return; 200 | 201 | 202 | 203 | try 204 | { 205 | Object dex = c.invoke(cls); 206 | 207 | 208 | 209 | byte[] data = (byte[])o.invoke(dex) ; 210 | 211 | 212 | if(data!= null ) 213 | { 214 | 215 | File f=new File( pini.getString("exp","/sdcard"), "Xpu01_dump" +Integer.toHexString(data.length)+ ".dex" ); //Integer.toHexString 整数转到十六进制 216 | if(!f.exists()) 217 | { 218 | dump_b( data,f.getAbsolutePath()); 219 | //XposedBridge.log(dex.toString()); 220 | } 221 | } 222 | } 223 | catch (Exception e) 224 | { 225 | XposedBridge.log(e.toString()); 226 | } 227 | } 228 | 229 | 230 | 231 | 232 | } 233 | 234 | ); 235 | 236 | } 237 | 238 | public void dump_i()//initRefect 239 | { 240 | try 241 | { 242 | p = Class.forName("com.android.dex.Dex"); 243 | 244 | o = p.getDeclaredMethod("getBytes"); 245 | 246 | c = Class.class.getDeclaredMethod("getDex" ); 247 | } 248 | catch ( Exception e) 249 | { 250 | 251 | XposedBridge.log(e.toString()); 252 | 253 | } 254 | } 255 | 256 | 257 | public void dump_b(byte [] data, String path )//写数据 Byte 258 | 259 | { 260 | 261 | try 262 | { 263 | OutputStream os = new FileOutputStream(path); 264 | os.write( data); 265 | os.close(); 266 | 267 | //Xpu01hook.Toast(Xpu01hook.this,"执行DUMP脱壳成功"); 268 | } 269 | catch (Exception e) 270 | { 271 | //Xpu01hook.Toast(Xpu01hook.this,"执行DUMP脱壳失败" + e.toString()); 272 | } 273 | 274 | } 275 | 276 | 277 | private void edump()//第二脱壳 278 | { 279 | if(!pini.getString("edump","").equals("true")) //开关 判断是否开启 !取反 等于 就是不等于 真等于 280 | return; 281 | 282 | try 283 | { 284 | final Class g = Class.forName("android.os.Bundle"); 285 | 286 | Class cls2 = Class.forName("android.app.Activity"); 287 | XposedHelpers.findAndHookMethod(cls2, "onCreate",g, new XC_MethodHook() { 288 | 289 | @Override 290 | protected void afterHookedMethod(XC_MethodHook.MethodHookParam paramMethodHookParam) {//方法后执行 291 | 292 | hack2Dex(paramMethodHookParam.thisObject.getClass()); 293 | //Xpu01hook.Toast(Xpu01hook.this,"执行成功"); 294 | } 295 | }); 296 | 297 | } 298 | catch (ClassNotFoundException e) 299 | {} 300 | } 301 | 302 | 303 | private void hack2Dex(Class cls) { 304 | try { 305 | byte[] bArr = (byte[]) Class.forName("com.android.dex.Dex").getDeclaredMethod("getBytes", new Class[0]).invoke(Class.forName("java.lang.Class").getMethod("getDex", new Class[0]).invoke(cls, new Object[0]), new Object[0]); 306 | File file = new File(pini.getString("exp","/sdcard"), "Xpu01_2dump" +Integer.toHexString(bArr.length)+ ".dex" ); 307 | if (!file.exists()) { 308 | file.createNewFile(); 309 | } 310 | FileOutputStream fileOutputStream = new FileOutputStream(file); 311 | fileOutputStream.write(bArr); 312 | fileOutputStream.flush(); 313 | fileOutputStream.close(); 314 | //Xpu01hook.Toast(Xpu01hook.this,"执行2DUMP脱壳成功"); 315 | } catch (Exception e) { 316 | //Toast.makeText(context, e.toString(), 0).show(); 317 | //Xpu01hook.Toast(Xpu01hook.this,"执行2DUMP脱壳失败" + e.toString()); 318 | } 319 | } 320 | 321 | 322 | private void Xpu01_log(String s) { 323 | if(pini.getString("xlog","").equals("true")){ //开关 判断是否开启打印到xp框架日志 324 | XposedBridge.log(s); 325 | }else{ 326 | 327 | File file = new File("/sdcard/Xpu01/Xpu01.log"); 328 | BufferedWriter bw = null; 329 | try { 330 | bw = new BufferedWriter(new FileWriter(file,true)); 331 | bw.write("I: " +s + "\n"); 332 | bw.close(); 333 | } catch (IOException e) { 334 | e.printStackTrace(); 335 | } 336 | 337 | } 338 | } 339 | 340 | 341 | } 342 | 343 | -------------------------------------------------------------------------------- /src/main/java/com/xpu01/x64.java: -------------------------------------------------------------------------------- 1 | package com.xpu01; 2 | 3 | import java.io.*; 4 | import java.math.*; 5 | import java.nio.*; 6 | import java.nio.channels.*; 7 | import java.security.*; 8 | import java.text.*; 9 | import java.util.zip.*; 10 | 11 | public class x64 12 | { 13 | 14 | 15 | public static boolean copy_file(String sourcePath, String targetPath) {//复制文件 文件路径 保存后的路径文件名 16 | if (!new File(sourcePath).exists()) { 17 | return false; 18 | } 19 | int bytesum = 0; 20 | try { 21 | InputStream inStream = new FileInputStream(sourcePath); 22 | FileOutputStream fs = new FileOutputStream(targetPath); 23 | byte[] buffer = new byte[1444]; 24 | while (true) { 25 | int byteread = inStream.read(buffer); 26 | if (byteread != -1) { 27 | bytesum += byteread; 28 | fs.write(buffer, 0, byteread); 29 | } else { 30 | inStream.close(); 31 | fs.close(); 32 | return true; 33 | } 34 | } 35 | } catch (Exception e) { 36 | e.printStackTrace(); 37 | return false; 38 | } 39 | } 40 | 41 | public static String readableFileSize(long size) { //文件大小字节转换 42 | if (size <= 0) return "0"; 43 | final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"}; 44 | int digitGroups = (int) (Math.log10(size) / Math.log10(1024)); 45 | return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups]; 46 | } 47 | 48 | 49 | 50 | public static long fisize(String filePath) {//取文件大小尺寸 51 | File file = new File(filePath); 52 | try { 53 | if (file.isDirectory()) { 54 | return getFileSizes(file); 55 | } 56 | return getFileSize(file); 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | return 0; 60 | } 61 | } 62 | 63 | private static long getFileSize(File file) throws Exception { 64 | if (file.exists()) { 65 | return (long) new FileInputStream(file).available(); 66 | } 67 | file.createNewFile(); 68 | return 0; 69 | } 70 | 71 | private static long getFileSizes(File f) throws Exception { 72 | long fileSize; 73 | long size = 0; 74 | File[] flist = f.listFiles(); 75 | for (int i = 0; i < flist.length; i++) { 76 | if (flist[i].isDirectory()) { 77 | fileSize = getFileSizes(flist[i]); 78 | } else { 79 | fileSize = getFileSize(flist[i]); 80 | } 81 | size += fileSize; 82 | } 83 | return size; 84 | } 85 | 86 | 87 | 88 | public static String checksh(String sourcePath) { //查壳 文件路径 89 | String dat= drtext(sourcePath); 90 | 91 | String nm = "360加固|腾讯加固|爱加密|百度加固|阿里加固|几维安全加固|通付盾加固|梆梆加固|"; 92 | String[] identify_nm = nm.split("\\|");// 分割文本 93 | String tz = "libjiagu.so|libshell.so|libexec.so|libbaiduprotect.so|libmobisecx.so|libkwscmm.so|libegis.so|libsecexe.so|"; 94 | String[] identify_tz = tz.split("\\|");// 分割文本 95 | 96 | for (int i = 0; i <= 7; i++) { 97 | if(dat.indexOf(identify_tz[i]) != -1){ //indexOf 由指定位置从前向后查找指定字符串的位置,如果查找到了则返回(第一个字母)位置索引,如果找不到返回-1. 98 | return identify_nm[i]; 99 | 100 | } 101 | } 102 | return "未发现加固"; 103 | } 104 | 105 | 106 | private static String drtext(String filename) { //读入文本文件 返回内容 107 | Exception e; 108 | String res = ""; 109 | if (!new File(filename).exists()) { 110 | return res; 111 | } 112 | try { 113 | FileInputStream fin = new FileInputStream(filename); 114 | int length = fin.available(); 115 | byte[] buffer = new byte[length]; 116 | fin.read(buffer); 117 | String res2 = new String(buffer, 0, length, "utf8"); 118 | try { 119 | fin.close(); 120 | return res2; 121 | } catch (Exception e2) { 122 | e = e2; 123 | res = res2; 124 | e.printStackTrace(); 125 | return res; 126 | } 127 | } catch (Exception e3) { 128 | e = e3; 129 | e.printStackTrace(); 130 | return res; 131 | } 132 | } 133 | 134 | 135 | 136 | 137 | public static String getMd5ByFile(String filePath) { //获取文件md5值 文件路径 138 | String value = null; 139 | FileInputStream in = null; 140 | File file = new File(filePath); 141 | try { 142 | in = new FileInputStream(file); 143 | MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length()); 144 | MessageDigest md5 = MessageDigest.getInstance("MD5"); 145 | md5.update(byteBuffer); 146 | BigInteger bi = new BigInteger(1, md5.digest()); 147 | value = bi.toString(16); 148 | } catch (Exception e) { 149 | e.printStackTrace(); 150 | } finally { 151 | if(null != in) { 152 | try { 153 | in.close(); 154 | } catch (IOException e) { 155 | e.printStackTrace(); 156 | } 157 | } 158 | } 159 | return value; 160 | } 161 | 162 | public static String getcrc32(String filename) { //获取文件crc32值 163 | File file = new File(filename); 164 | CRC32 crc32 = new CRC32(); 165 | //MessageDigest.get 166 | FileInputStream fileInputStream = null; 167 | try { 168 | fileInputStream = new FileInputStream(file); 169 | byte[] buffer = new byte[8192]; 170 | int length; 171 | while ((length = fileInputStream.read(buffer)) != -1) { 172 | crc32.update(buffer,0, length); 173 | } 174 | return crc32.getValue()+""; 175 | 176 | 177 | } catch (FileNotFoundException e) { 178 | e.printStackTrace(); 179 | return null; 180 | } catch (IOException e) { 181 | e.printStackTrace(); 182 | return null; 183 | } finally { 184 | try { 185 | if (fileInputStream != null) 186 | fileInputStream.close(); 187 | } catch (IOException e) { 188 | e.printStackTrace(); 189 | } 190 | } 191 | } 192 | 193 | public static String tohex(long j) { //转到十六进制 194 | try { 195 | String toHexString = Long.toHexString(j); 196 | return toHexString.length() < 2 ? "0" + toHexString : toHexString; 197 | } catch (Exception e) { 198 | return ""; 199 | } 200 | } 201 | 202 | public static String strMd5(String str) { //字符串获取md5 203 | MessageDigest messageDigest = null; 204 | try { 205 | messageDigest = MessageDigest.getInstance("MD5"); 206 | messageDigest.reset(); 207 | messageDigest.update(str.getBytes("UTF-8")); 208 | } catch (NoSuchAlgorithmException e) { 209 | e.printStackTrace(); 210 | } catch (UnsupportedEncodingException e) { 211 | e.printStackTrace(); 212 | } 213 | byte[] byteArray = messageDigest.digest(); 214 | StringBuffer md5StrBuff = new StringBuffer(); 215 | for (int i = 0; i < byteArray.length; i++) { 216 | if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) 217 | 218 | md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i])); 219 | else 220 | md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i])); 221 | } 222 | return md5StrBuff.toString(); 223 | } 224 | 225 | 226 | 227 | 228 | } 229 | -------------------------------------------------------------------------------- /src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eksenior/xpu01/152ac26e4578e38090792f080f0f01e5a7e5c4b8/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eksenior/xpu01/152ac26e4578e38090792f080f0f01e5a7e5c4b8/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eksenior/xpu01/152ac26e4578e38090792f080f0f01e5a7e5c4b8/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eksenior/xpu01/152ac26e4578e38090792f080f0f01e5a7e5c4b8/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eksenior/xpu01/152ac26e4578e38090792f080f0f01e5a7e5c4b8/src/main/res/drawable/about.png -------------------------------------------------------------------------------- /src/main/res/drawable/modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eksenior/xpu01/152ac26e4578e38090792f080f0f01e5a7e5c4b8/src/main/res/drawable/modules.png -------------------------------------------------------------------------------- /src/main/res/drawable/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eksenior/xpu01/152ac26e4578e38090792f080f0f01e5a7e5c4b8/src/main/res/drawable/settings.png -------------------------------------------------------------------------------- /src/main/res/layout/drill.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 |