diff --git a/README.md b/README.md index c02a2de..7fbfc90 100644 --- a/README.md +++ b/README.md @@ -1,247 +1,69 @@ -### [**English Version**](https://github.com/WindySha/Xpatch/blob/6ec0f3c16128dda46ab05bdd915d66ebbdaaf9fc/README_en.md) +# What is MMPatch -# Android App破解工具Xpatch的使用方法 +fork from [Xpatch][11] -## Xpatch概述 -Xpatch用来重新签名打包Apk文件,使重打包后的Apk能加载安装在系统里的Xposed插件,从而实现免Root Hook任意App。 +MMPatch is a jar tool which is used to repackage the apk file. Then, the new apk can load any Xposed modules installed in the android system. -## Xpatch基本原理 -Xpatch的原理是对Apk文件进行二次打包,重新签名,并生成一个新的apk文件。 -在Apk二次打包过程中,插入加载Xposed插件的逻辑,这样,新的Apk文件就可以加载任意Xposed插件,从而实现免Root Hook任意App的Java代码。 +This is a way to use Xposed modules without root your device. -1.0~1.4版本,Hook框架使用的是Lody的[whale](https://github.com/asLody/whale) -2.0版本开始,Hook框架底层使用的是ganyao114的[SandHook](https://github.com/ganyao114/SandHook)。 -3.0版本开始,默认使用SandHook,同时,兼容切换为whale +It is easy way to modify one app using xposed module. And any apps changed by MMPatch can load every modules downloaded in the [Xposed Module Repository](https://repo.xposed.info/). -## Xpatch工具包下载 -[下载最新的Xpatch Jar包][1] -或者进入Releases页面下载指定版本:[releases][2] +# Benefits -## Xpatch App版本(Xposed Tool)下载 -[XposedTool][16] [下载XposedTool Apk][15] +1. Use xposed modules without your device; +2. Modify any apps without root your device. -## Xpatch使用方法 -Xpatch项目最终生成物是一个Jar包,此Jar使用起来非常简单,只需要一行命令,一个接入xposed hook功能的apk就生成: +# How to use + +1. Download the latest jar file from the [release page](https://github.com/327135569/MMPatch/releases); +2. Run this command in the Windows/Mac console: ``` -$ java -jar XpatchJar包路径 apk文件路径 +$ java -jar mmpatch.jar source.apk +``` +Then, a new apk named `source-xposed-signed.apk` in the same folder as `source.apk`. -For example: -$ java -jar ../xpatch.jar ../Test.apk +More command details can be found when no parameter is added, eg: +``` +$ java -jar mmpatch.jar ``` -这条命令之后,在原apk文件(Test.apk)相同的文件夹中,会生成一个名称为`Test-xposed-signed.apk`的新apk,这就是重新签名之后的支持xposed插件的apk。 +# How to manage Xposed modules -**Note:** 由于签名与原签名不一致,因此需要先卸载掉系统上已经安装的原apk,才能安装这个Xpatch后的apk +When the new apk is installed in the device, It will load all the Xposed modules installed in the device when it's process started. -当然,也可以增加`-o`参数,指定新apk生成的路径: -``` -$ java -jar ../xpatch.jar ../Test.apk -o ../new-Test.apk -``` +But you can manage the installed Xposed modules on/off state by a file in the storage. +The file path is `/sdcard/xpmodules.list`. -更多参数类型可以使用--help查看,或者不输入任何参数运行jar包: -``` -$ java -jar ../xpatch.jar --h(可省略) -``` -这行命令之后得到结果(v1.0-v2.0): -``` -Please choose one apk file you want to process. -options: - -f,--force force overwrite - -h,--help Print this help message - -k,--keep not delete the jar file that is changed by dex2jar - and the apk zip files - -l,--log show all the debug logs - -o,--output output .apk file, default is $source_apk_dir/[file - -name]-xposed-signed.apk -``` - -## Xposed模块开关控制的两种方法 -### 1. 手动修改sdcard文件控制模块开关 -当新apk安装到系统之后,应用启动时,默认会加载所有已安装的Xposed插件(Xposed Module)。 - -一般情况下,Xposed插件中都会对包名过滤,有些Xposed插件有界面,并且在界面上可以设置开关,所以默认启用所有的Xposed插件的方式,大多数情形下都可行。 - -但在少数情况可能会出现问题,比如,同一个应用安装有多个Xposed插件(wechat插件就非常多),并且都没有独立的开关界面,同时启用这些插件可能会产生冲突。 - -为了解决此问题,当应用启动时,会查找系统中所有已安装的Xposed插件,并在文件目录下生成一个文件 -`mnt/sdcard/xposed_config/modules.list`,记录这些Xposed插件App。 -比如: +When the new app started, it will search all the installed Xposed modules and write the the module app name and the module application name into this file. (`/sdcard/xpmodules.list`) +eg: ``` com.blanke.mdwechat#MDWechat -com.example.wx_plug_in3#畅玩微信 liubaoyua.customtext#文本自定义 ``` -记录的方式是:`插件app包名#插件app名称` - -需要禁用某个插件,只需要修改此文件,在该插件包名前面增加一个`#`号即可。 - -比如,需要禁用`畅玩微信`和`文本自定义`两个插件,只需要修改该文本文件,增加一个`#`号即可: +Each line of this file is Application Name#App Name. +You can disable a Xposed module by add `#` before the Application Name, eg: ``` -com.blanke.mdwechat#MDWechat -#com.example.wx_plug_in3#畅玩微信 +#com.blanke.mdwechat#MDWechat +liubaoyua.customtext#文本自定义 +``` +This means the MDWechat Xposed module is disabled. + +``` +#com.blanke.mdwechat#MDWechat #liubaoyua.customtext#文本自定义 ``` -如果需要禁用所有插件,只需在所有的包名前面增加`#`。 +This means all Xposed modules are disabled. -**注意:** -有些App没有获取到sd卡文件读写权限,这会导致无法读取modules.list配置文件,此时会默认启用所有插件。这种情况下,需要手动打开app的文件读写权限。 -### 2. 通过Xposed Tool App控制模块开关 -下载并安装Xpatch App(Xposed Tool) -[点我下载XposedTool Apk][15] -通过`Xposed模块管理`页面来控制模块开关。(原理跟方法1一致) -![Screenshot.png](https://upload-images.jianshu.io/upload_images/1639238-84d7a1dd814f314a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300) - -## 可用的Xposed模块示例 - - - [MDWechat][8] - - [文本自定义][9] - - [RemoveVideoAdsPlugin](https://github.com/WindySha/RemoveVideoAdsPlugin) - - **你自己编写的Xposed模块** +Note: The target app must have file system access permission. Otherwise this file will not be created, and all xposed modules are enabled. -## 源码解析 -Xpatch源码解析博文已发布到个人技术公众号**Android葵花宝典**上。 -扫一扫关注公众号,即可查看: -![](https://upload-images.jianshu.io/upload_images/1639238-ab6e0fceabfffdda.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/180) - -## 其他 -assets目录下的classes.dex是来加载设备上已安装的Xposed插件,其源代码也已经开源: -[xposed_module_loader](https://github.com/WindySha/xposed_module_loader) -欢迎star and fork. - -## 局限性 -Xpatch是基于apk二次打包实现的,而且使用到了dex2Jar工具,因此,也存在不少的局限性。大概有以下几点: - -1. Hook框架默认使用的是SandHook,此框架存在一些不稳定性,在少数机型上hook可能会崩溃。 -2. 对于校验了文件完整性的app,重打包后可能无法启动; -3. Xposed Hook框架暂时不支持Dalvik虚拟机。 -4. 暂时不支持Xposed插件中的资源Hook。 - -## Technology Discussion -**QQ Group: 977513757** -or -**Post comments under this article: [Xpatch: 免Root实现App加载Xposed插件的一种方案](https://windysha.github.io/2019/04/18/Xpatch-%E5%85%8DRoot%E5%AE%9E%E7%8E%B0App%E5%8A%A0%E8%BD%BDXposed%E6%8F%92%E4%BB%B6%E7%9A%84%E4%B8%80%E7%A7%8D%E6%96%B9%E6%A1%88/)** - -## 功能更新 - ----- -### 1. 2019/4/15 updated -增加自动破解签名检验的功能,此功能默认开启,如果需要关闭可以增加`-c`即可,比如: -``` -$ java -jar ../xpatch.jar ../Test.apk -c -``` -通过help(-h)可以查看到: ->options: -> -c,--crach disable craching the apk's signature. - -### 2. 2019/4/25 updated -增加将Xposed modules打包到apk中的功能 -通过help(-h)可以查看到: - >-xm,--xposed-modules the xposed mpdule files to be packaged into the ap - > k, multi files should be seperated by :(mac) or ;( - > win) - -使用方式为在命令后面增加`-xm path`即可,比如: -``` -$ java -jar ../xpatch.jar ../source.apk -xm ../module1.apk -``` -假如需要将多个Xposed插件打包进去,在Mac中使用":",在Windows下使用";",隔开多个文件路径即可,比如: -``` -mac -$ java -jar ../xpatch.jar ../source.apk -xm ../module1.apk:../module2.apk - -windows -$ java -jar ../xpatch.jar ../source.apk -xm ../module1.apk;../module2.apk -``` - -**注意:** -1. 多个Xposed modules使用`:`(mac)/`;`(win)分割; -2. 假如此module既被打包到apk中,又安装在设备上,则只会加载打包到apk中的module,不会加载安装的。 -这里是通过包名区分是不是同一个module。 - ----- - -### 3. 2020/02/09 updated (Version 3.0) -3.0版本增加了不少新功能,同时修复了一些用户反馈的Bug。 - -新增功能: -1. 支持android 10; -2. 支持更改植入的hook框架,默认使用Sandhook(支持android10),可更改为whale(暂不支持android10)(-w); -3. 默认使用修改Maniest文件方式,植入初始化代码,同时,支持更改为老版本中的修改dex文件的方式植入代码(-dex); -4. 支持修改apk包名(一般不建议使用,很多应用会校验包名,会导致无法使用) -5. 支持修改apk的version code; -6. 支持修改apk的version name; -7. 支持修改apk的debuggable为true或者false; - -Bug修复: -1. 修复Manifest文件中未定义ApplicationName类,导致无法实现Hook的问题; -2. 修复破解无so文件的apk时,出现无法找到so的问题; -3. 修复签名可能会失败的问题; -4. 修复dex文件数超过65536的问题; - -#### 新功能用法 -在命令行中输入-h,可以看到3.0版本完整的帮助文档: -`$ java -jar ../xpatch-3.0.jar -h` -``` -options: - -c,--crach disable craching the apk's signature. - -d,--debuggable <0 or 1> set 1 to make the app debuggable = true, set 0 to - make the app debuggable = false - -dex,--dex insert code into the dex file, not modify manifest - application name attribute - -f,--force force overwrite - -h,--help Print this help message - -k,--keep not delete the jar file that is changed by dex2jar - and the apk zip files - -l,--log show all the debug logs - -o,--output output .apk file, default is $source_apk_dir/[file - -name]-xposed-signed.apk - -pkg,--packageName modify the apk package name - -vc,--versionCode set the app version code - -vn,--versionName set the app version name - -w,--whale Change hook framework to Lody's whale - -xm,--xposed-modules - the xposed module files to be packaged into the ap - k, multi files should be seperated by :(mac) or ;( - win) -version: 3.0 -``` -具体用法: -1. 修改Apk的debuggable = true: - `$ java -jar ../xpatch-3.0.jar ../Test.apk -d 1` (false改为0) -2. 使用老版本的破解dex方法破解apk: -`$ java -jar ../xpatch-3.0.jar ../Test.apk -dex` -3. 修改包名,版本号: -`$ java -jar ../xpatch-3.0.jar ../Test.apk -pkg com.test.test -vc 1000 -vn 1.1.1` -2. 更改Hook框架为whale: -`$ java -jar ../xpatch-3.0.jar ../Test.apk -w` - -### 4. 2021/01/13 updated (Version 4.0) -Update SandHook to the newest version and SandHook support the Android 11. - -## Thanks +# Thanks to + - [Xpatch][11] - [Xposed][10] - - [whale][11] - - [dex2jar][12] - [AXMLPrinter2][13] - - [SandHook](https://github.com/ganyao114/SandHook) - - [xposed_module_loader](https://github.com/WindySha/xposed_module_loader) - - [axml](https://github.com/Sable/axml) - [1]: https://github.com/WindySha/Xpatch/releases/download/v3.0/xpatch-3.0.jar - [2]: https://github.com/WindySha/Xpatch/releases - [3]: https://ibotpeaches.github.io/Apktool/install/ - [5]: https://github.com/asLody/whale - [6]: https://repo.xposed.info/module/com.example.wx_plug_in3 - [7]: https://github.com/Gh0u1L5/WechatMagician/releases - [8]: https://github.com/Blankeer/MDWechat - [9]: https://repo.xposed.info/module/liubaoyua.customtext + + [11]: https://github.com/WindySha/Xpatch.git [10]: https://github.com/rovo89/Xposed - [11]: https://github.com/asLody/whale - [12]: https://github.com/pxb1988/dex2jar [13]: https://code.google.com/archive/p/android4me/downloads - [14]: http://www.apache.org/licenses/LICENSE-2.0.html - [15]: https://xposed-tool-app.oss-cn-beijing.aliyuncs.com/data/Xposed_Tool_2.0.3.apk - [16]: https://github.com/WindySha/xposed-tool-app - diff --git a/README_en.md b/README_en.md deleted file mode 100644 index 5896c9b..0000000 --- a/README_en.md +++ /dev/null @@ -1,98 +0,0 @@ -# What is Xpatch - -Xpatch is a jar tool which is used to repackage the apk file. Then, the new apk can load any Xposed modules installed in the android system. - -This is a way to use Xposed modules without root your device. - -It is easy way to modify one app using xposed module. And any apps changed by Xpatch can load every modules downloaded in the [Xposed Module Repository](https://repo.xposed.info/). -# Benefits -1. Use xposed modules without your device; -2. Modify any apps without root your device. - -# How to use -1. Download the latest jar file from the [release page](https://github.com/WindySha/Xpatch/releases); -2. Run this command in the Windows/Mac console: -``` -$ java -jar ../../xpatch.jar ../../source.apk -``` -Then, a new apk named `source-xposed-signed.apk` in the same folder as `source.apk`. - -# More commands -1. You can specify the output apk path by add `-o` parameter, eg: -``` -$ java -jar ../../xpatch.jar ../../source.apk -o ../../dst.apk -``` -2. Show all the building new apk logs, just add `-l`, eg: -``` -$ java -jar ../../xpatch.jar ../../source.apk -l -``` -3. Not delete the build files, just add `-k`, eg: -``` -$ java -jar ../../xpatch.jar ../../source.apk -k -``` -4. After the version 1.2, craching app signature verifying is added, if you won't need the function, just add '-c', eg: -``` -$ java -jar ../../xpatch.jar ../../source.apk -c -``` -5. More command details can be found when no parameter is added, eg: -``` -$ java -jar ../../xpatch.jar -``` -# How to manage Xposed modules -When the new apk is installed in the device, It will load all the Xposed modules installed in the device when it's process started. - -But you can manage the installed Xposed modules on/off state by a file in the storage. -The file path is `mnt/sdcard/xposed_config/modules.list`. - -When the new app started, it will search all the installed Xposed modules and write the the module app name and the module application name into this file. (`mnt/sdcard/xposed_config/modules.list`) -eg: -``` -com.blanke.mdwechat#MDWechat - -liubaoyua.customtext#文本自定义 -``` -Each line of this file is Application Name#App Name. -You can disable a Xposed module by add `#` before the Application Name, eg: -``` -#com.blanke.mdwechat#MDWechat - -liubaoyua.customtext#文本自定义 -``` -This means the MDWechat Xposed module is disabled. - -``` -#com.blanke.mdwechat#MDWechat - -#liubaoyua.customtext#文本自定义 -``` -This means all Xposed modules are disabled. - -Note: The target app must have file system access permission. Otherwise this file will not be created, and all xposed modules are enabled. - - -# Todo list -1. Support packaging the xposed modules into the source apk; -2. Support loading so library in the xposed modules; -3. Crach apk protections. - -# Issues -1. If the apk dex files are protected, dex2jar can not effect on the dexs, then this tool will not work; -2. The hook framework is using [whale](https://github.com/asLody/whale); this framework is not very stable, some hooks may fail; -3. Do not support Davlik VM; -4. Do not support resource hook; - -# Discuss -You can discuss with me under this page. -[Xpatch Comments](https://windysha.github.io/2019/04/18/Xpatch-%E5%85%8DRoot%E5%AE%9E%E7%8E%B0App%E5%8A%A0%E8%BD%BDXposed%E6%8F%92%E4%BB%B6%E7%9A%84%E4%B8%80%E7%A7%8D%E6%96%B9%E6%A1%88/) - - -# Thanks to - - [Xposed][10] - - [whale][11] - - [dex2jar][12] - - [AXMLPrinter2][13] - - [10]: https://github.com/rovo89/Xposed - [11]: https://github.com/asLody/whale - [12]: https://github.com/pxb1988/dex2jar - [13]: https://code.google.com/archive/p/android4me/downloads diff --git a/xpatch/src/main/assets/dex/sandhook/classes-2.0.dex b/xpatch/src/main/assets/dex/sandhook/classes-2.0.dex deleted file mode 100644 index 0e7195f..0000000 Binary files a/xpatch/src/main/assets/dex/sandhook/classes-2.0.dex and /dev/null differ diff --git a/xpatch/src/main/assets/dex/whale/classes-1.0.dex b/xpatch/src/main/assets/dex/whale/classes-1.0.dex deleted file mode 100644 index 60eae2d..0000000 Binary files a/xpatch/src/main/assets/dex/whale/classes-1.0.dex and /dev/null differ diff --git a/xpatch/src/main/assets/lib/arm64-v8a/libsandhook b/xpatch/src/main/assets/lib/arm64-v8a/libsandhook deleted file mode 100644 index c365c43..0000000 Binary files a/xpatch/src/main/assets/lib/arm64-v8a/libsandhook and /dev/null differ diff --git a/xpatch/src/main/assets/lib/arm64-v8a/libwhale b/xpatch/src/main/assets/lib/arm64-v8a/libwhale deleted file mode 100644 index 4a2a263..0000000 Binary files a/xpatch/src/main/assets/lib/arm64-v8a/libwhale and /dev/null differ diff --git a/xpatch/src/main/assets/lib/armeabi-v7a/libsandhook b/xpatch/src/main/assets/lib/armeabi-v7a/libsandhook deleted file mode 100644 index 3b4ae6c..0000000 Binary files a/xpatch/src/main/assets/lib/armeabi-v7a/libsandhook and /dev/null differ diff --git a/xpatch/src/main/assets/lib/armeabi-v7a/libwhale b/xpatch/src/main/assets/lib/armeabi-v7a/libwhale deleted file mode 100644 index 8f93a05..0000000 Binary files a/xpatch/src/main/assets/lib/armeabi-v7a/libwhale and /dev/null differ diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java b/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java index d214380..65924c6 100644 --- a/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java +++ b/xpatch/src/main/java/com/storm/wind/xpatch/MainCommand.java @@ -33,21 +33,10 @@ public class MainCommand extends BaseCommand { @Opt(opt = "f", longOpt = "force", hasArg = false, description = "force overwrite") private boolean forceOverwrite = false; - @Opt(opt = "k", longOpt = "keep", hasArg = false, description = "not delete the jar file " + - "that is changed by dex2jar and the apk zip files") - private boolean keepBuildFiles = false; - - @Opt(opt = "l", longOpt = "log", hasArg = false, description = "show all the debug logs") - private boolean showAllLogs = false; - @Opt(opt = "c", longOpt = "crach", hasArg = false, description = "disable craching the apk's signature.") private boolean disableCrackSignature = false; - @Opt(opt = "xm", longOpt = "xposed-modules", description = "the xposed module files to be packaged into the apk, " + - "multi files should be seperated by :(mac) or ;(win) ", argName = "xposed module file path") - private String xposedModules; - // 使用dex文件中插入代码的方式修改apk,而不是默认的修改Manifest中Application name的方式 @Opt(opt = "dex", longOpt = "dex", hasArg = false, description = "insert code into the dex file, not modify manifest application name attribute") private boolean dexModificationMode = false; @@ -101,15 +90,14 @@ public class MainCommand extends BaseCommand { File srcApkFile = new File(apkPath); if (!srcApkFile.exists()) { - System.out.println(" The source apk file not exsit, please choose another one. " + + System.out.println("The source apk file not exsit, please choose another one. " + "current apk file is = " + apkPath); return; } String currentDir = new File(".").getAbsolutePath(); // 当前命令行所在的目录 - if (showAllLogs) { - System.out.println(" currentDir = " + currentDir + " \n apkPath = " + apkPath); - } + System.out.println("currentDir: " + currentDir); + System.out.println("apkPath: " + apkPath); if (output == null || output.length() == 0) { output = getBaseName(apkPath) + "-xposed-signed.apk"; @@ -129,8 +117,8 @@ public class MainCommand extends BaseCommand { outputApkFileParentPath = absPath.substring(0, index); } - System.out.println(" !!!!! output apk path --> " + output + - " disableCrackSignature --> " + disableCrackSignature); + System.out.println("output apk path: " + output); + System.out.println("disableCrackSignature: " + disableCrackSignature); String apkFileName = getBaseName(srcApkFile); @@ -141,10 +129,8 @@ public class MainCommand extends BaseCommand { // apk文件解压的目录 unzipApkFilePath = tempFilePath + apkFileName + "-" + UNZIP_APK_FILE_NAME + File.separator; - if (showAllLogs) { - System.out.println(" !!!!! outputApkFileParentPath = " + outputApkFileParentPath + - "\n unzipApkFilePath = " + unzipApkFilePath); - } + System.out.println("outputApkFileParentPath: " + outputApkFileParentPath); + System.out.println("unzipApkFilePath = " + unzipApkFilePath); if (!disableCrackSignature) { // save the apk original signature info, to support crach signature. @@ -155,16 +141,12 @@ public class MainCommand extends BaseCommand { long currentTime = System.currentTimeMillis(); FileUtils.decompressZip(apkPath, unzipApkFilePath); - if (showAllLogs) { - System.out.println(" decompress apk cost time: " + (System.currentTimeMillis() - currentTime)); - } + System.out.println("decompress apk cost time: " + (System.currentTimeMillis() - currentTime) + "ms"); // Get the dex count in the apk zip file dexFileCount = findDexFileCount(unzipApkFilePath); - if (showAllLogs) { - System.out.println(" --- dexFileCount = " + dexFileCount); - } + System.out.println("dexFileCount: " + dexFileCount); String manifestFilePath = unzipApkFilePath + "AndroidManifest.xml"; @@ -177,14 +159,12 @@ public class MainCommand extends BaseCommand { applicationName = pair.applicationName; } - if (showAllLogs) { - System.out.println(" Get application name cost time: " + (System.currentTimeMillis() - currentTime)); - System.out.println(" Get the application name --> " + applicationName); - } + System.out.println("Get application name cost time: " + (System.currentTimeMillis() - currentTime) + "ms"); + System.out.println("Get the application name: " + applicationName); // modify manifest File manifestFile = new File(manifestFilePath); - String manifestFilePathNew = unzipApkFilePath + "AndroidManifest" + "-" + currentTimeStr() + ".xml"; + String manifestFilePathNew = unzipApkFilePath + "AndroidManifest" + "-" + currentTimeStr() + ".xml"; File manifestFileNew = new File(manifestFilePathNew); manifestFile.renameTo(manifestFileNew); @@ -204,20 +184,19 @@ public class MainCommand extends BaseCommand { // modify the apk dex file to make xposed can run in it if (dexModificationMode && isNotEmpty(applicationName)) { - mXpatchTasks.add(new ApkModifyTask(showAllLogs, keepBuildFiles, unzipApkFilePath, applicationName, + mXpatchTasks.add(new ApkModifyTask(true, true, unzipApkFilePath, applicationName, dexFileCount)); } // copy xposed so and dex files into the unzipped apk - mXpatchTasks.add(new SoAndDexCopyTask(dexFileCount, unzipApkFilePath, - getXposedModules(xposedModules), useWhaleHookFramework)); + mXpatchTasks.add(new SoAndDexCopyTask(dexFileCount, unzipApkFilePath)); // compress all files into an apk and then sign it. - mXpatchTasks.add(new BuildAndSignApkTask(keepBuildFiles, unzipApkFilePath, output)); + mXpatchTasks.add(new BuildAndSignApkTask(true, unzipApkFilePath, output)); // copy origin apk to assets // convenient to bypass some check like CRC - if(!FileUtils.copyFile(srcApkFile, new File(unzipApkFilePath, "assets/origin_apk.bin"))){ + if (!FileUtils.copyFile(srcApkFile, new File(unzipApkFilePath, "assets/origin_apk.bin"))) { throw new IllegalStateException("orignal apk copy fail"); } @@ -226,21 +205,8 @@ public class MainCommand extends BaseCommand { currentTime = System.currentTimeMillis(); executor.run(); - if (showAllLogs) { - System.out.println(executor.getClass().getSimpleName() + " cost time: " - + (System.currentTimeMillis() - currentTime)); - } - } - - // 5. delete all the build files that is useless now - File unzipApkFile = new File(unzipApkFilePath); - if (!keepBuildFiles && unzipApkFile.exists()) { - FileUtils.deleteDir(unzipApkFile); - } - - File tempFile = new File(tempFilePath); - if (!keepBuildFiles && tempFile.exists()) { - tempFile.delete(); + System.out.println(executor.getClass().getSimpleName() + " cost time: " + + (System.currentTimeMillis() - currentTime) + "ms"); } } diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/task/SaveApkSignatureTask.java b/xpatch/src/main/java/com/storm/wind/xpatch/task/SaveApkSignatureTask.java index 4c4db8a..0e5e199 100644 --- a/xpatch/src/main/java/com/storm/wind/xpatch/task/SaveApkSignatureTask.java +++ b/xpatch/src/main/java/com/storm/wind/xpatch/task/SaveApkSignatureTask.java @@ -13,7 +13,7 @@ public class SaveApkSignatureTask implements Runnable { private String apkPath; private String dstFilePath; - private final static String SIGNATURE_INFO_ASSET_PATH = "assets/xpatch_asset/original_signature_info.ini"; + private final static String SIGNATURE_INFO_ASSET_PATH = "assets/original_signature_info.ini"; public SaveApkSignatureTask(String apkPath, String unzipApkFilePath) { this.apkPath = apkPath; @@ -33,7 +33,9 @@ public class SaveApkSignatureTask implements Runnable { File file = new File(dstFilePath); File fileParent = file.getParentFile(); if (!fileParent.exists()) { - fileParent.mkdirs(); + if(!fileParent.mkdirs()){ + System.out.println("mkdir fails " + fileParent.getAbsolutePath()); + } } FileUtils.writeFile(dstFilePath, originalSignature); diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/task/SaveOriginalApplicationNameTask.java b/xpatch/src/main/java/com/storm/wind/xpatch/task/SaveOriginalApplicationNameTask.java index 2bcd865..0550bb7 100644 --- a/xpatch/src/main/java/com/storm/wind/xpatch/task/SaveOriginalApplicationNameTask.java +++ b/xpatch/src/main/java/com/storm/wind/xpatch/task/SaveOriginalApplicationNameTask.java @@ -13,7 +13,7 @@ public class SaveOriginalApplicationNameTask implements Runnable { private final String unzipApkFilePath; private String dstFilePath; - private final String APPLICATION_NAME_ASSET_PATH = "assets/xpatch_asset/original_application_name.ini"; + private final String APPLICATION_NAME_ASSET_PATH = "assets/original_application_name.ini"; public SaveOriginalApplicationNameTask(String applicationName, String unzipApkFilePath) { this.applcationName = applicationName; diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/task/SoAndDexCopyTask.java b/xpatch/src/main/java/com/storm/wind/xpatch/task/SoAndDexCopyTask.java index b69daca..d178eb4 100644 --- a/xpatch/src/main/java/com/storm/wind/xpatch/task/SoAndDexCopyTask.java +++ b/xpatch/src/main/java/com/storm/wind/xpatch/task/SoAndDexCopyTask.java @@ -3,52 +3,27 @@ package com.storm.wind.xpatch.task; import com.storm.wind.xpatch.util.FileUtils; import java.io.File; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; /** * Created by Wind */ public class SoAndDexCopyTask implements Runnable { - private static final String SANDHOOK_SO_FILE_NAME = "libsandhook"; - private static final String WHALE_SO_FILE_NAME = "libwhale"; - - private static final String SANDHOOK_SO_FILE_NAME_WITH_SUFFIX = "libsandhook.so"; - private static final String WHALE_SO_FILE_NAME_WITH_SUFFIX = "libwhale.so"; - - private static final String XPOSED_MODULE_FILE_NAME_PREFIX = "libxpatch_xp_module_"; - private static final String SO_FILE_SUFFIX = ".so"; - private final String[] APK_LIB_PATH_ARRAY = { "lib/armeabi-v7a/", "lib/armeabi/", "lib/arm64-v8a/" }; - private final HashMap mSoFilePathMap = new HashMap<>(); private int dexFileCount; private String unzipApkFilePath; - private String[] xposedModuleArray; - private boolean useWhaleHookFramework; - - public SoAndDexCopyTask(int dexFileCount, String unzipApkFilePath, - String[] xposedModuleArray, boolean useWhaleHookFramework) { + public SoAndDexCopyTask(int dexFileCount, String unzipApkFilePath) { this.dexFileCount = dexFileCount; this.unzipApkFilePath = unzipApkFilePath; - this.xposedModuleArray = xposedModuleArray; - this.useWhaleHookFramework = useWhaleHookFramework; - - String soFileName; - if (useWhaleHookFramework) { - soFileName = WHALE_SO_FILE_NAME; - } else { - soFileName = SANDHOOK_SO_FILE_NAME; - } - - mSoFilePathMap.put(APK_LIB_PATH_ARRAY[0], "assets/lib/armeabi-v7a/" + soFileName); - mSoFilePathMap.put(APK_LIB_PATH_ARRAY[1], "assets/lib/armeabi-v7a/" + soFileName); - mSoFilePathMap.put(APK_LIB_PATH_ARRAY[2], "assets/lib/arm64-v8a/" + soFileName); } @Override @@ -62,96 +37,74 @@ public class SoAndDexCopyTask implements Runnable { } private void copySoFile() { - String[] existLibPathArray = new String[3]; - int arrayIndex = 0; + List existLibPathArray = new ArrayList<>(); for (String libPath : APK_LIB_PATH_ARRAY) { String apkSoFullPath = fullLibPath(libPath); File apkSoFullPathFile = new File(apkSoFullPath); if (apkSoFullPathFile.exists()) { - existLibPathArray[arrayIndex] = libPath; - arrayIndex++; + existLibPathArray.add(libPath); + } else { + System.out.println("target app dont have " + libPath + ", skip"); } } - // 不存在lib目录,则创建lib/armeabi-v7 文件夹 - if (arrayIndex == 0) { + if (existLibPathArray.isEmpty()) { + System.out.println("target app dont have any so in \"lib/{eabi}\" dir, so create default \"armeabi-v7a\""); String libPath = APK_LIB_PATH_ARRAY[0]; String apkSoFullPath = fullLibPath(libPath); File apkSoFullPathFile = new File(apkSoFullPath); - apkSoFullPathFile.mkdirs(); - existLibPathArray[arrayIndex] = libPath; + if (apkSoFullPathFile.mkdirs()) { + throw new IllegalStateException("mkdir fail " + apkSoFullPathFile.getAbsolutePath()); + } + existLibPathArray.add(libPath); } for (String libPath : existLibPathArray) { - if (libPath != null && !libPath.isEmpty()) { - String apkSoFullPath = fullLibPath(libPath); - copyLibFile(apkSoFullPath, mSoFilePathMap.get(libPath)); + if (libPath == null || libPath.isEmpty()) { + throw new IllegalStateException("fail eabi path"); } - } - // copy xposed modules into the lib path - if (xposedModuleArray != null && xposedModuleArray.length > 0) { - int index = 0; - for (String modulePath : xposedModuleArray) { - modulePath = modulePath.trim(); - if (modulePath == null || modulePath.length() == 0) { - continue; - } - File moduleFile = new File(modulePath); - if (!moduleFile.exists()) { - continue; - } - for (String libPath : existLibPathArray) { - if (libPath != null && !libPath.isEmpty()) { - String apkSoFullPath = fullLibPath(libPath); - String outputModuleName = XPOSED_MODULE_FILE_NAME_PREFIX + index + SO_FILE_SUFFIX; - File outputModuleSoFile = new File(apkSoFullPath, outputModuleName); - FileUtils.copyFile(moduleFile, outputModuleSoFile); - } - } - index++; + String apkSoFullPath = fullLibPath(libPath); + String eabi = libPath.substring(libPath.indexOf("/")); + if (eabi.isEmpty()) { + throw new IllegalStateException("fail find eabi in " + libPath); + } + + File[] files = new File("list-so", eabi).listFiles(); + if (files == null) { + System.out.println("Warning: Nothing so file has been copied in " + libPath); + continue; + } + for (File mySoFile : files) { + File target = new File(apkSoFullPath, mySoFile.getName()); + FileUtils.copyFile(mySoFile, target); + System.out.println("Copy " + mySoFile.getAbsolutePath() + " to " + target.getAbsolutePath()); } } } private void copyDexFile(int dexFileCount) { - // copy dex file to root dir, rename it first - String copiedDexFileName = "classes" + (dexFileCount + 1) + ".dex"; - // assets/classes.dex分隔符不能使用File.seperater,否则在windows上无法读取到文件,IOException - String dexAssetPath; - if (useWhaleHookFramework) { - dexAssetPath = "assets/dex/whale/classes-1.0.dex"; - } else { - dexAssetPath = "assets/dex/sandhook/classes-2.0.dex"; + boolean copyed = false; + // copy all dex files in list-dex + File[] files = new File("list-dex").listFiles(); + if (files == null || files.length == 0) { + System.out.println("Warning: Nothing dex file has been copied"); + return; + } + for (File file : files) { + String copiedDexFileName = "classes" + (dexFileCount + 1) + ".dex"; + File target = new File(unzipApkFilePath, copiedDexFileName); + FileUtils.copyFile(file, target); + System.out.println("copy " + file.getAbsolutePath() + " to " + target.getAbsolutePath()); + dexFileCount++; + copyed = true; } - FileUtils.copyFileFromJar(dexAssetPath, unzipApkFilePath + copiedDexFileName); } private String fullLibPath(String libPath) { return unzipApkFilePath + libPath.replace("/", File.separator); } - private void copyLibFile(String libFilePath, String srcSoPath) { - File apkSoParentFile = new File(libFilePath); - if (!apkSoParentFile.exists()) { - apkSoParentFile.mkdirs(); - } - - // get the file name first - // int lastIndex = srcSoPath.lastIndexOf('/'); - // int length = srcSoPath.length(); - String soFileName; - if (useWhaleHookFramework) { - soFileName = WHALE_SO_FILE_NAME_WITH_SUFFIX; - } else { - soFileName = SANDHOOK_SO_FILE_NAME_WITH_SUFFIX; - } - - // do copy - FileUtils.copyFileFromJar(srcSoPath, new File(apkSoParentFile, soFileName).getAbsolutePath()); - } - - private void deleteMetaInfo() { String metaInfoFilePath = "META-INF"; File metaInfoFileRoot = new File(unzipApkFilePath + metaInfoFilePath); diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/util/ApkSignatureHelper.java b/xpatch/src/main/java/com/storm/wind/xpatch/util/ApkSignatureHelper.java index b98020f..d35597c 100644 --- a/xpatch/src/main/java/com/storm/wind/xpatch/util/ApkSignatureHelper.java +++ b/xpatch/src/main/java/com/storm/wind/xpatch/util/ApkSignatureHelper.java @@ -72,7 +72,7 @@ public class ApkSignatureHelper { } } jarFile.close(); - System.out.println(" getApkSignInfo result --> " + certs[0]); + System.out.println("getApkSignInfo result: " + certs[0]); return new String(toChars(certs[0].getEncoded())); } catch (Exception e) { e.printStackTrace(); diff --git a/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java b/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java index 9b8d31d..afd1d83 100644 --- a/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java +++ b/xpatch/src/main/java/com/storm/wind/xpatch/util/FileUtils.java @@ -44,7 +44,9 @@ public class FileUtils { } File pathFile = new File(descDir); if (!pathFile.exists()) { - pathFile.mkdirs(); + if(!pathFile.mkdirs()){ + throw new IllegalStateException("mkdir fail " + pathFile.getAbsolutePath()); + } } ZipFile zip = null; @@ -67,7 +69,9 @@ public class FileUtils { File file = new File(outPath.substring(0, outPath.lastIndexOf(File.separator))); if (!file.exists()) { - file.mkdirs(); + if(!file.mkdirs()){ + throw new IllegalStateException("mkdir fail " + file.getAbsolutePath()); + } } //判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压 if (new File(outPath).isDirectory()) {