最近公司产品有个需求,获取竞争公司的app应用上一些数据。当时听了后也有点蒙逼,要知道现在的app都是混淆并且加固过了的,而且数据接口都进行过加密处理的,这样就给爬取数据增加了一些难度。思考了一段时间,想出来本文的解决方案。此方案难点在于需要对app进行脱壳处理,寻找需要hook的方法。
技术点
- app加固原理和一些常用的脱壳技术
- xposed框架使用
- android中各种网络请求框架的数据返回处理点
- ui automator自动化技术
- android应用开发基础
- node开发基础
- adb命令用法
实现步骤
对加固过app进行脱壳处理
市面上的app大多数app都是用360、腾讯等其他厂商的免费加固,面对这些app的脱壳,有很多方法如ZjDroid、DexHunter、dex2oat法。
我这里采用的是dex2oat法,这种方式对360加固方式脱壳成功率挺高的,其原理:android系统art模式下,apk文件首次打开时后会用dex2oat生成oat时,这个时候内存中的DEX是完整的,所以只需要在这个时候将dex保存下来就ok了。看雪上有个大牛专门采用这种方法做了一个android4.4版本脱壳系统镜像,我们只需要在android虚拟机上安装镜像,修改系统启动模式为ART,重启机器,安装apk,打开后就会自动脱可。
以下是脱壳过程中产生的日志:
|
|
在代码中寻找请求数据返回的hook点
通过查看apk的mainifest文件,获取包名,用jadx打开脱壳后的classes.dex文件,通过包名寻找到代码,我们这里是为了寻找接口数据的hook点,所以我一般会先去寻找登录界面的代码,然后通过登录界面找出接口调用涉及到的那些类,在通过这些类判断出该app用的是哪个数据请求框架,常见的也就几种:volley、retrofit+okhttp、xutil、asynchttpclient等。找到app用的是哪个请求框架后,我们就可以推断出具体需要hook的位置了。
例如:我发现app用的是retrofit,那就可以推断出它会使用Converter<ResponseBody, T>
对返回的结果进行json反序列化处理,因此找到了以下类:
|
|
书写xposed模块
该模块主要是为了hook掉指定的方法,进行数据采集,并且采集的数据会在记录在文件里面,以便上传服务器分析。具体xposed的使用请自行查看官方文档。
- 定义一个基类,统一处理mutil-dex hook
|
|
- 定义具体应用的hook
|
|
收集数据到本地文件
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970public class FileLogger {private static final String TAG = "HookLogger";private static final String LOG_DIR = "hookLogs";private static final long MAX_SIZE = 10 * 1024 * 1024;public static void log(String content) {if (TextUtils.isEmpty(content)) {return;}content = content + "\n";try {File logFile = getOutputFile();FileOutputStream fileOutputStream = new FileOutputStream(logFile, true);fileOutputStream.write(content.getBytes());fileOutputStream.flush();fileOutputStream.close();} catch (Exception e) {Logger.e(TAG, e);}}public static File getOutputFile() throws Exception {File hookLogDir = getLogDir();SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");String today = simpleDateFormat.format(Calendar.getInstance().getTime());File file = new File(hookLogDir, today + ".log");long fileSize = file.length();if (file.exists() && fileSize > MAX_SIZE) {String indicator = getIndicatorStr();File renameFile = new File(hookLogDir, today + "-" + indicator + ".log");file.renameTo(renameFile);}return file;}public static File[] scanCompleteFile() throws Exception {File dir = getLogDir();SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");String today = simpleDateFormat.format(new Date());File[] fileArr = dir.listFiles(new FilenameFilter() {@Overridepublic boolean accept(File dir, String name) {return !(today + ".log").equals(name);}});if (fileArr != null && fileArr.length > 0) {Arrays.sort(fileArr, new Comparator<File>() {@Overridepublic int compare(File o1, File o2) {return o1.getName().compareTo(o2.getName());}});}return fileArr;}public static File getLogDir() {File file = new File(Environment.getExternalStorageDirectory(), LOG_DIR);if (!file.exists()) {file.mkdirs();}return file;}public static String getIndicatorStr() {return System.currentTimeMillis() + "";}}
书写定期上传日志文件服务
定义一个前台服务,定期上传xposed模块收集完成的数据
|
|
自动化脚本
上面的步骤做完了后,我们就可以就可以进行数据采集啦。但是通过人工手动点击操作app来收集数据的化,肯定是很傻的操作。因此需要通过android自动化框架ui automator来进行自动化操作。
|
|
自动化脚本定期执行
自动化脚本的运行其实就是执行脚本apk文件的过程,因此只需要将打包好的脚本apk文件,放在服务器定时执行即可。
这里说下我的实现思路:
- 通过
adb connect
命令让android手机和服务器链接在一起 - nodejs写一个定时执行任务,将打包好的脚本apk文件定期在android手机运行即可
- 通过pm2运行node执行文件,当定时任务挂掉后重启定时任务
- 记录执行过程中导致自动化挂掉的问题日志
我把在android手机执行测试脚本的过程写成一个shell脚本,然后通过shelljs去不断执行脚本
|
|
|
|
|
|
闲扯几句
- 其实通过xposed hook应用可以做很多事情,比如有些人通过它修改一些收费应用商的金币数量等等,让自己可以使用收费版功能。
- 在通过自动化爬取数据的时候很容易被封号,这个就需要你为你的自动化脚本指定一些执行策略,比如执行多少次后,换一个账户、换imei、换地址位置、换ip地址
- 通过该方案爬取数据的好处是,你不需要真正的区破解接口的加密逻辑。如果你确实有这方面的需求可以通过xposed上的InspectPackage模块来对应用进行动态分析,在结合源码进行破解,所需要花费的时间也是比较多。
- 以后还是少熬夜的比较好!