声明:
免责声明:本文为个人作品,只做技术研究,只可用于正常的技术交流与学习,不可用于灰黑产业,不可从事违法犯罪行,严禁利用本文所介绍的技术恶意攻击,否则,后果自负!!!
背景:
搞安卓逆向的朋友都知道,现在的安卓app过root检测越来越难了。那么有没有什么方法在非root环境下,也可以hook java方法,拦截函数,修改函数的返回值呢?经过笔者不懈的努力,终于在github上搜到了这么一款牛逼的框架 TweakMe
放下github 链接 https://github.com/liaoguobao/TweakMe
放下作者介绍
TweakMe是一个轻量级的逆向开发框架。 它与传统的xposed、frida、magisk等框架相比的最大优势是无需root环境。 对手机系统的要求很低,只需要一个正常手机即可对app进行逆向分析。 简单的几步操作就可以完成对指定app的进程注入,可以绕过大多数加固(360、梆梆、爱加密等)的签名证书校验。 完成注入后通过Java编写hook代码,一键脚本编译为dex插件。 目前TweakMe框架在5.0到11.0的android手机上测试通过。 支持arm、arm64两种abi。 详细介绍及使用请看《免ROOT框架TweakMe介绍.docx》。
测试环节
接下来我们就开始测试,找一个xxx银行的app 来测试,通过分析so库,发现该银行没有采用各大厂商的加固方案,如果是加固过得,参照文档如下
对于梆梆加固是libDexHelper.so,对于爱加密是libexec.so,对于360加固是libjiagu.so。需要注意的是,对于爱加密、360加固等壳的so并不在通常的lib/abi目录下,而在assets目录中,所以--target 参数必须要指定绝对路径,只有在通常的lib/abi目录下时才可以只写so名称。可供参考的命令如下:
apktweak --apk xxx.apk --target libDexHelper.so
apktweak --apk xxx.apk --target assets/ijm_lib/armeabi/libexec.so
apktweak --apk xxx.apk --target assets/libjiagu.so
而这个app是自己写的加固,那么按照官方文档,我们需要找一个基地址最大的so 来注入 通过以下命令来找,
cat /proc/25189/maps |grep com.xxxxbank
这里提醒说明几点,
1:既然是免root了,那我怎么能看maps 文件呢?(magisk+shamiko 了解下?)
2:so文件一定是安卓包里的so 不要看到系统库了
按照文档要求,我们执行以下命令
apktweak --apk xxx.apk --target libxloader.so
此时会在当前目录下面生成一个xxx-sign.apk 的应用,这个是注入了作者框架的apk 多了一个libx.so
这里有几个坑点 需要说明
第一 : 这个默认注入的 arm32 位的。
apktweak --apk xxx.apk --target libxloader.so
如果要注入 64位的需要指定
apktweak --apk xxx.apk --target libxloader.so --abi arm64-v8a
第二:如果安装签名后的应用提示
Failure [-124: Failed parse during installPackageLI: Targeting R+ (version 30 and above) requires the resources.arsc of installed APKs to be stored uncompressed and aligned on a 4-byte boundary]
则需要进行4字节对齐,此时还要加一个 --align
apktweak --apk xxx.apk --target libxloader.so --abi arm64-v8a --align
此时当前目录会生成一个 com.xxxx-align-sign.apk 文件
然后我们把这个重打包签名后的apk 安装到手机上,同时把原始apk 传到
/sdcard/tweak/com.xxxx.bank/ 目录下(com.xxxx.bank是该银行app的包名),并且命名为base.apk
再把工程下的libs目录下的 对应架构的libsotweak.so 传到/sdcard/tweak/com.xxxx.bank/目录下
现在我们就可以写hook代码了,具体写法和规则 ,本文不做解释,请自行查看源码
这里我新建了一个 插件。然后在 loadDexFile hook 安卓的api,主要是获取app运行列表和读取内存卡目录
package com.android.guobao.liao.apptweak.plugin;
import android.util.Log;
import com.android.guobao.liao.apptweak.JavaTweakBridge;
public class JavaTweak_camille {
static public void loadDexFile(String dex) {
//此函数内可以做一些初始化操作,比如加载native动态库,拦截系统类函数等等
JavaTweakBridge.hookJavaMethod("android.app.ActivityThread", "performLaunchActivity");
JavaTweakBridge.hookJavaMethod("android.app.ActivityManager","getRunningAppProcesses");
JavaTweakBridge.hookJavaMethod("android.os.Environment","getExternalStorageDirectory");
}
/**
* @Hook_method: getRunningAppProcesses 要替换的方法 必须静态函数
* @author: Pyth0n
* @todo:
* @date: 2022/9/1 14:00
* @param thiz
* @return java.lang.Object 原函数如果是静态的必须用callStaticOrignalMethod
*/
static private Object getRunningAppProcesses(Object thiz) { // 打印获取app 运行列表 隐私合规权限
JavaTweakBridge.writeToLogcat(Log.INFO,"获取app运行列表 调用栈:%s",Log.getStackTraceString(new Throwable()));
Log.i("WriteToLogcat","打印完毕");
return JavaTweakBridge.callOriginalMethod(thiz);
}
/*
* public static File getExternalStorageDirectory() {
*
被替换方法 是静态的 因此 替换方法 _getExternalStorageDirectory 不需要this 参数
调用原始函数 也需要用callStaticOriginalMethod
*/
static private Object getExternalStorageDirectory() { // 打印获取app 获取存储卡信息 隐私合规权限
JavaTweakBridge.writeToLogcat(Log.INFO,"获取存储卡信息 调用栈:%s",Log.getStackTraceString(new Throwable()));
Log.i("WriteToLogcat","打印完毕");
return JavaTweakBridge.callStaticOriginalMethod();
}
}
写完插件记得在主方法里 把这个插件打开
然后我们执行 java2dex.bat 会生成一个 javatweak.dex 把他也传到 /sdcard/tweak/com.xxxx.bank/ 目录下,此时目录结构如下
然后打开app,在 logcat 查看日志
,发现成功在非root下 hook到了方法 同时打印出调用栈
以上就是本文的内容,那么感兴趣的朋友肯定会问了,如果是混淆过的app怎么办呢?
混淆过的app 安卓api 不会变,但是应用层java方法必须得知道。
下一步就是我们可以研究下 怎样把hook到的参数发送给burp,同时接受返回的数据,继续调用原始方法,后续文章继续放出,敬请关注