Github: https://github.com/skyun1314/haha
前几天突然看到一个牛逼的工具Xpatch。由于Android 9.0以上手机不好root,不能装xposed 。虽然有一个太极可以免root加载xposed模块,但是里面的模块是有限制的,必须的上传到太极官网,审核通过才能使用 xposed模块。所以 平时 调试app在高版本手机上 兼容性的时候很麻烦。虽然可以重打包测试。但是遇到几百兆的app的时候就特别无力了。Xpatch确实是一个不错的选择,但是他不是app,为了使用方便我把它改成了app,并针对手机特性 结合Xposed代码做了一些优化。
效果图:
优化点:
1、需要加载Xposed模块的app可以选择开启那些模块,而不需要自身开启读取sd卡权限,因为咱们使用了 ContentProvider!
2、为了加快速度不反编译smali代码,使用dexlib2库查找manifest中定义的 application 最终父类 所在的dex。 并查看当前application中有没有静态代码块,
同时使用 baksmali d 参数 只反编译一个smali文件从而插入入口代码。最后使用 DexMerger 库 合并dex。速度大大提升
3、使用AXmlConverter 库修改manifest 二进制文件
关键代码
使用ContentProvider 保存模块开启状态
public synchronized void updateModulesList(Context showToast) { try { String[] strings = {MODULES_LIST_FILE, ENABLED_MODULES_LIST_FILE}; for (int i = 0; i < strings.length; i++) { File file = new File(strings[i]); if (!file.getParentFile().exists()) { file.getParentFile().mkdirs(); } } Cursor query = showToast.getContentResolver().query(StudentsProvider.CONTENT_URIstudents, null, null, null, null); if (query != null && query.getCount() > 0) { showToast.getContentResolver().delete(StudentsProvider.CONTENT_URIstudents, "_id>0", null); } PrintWriter modulesList = new PrintWriter(MODULES_LIST_FILE); PrintWriter enabledModulesList = new PrintWriter(ENABLED_MODULES_LIST_FILE); List<InstalledModule> enabledModules = getEnabledModules(); for (InstalledModule module : enabledModules) { modulesList.println(module.app.sourceDir); try { String installer = mPm.getInstallerPackageName(module.app.packageName); if (!PLAY_STORE_PACKAGE.equals(installer)) { enabledModulesList.println(module.app.packageName); ContentValues values = new ContentValues(); values.put(StudentsProvider.NAME, module.app.packageName); values.put(StudentsProvider.GRADE, module.app.sourceDir); Uri uri = showToast.getContentResolver().insert( StudentsProvider.CONTENT_URIstudents, values); } } catch (IllegalArgumentException ignored) { // In rare cases, the package might not be installed anymore at this point, // so the PackageManager can't return its installer package name. } } modulesList.close(); enabledModulesList.close(); FileUtils.setPermissions(MODULES_LIST_FILE, 00664, -1, -1); FileUtils.setPermissions(ENABLED_MODULES_LIST_FILE, 00664, -1, -1); } catch (IOException e) { hahahaha.Log.e(e.toString()); } }
使用dexlib2 解析 class和method
public static List<String> getClass(String input) { List<String>strings=new ArrayList<>(); try { File file = new File(input); MultiDexContainer<? extends DexBackedDexFile> container = DexFileFactory.loadDexContainer(file, null); MultiDexContainer.DexEntry<? extends DexBackedDexFile> dexEntry = container.getEntry(container.getDexEntryNames().get(0)); assert dexEntry != null; DexBackedDexFile dexFile = dexEntry.getDexFile(); for (ClassDef classDef : dexFile.getClasses()) { strings.add(classDef.getType()); // System.out.println(classDef.getType()); } } catch (Exception e) { e.printStackTrace(); } return strings; } public static List<String> getMethods(String input) { List<String>strings=new ArrayList<>(); try { File file = new File(input); MultiDexContainer<? extends DexBackedDexFile> container = DexFileFactory.loadDexContainer(file, null); MultiDexContainer.DexEntry<? extends DexBackedDexFile> dexEntry = container.getEntry(container.getDexEntryNames().get(0)); assert dexEntry != null; DexBackedDexFile dexFile = dexEntry.getDexFile(); for (Reference reference: dexFile.getReferences( ReferenceType.METHOD)) { // System.out.println(ReferenceUtil.getReferenceString(reference)); strings.add(ReferenceUtil.getReferenceString(reference)); } } catch (Exception e) { e.printStackTrace(); } return strings; }
使用axml 修改 manifest
// 修改app名称 @Override public NodeVisitor visitChild(String ns, String name) {// manifest 下面的说有大标签 if ("application".equals(name)) { return new NodeVisitor(super.visitChild(ns, name)) { @Override public void visitEnd() { NodeVisitor meta = super.visitChild("http://schemas.android.com/apk/res/android", "meta-data"); meta.visitContentAttr("http://schemas.android.com/apk/res/android", "name", 16842755, 3, meta_data_key); meta.visitContentAttr("http://schemas.android.com/apk/res/android", "value", 16842788, 3, meta_data_value); if (!has_Application) { super.visitContentAttr("http://schemas.android.com/apk/res/android", "name", 16842755, 3, Application_name); } super.visitEnd(); } @Override public void visitContentAttr(String ns, String name, int resourceId, int type,//application 这一行 Object obj) { if ("name".equals(name)) { yuan_Application=obj.toString(); if(obj.toString().startsWith(".")){ yuan_Application=yuan_packageName+yuan_Application; } has_Application = true; hahahaha.Log.e("visitContentAttr:" + name + " " + obj + " " + ns + " " + resourceId + " " + type); } super.visitContentAttr(ns, name, resourceId, type, obj); } @Override public NodeVisitor visitChild(String ns, String name) {//application 下面所有大标签 return new NodeVisitor(super.visitChild(ns, name)) { @Override public void visitContentAttr(String ns, String name, int resourceId, int type, Object obj) { // System.out.println("aaaa_visitChild:"+ns+" "+name+" "+resourceId+" "+type+" "+obj); super.visitContentAttr(ns, name, resourceId, type, obj); } @Override public NodeVisitor visitChild(String ns, String name) { // System.out.println("xxxx_visitChild:"+ns+" "+name); return super.visitChild(ns, name); } }; } }; } return super.visitChild(ns, name); } }; } });
[公告]安全测试和项目外包请将项目需求发到看雪企服平台:https://qifu.kanxue.com
最后于 5小时前 被skyun编辑 ,原因: