Xpatch
是一个重打包工具,可以将自己的代码注入到第三方apk中。在使用的时候,遇到下面几个问题会导致打包失败:
1、原app没有Application
2、原app有自己的static代码块
3、原app有dex完整性校验
这篇 https://bbs.pediy.com/thread-251422.htm 文章对Xpatch
流程梳理的很详细,实现上是在原app的Application
入口位置添加static代码块,
抢在原app代码执行前注入自己代码。
但是选择这个注入点会有一些问题,如果原应用没有Application
就会注入失败;或者原应用加壳了的,已经有 static 代码块,这个时候注入的代码执行时机也会有问题;
还有种情况,这样是修改了原应用的dex文件,如果应用对dex完整性做了检验,打包后也会失败。为了解决这些问题,想到了两种解决方式:
1、模拟加壳原理,将自己壳代码打包成dex文件,合并进原来apk中,并且将 AndroidManifest.xml 中应用入口改成壳的Application
,通过壳将原app加载起来;
2、利用MutilDex机制,生成一个我们自己的Application
类, 并且继承自 AndroidManifest.xml 中声明的Application
, 重写 attachBaseContext 函数,
,在调用父类的 attachBaseContext 函数之前加入自己逻辑,打包成dex文件,修改 AndroidManifest.xml 中应用入口为我们自己的Application
。
综合下来,第一种方法显然麻烦很多,这里使用第二种方式。
代码很简单,分三部分:
1、自己写一个只包含 Application 的app,目的是为了取到dex
2、修改自定义的 Application 继承于原来 Application,如果原来没有 Application 就不用修改
3、修改 AndroidManifest.xml 入口为我们自定义的 Application
第一部分代码很简单
public class StubApplication extends Application { private static final String TAG = "StubApplication"; @Override protected void attachBaseContext(Context base) { //这里可以写自己的代码逻辑,比如注入hook框架,过签名校验等操作 super.attachBaseContext(base); Log.i(TAG, "StubApplication attachBaseContext..."); } }
第二部分代码也很简单,就是提取出第一部分生成的dex文件,转为smali之后修改继承的父类就可以了
private File modifyStubDex() throws Exception { //stub.apk就是我们第一步打包的apk File stubAppFile = new File(new File("apks"), "stub.apk"); File stubDexSmaliDir = new File(new File("work"), "stubSmali"); //将dex转为smali Util.decodeDex2Smali(stubAppFile, "classes.dex", stubDexSmaliDir); //遍历所有文件,找到StubApplication.smali文件 Iterator<File> fileIterator = FileUtils.iterateFiles(stubDexSmaliDir, new String[]{"smali"}, true); File stubApplicationFile = null; while (fileIterator.hasNext()) { File next = fileIterator.next(); if (StringUtils.containsIgnoreCase(next.getName(), "StubApplication")) { stubApplicationFile = next.getAbsoluteFile(); break; } } if (Objects.isNull(stubApplicationFile)) { System.out.println("没找到StubApplication.smali文件"); return null; } //找到之后,修改继承的父类 List<String> lines = FileUtils.readLines(stubApplicationFile, Charsets.UTF_8); String originApplicationName = StringUtils.replace(applicationName, ".", "/"); originApplicationName = StringUtils.prependIfMissing(originApplicationName, ".super L"); originApplicationName = StringUtils.appendIfMissing(originApplicationName, ";"); lines.set(1, originApplicationName); String invoke = "Landroid/app/Application;"; for (int i = 0; i < lines.size(); i++) { if (StringUtils.containsIgnoreCase(lines.get(i).trim(), invoke)) { lines.set(i, StringUtils.replace(lines.get(i), "Landroid/app/Application;", StringUtils.remove(originApplicationName, ".super "))); } } //将修改完后的smali重打包成dex FileUtils.writeLines(stubApplicationFile, lines); File stubDexFile = new File(new File("work"), "stubDex.dex"); SmaliBuilder.build(new ExtFile(stubDexSmaliDir), stubDexFile, Opcodes.getDefault().api); return stubDexFile; }
第三步就是修改 AndroidManifest.xml 文件中的应用入口为我们自定义的 Application了。
修改 AndroidManifest.xml 可以直接在二进制层面改,也可以转为xml之后再修改,不过现在有些app做了对坑,直接转xml可能报错。
刚开始用的四哥的 AXMLEditor 进行二进制层面修改,修改的确成功了,但是重打包安装之后一启动就报错,看错误日志应该是没找到Application类,查了半天也没解决,
只能先暂时转为xml进行修改。
private static File editManifest(File originApk) throws Exception { //使用 net.dongliu:apk-parser 对xml进行提取 String originAPKManifestXml = ApkParsers.getManifestXml(originApk); Document document = loadDocument(new ByteArrayInputStream(originAPKManifestXml.getBytes(Charset.forName("utf-8")))); Element applicationElement = (Element) document.getElementsByTagName("application").item(0); applicationElement.setAttribute("android:name", stubAppApplicationName); File file = new File(new File("work"), "AndroidManifest.xml"); TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource source = new DOMSource(document); StreamResult streamResult = new StreamResult(file); transformer.transform(source, streamResult); return file; }
上面就是所有的流程,做完后就是集成到Xpatch中了,Xpatch中每个步骤都是一个task,所以集成还是蛮方便的,只需要将上面步骤写到一个task中,再做如下修改,
// 1. modify the apk dex file to make xposed can run in it //mXpatchTasks.add(new ApkModifyTask(showAllLogs, keepBuildFiles, unzipApkFilePath, applicationName, dexFileCount)); mXpatchTasks.add(new ChangeApplicationTask(applicationName, dexFileCount, unzipApkFilePath));
这样就完事了。最后测试没有大规模测试,只做了业务需要的,基本是可以解决最开始遇到的几个问题。