banner
Silas

REAO

Be a better man
github

Ratel 初体验

故事的起源: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 编译成功的。

希望大家顺利编译:

image

image

基本用法#

使用下边脱壳实验的目标《网上国网》做演示(没日过,只因为他有 Anti Root 和加壳)

1. 感染目标应用#

感染目标就是对目标进行二次打包,其运行在沙箱环境中。

在可感染列表中,选中:

image

这里还有点用,gda 识别不到这个 apk 的包名,所以直接看这里也可以。
image

选中以后会自动重打包。

感染成功:

image

先卸载之前安装的原版 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:

image

这个插件结构,和 xposed 模块一样,方便上手:

image

对 xposed 不熟悉的可以看看我年轻的时候写的文章 https://www.freebuf.com/articles/terminal/248675.html

脱壳#

这个目标 app 加壳的,并且有 anti root, 开了 Magisk Hide 才勉强能运行。
所以用 frida-dexdump 比较困难...

直接反编译,可以看到加壳了:

image

接下来需要脱壳,脱壳插件代码:

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:

image

在终端中输入 adb logcat -s unpack, 可以查看脱壳状态

image

确认模块已开启

image
在感染应用中点击目标应用 -> 强杀进程,然后重新运行目标应用。

image

在脱壳过程中的 constructUnpackedApk 步骤中,总是报错:

image

不过最终,还是生成了 apk 给俺:

image

adb pull /sdcard/wsgw-unpack.apk ./

拖到 gda 中打开,说实话这个脱壳效果,还真不错...:

image

抓包#

利用 vpn 抓包,提示证书验证异常,应该是用了 ssl pining 或者 https 双向认证:

image

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: 根据文件创建日期可能对确定请求有帮助诶

image

Warning: 此处没测试有没有对 native 进行处理.. 另外没有测试是否支持证书导出,文档好少

读取目标 app 私有文件#

这种需求也经常有,个人觉得所有免 root 方案都应该考虑这个问题,看看 Ratel 怎么用吧。

repo 地址:https://github.com/virjarRatel/sshdroid

文档可以看 Readme 和 http://ratel-doc.virjar.com/04_ecology/SSHdroid.html

image

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 打开并编写配置文件,配置完直接运行:

image

image

在 RM 的模块中开启此模块,启动目标 app

# 端口转发
adb forward tcp:3478 tcp:3478

# ssh登录
ssh 127.0.0.1 -p 3478

目前的 shell 命令交互展示还未进行优化

确实没优化,不过能用。

image

TIPS:密码直接回车即可​

scp 也支持,体验还不错

1. 获取文件夹

image

2. 获取单个文件

image

cat 也支持

image

其余的命令用到的时候再说吧,确实很不人性化,不过这个 shell 用在自动化脚本上就很合适,毫无多余信息,不用考虑自动补全什么的。

Hook#

随便找个函数 hook 一下,在之前抓包中的调用栈中找到一个函数,net.security.device.api.SecurityUtil.httpPost

image

编写插件对该函数进行 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 效果:

image

没测试 hook native,俺不会

后记#

Ratel 挺强的,但是当然不是银弹:

image

但是已经很不错了,可以考虑二开,来应对即将到来的 Anti。


参考文章:

  1. app 逆向 平头哥实战(某农产品 app)
加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。