故事的起源:Ratel,重打包沙箱
当时我思考 VirtualXposed 可以基于 VA 实现沙箱环境 + 上帝模式的 hook 能力
我想应该很多人想到过,但是只有 virjar 做到了,这可能就是大佬吧。
为了便于理解,我觉得可以把平头哥理解成 太极、应用转生的 PRO 版本,功能更强大、生态更好、更适合我们使用。
请注意,该类工具都是免 root的,通过对目标 app 进行重打包来达到运行在沙箱的效果。
文中相关文件:https://github.com/runtimed/useRatel
Ratel Manager#
首先得编译一个 Ratel Manager 出来,RM 是平头哥的本体,这玩意可以理解成太极和应用转生的主程序,用与对目标 app 进行二次打包、提供沙箱环境、xposed 插件管理等等。
在渣总的 repo 里没找到成品,应该是得自己编译
RM 构建需要使用脚本构建,否则无法支持 app 端进行重打包
git clone https://github.com/virjarRatel/ratel-core
cd ratel-core
# 开始编译
./script/build_ratel_manager.sh
接下来就是处理报错时间!
Connection refused
IO exception while downloading manifest:
java.net.ConnectException: Connection refused (Connection refused)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:476)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:218)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:200)
at java.net.Socket.connect(Socket.java:606)
at java.net.Socket.connect(Socket.java:555)
at sun.net.NetworkClient.doConnect(NetworkClient.java:180)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:463)
at sun.net.www.http.HttpClient$1.run(HttpClient.java:515)
at sun.net.www.http.HttpClient$1.run(HttpClient.java:513)
at java.security.AccessController.doPrivileged(Native Method)
at sun.net.www.http.HttpClient.privilegedOpenServer(HttpClient.java:512)
代理问题,我之前给 gradle 设置了代理,现在去掉(因为我在家装了软路由。
## 去掉代理相关的部分
vim .gradle/gradle.properties
在排错的过程中,发现大多是 NDK 相关的问题。
别忘了在环境变量中加入 ANDROID_HOME ,在我的 mac 上是这样的:
export ANDROID_HOME="/Users/{UserName}/Library/Android/sdk/"
最后,我使用ndk 21.1.6352462
编译成功的。
希望大家顺利编译:
基本用法#
使用下边脱壳实验的目标《网上国网》做演示(没日过,只因为他有 Anti Root 和加壳)
1. 感染目标应用#
感染目标就是对目标进行二次打包,其运行在沙箱环境中。
在可感染列表中,选中:
这里还有点用,gda 识别不到这个 apk 的包名,所以直接看这里也可以。
选中以后会自动重打包。
感染成功:
先卸载之前安装的原版 apk,然后安装感染过的 app 就可以辣。
2. 新建插件#
https://github.com/virjarRatel/ratel-module-template 平头哥的插件模块模版,用于快速生成一个插件项目
支持两种方式生成插件:
./template.sh -a ~/Downloads/wsgw.apk -m crack-wsgw # -a 指向目标apk(会自动识别其包名
./template.sh -p com.sgcc.wsgw.cn -m crack-wsgw # -p 包名
使用 as 打开,插件是绿色的,说明没 error:
这个插件结构,和 xposed 模块一样,方便上手:
对 xposed 不熟悉的可以看看我年轻的时候写的文章 https://www.freebuf.com/articles/terminal/248675.html。
脱壳#
这个目标 app 加壳的,并且有 anti root, 开了 Magisk Hide 才勉强能运行。
所以用 frida-dexdump 比较困难...
直接反编译,可以看到加壳了:
接下来需要脱壳,脱壳插件代码:
public class HookEntry implements IRposedHookLoadPackage {
private static final String tag = "DEMO_HOOK";
@Override
public void handleLoadPackage(final RC_LoadPackage.LoadPackageParam lpparam) {
if (lpparam.packageName.equals("com.sgcc.wsgw.cn")) {
Unpack.entry(lpparam);
}
}
}
public class Unpack {
public static final String tag = "TR_HOOK";
public static void entry(RC_LoadPackage.LoadPackageParam lpparam) {
// TODO 抽取壳要把这个选项打开
UnPackerToolKit.autoEnable(true);
UnPackerToolKit.unpack(new UnPackerToolKit.UnpackEvent() {
@Override
public void onFinish(File file) {
try {
FileUtils.copyFile(file, new File("/sdcard/wsgw-unpack.apk")); // 脱壳后生成的apk
} catch (IOException e) {
Log.e(tag, "unpacked error", e);
e.printStackTrace();
}
}
});
}
}
代码编辑完后,点击运行 button:
在终端中输入 adb logcat -s unpack
, 可以查看脱壳状态
确认模块已开启
在感染应用中点击目标应用 -> 强杀进程,然后重新运行目标应用。
在脱壳过程中的 constructUnpackedApk 步骤中,总是报错:
不过最终,还是生成了 apk 给俺:
adb pull /sdcard/wsgw-unpack.apk ./
拖到 gda 中打开,说实话这个脱壳效果,还真不错...:
抓包#
利用 vpn 抓包,提示证书验证异常,应该是用了 ssl pining 或者 https 双向认证:
Ratel 内置 socketMonitor (比肉丝的 R0Cpature 早出现 3 年,早期甚至支持线程跳跃追踪,用以解决异步问题)
抓包插件编写
public class HookEntry implements IRposedHookLoadPackage {
private static final String tag = "DEMO_HOOK";
@Override
public void handleLoadPackage(final RC_LoadPackage.LoadPackageParam lpparam) {
if (lpparam.packageName.equals("com.sgcc.wsgw.cn")) {
//Unpack.entry(lpparam);
Monitor.entry(lpparam);
}
}
}
public class Monitor {
public static void entry(RC_LoadPackage.LoadPackageParam lpparam) {
SocketMonitor.setPacketEventObserver(new FileLogEventObserver(new File(RatelToolKit.whiteSdcardDirPath, "socketMonitor")));
// 对应路径为 /sdcard/ratel_white_dir/com.sgcc.wsgw.cn/socketMonitor
}
}
插件安装完后,执行我们需要抓包的功能,然后将其 pull 下来:
adb pull /sdcard/ratel_white_dir/com.sgcc.wsgw.cn/socketMonitor ./
看起来还可以,请求、响应、调用栈 都有,每个文件对应一个请求:
OS: 根据文件创建日期可能对确定请求有帮助诶
Warning: 此处没测试有没有对 native 进行处理.. 另外没有测试是否支持证书导出,文档好少
读取目标 app 私有文件#
这种需求也经常有,个人觉得所有免 root 方案都应该考虑这个问题,看看 Ratel 怎么用吧。
repo 地址:https://github.com/virjarRatel/sshdroid
文档可以看 Readme 和 http://ratel-doc.virjar.com/04_ecology/SSHdroid.html:
git clone https://github.com/virjarRatel/sshdroid.git
cd sshdroid
cp app/src/main/assets/config.template.properties app/src/main/assets/config.properties
使用 as 打开并编写配置文件,配置完直接运行:
在 RM 的模块中开启此模块,启动目标 app
# 端口转发
adb forward tcp:3478 tcp:3478
# ssh登录
ssh 127.0.0.1 -p 3478
目前的 shell 命令交互展示还未进行优化
确实没优化,不过能用。
TIPS:密码直接回车即可
scp 也支持,体验还不错
1. 获取文件夹
2. 获取单个文件
cat 也支持
其余的命令用到的时候再说吧,确实很不人性化,不过这个 shell 用在自动化脚本上就很合适,毫无多余信息,不用考虑自动补全什么的。
Hook#
随便找个函数 hook 一下,在之前抓包中的调用栈中找到一个函数,net.security.device.api.SecurityUtil.httpPost
:
编写插件对该函数进行 hook:
@Override
public void handleLoadPackage(final RC_LoadPackage.LoadPackageParam lpparam) {
Log.i(tag, "start");
//Ratel提供的api ,自动选择目标类的classloader
ClassLoadMonitor.addClassLoadMonitor("net.security.device.api.SecurityUtil", new ClassLoadMonitor.OnClassLoader() {
@Override
public void onClassLoad(Class<?> clazz) {
Log.i(tag, "find class: " + clazz.getName());
RposedHelpers.findAndHookMethod(clazz, "httpPost",
String.class,
Map.class,
Map.class,
new RC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
String arg1 = (String) param.args[0];
String arg2 = (String) param.args[1].toString();
String arg3 = (String) param.args[2].toString();
Log.i(tag, "args1:" + arg1);
Log.i(tag, "args2:" + arg2);
Log.i(tag, "args3:" + arg3);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
String res = (String) param.getResult();
Log.i(tag, "result:" + res);
Log.i(tag, "-------------------------");
}
});
}
});
Log.i(tag, "end");
}
看看 hook 效果:
没测试 hook native,俺不会
后记#
Ratel 挺强的,但是当然不是银弹:
但是已经很不错了,可以考虑二开,来应对即将到来的 Anti。
参考文章: