吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1585|回复: 13
收起左侧

[Android CTF] N1CTF-Reverse-Attachment 题解

  [复制链接]
jbczzz 发表于 2024-11-18 16:24
本帖最后由 jbczzz 于 2024-11-18 20:30 编辑

0x0 前言
这道题根据出题人说,目前是只在安卓12和14上验证过,其他的系统可能会有问题,我刚开始是在mi11 安卓13上做的,输入假的flag依旧会弹出正确提示。猜测是frIDA hook java方法的时候某个地方出了问题,他是成功把callRabbit转换为jni函数并通过art_quick_generic_jni_trampoline调用,但可能entry_point_fromjni之类的偏移出错了,他并没有执行预计的逻辑,而是直接返回原java函数执行,这个时候用高版本的frida hook一次callRabbit后,这道题就正常了。
b697d0277236dd6aa39e9c2b86853f4a.png JZILF(NFR{CDX9W(UOC)[2Y.png
0x1 题解
首先这道题是个apk,照例丢进jadx看一眼
需要关注的两个地方,一个是MainActivity加载的librefantazio.so,一个是java层的验证函数callRabbit
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static {
        System.loadLibrary("refantazio");
    }
public class rabbitHole {
    public static String callRabbit(String flag) {
        byte[] inputBytes = flag.getBytes();
        byte[] tokenBytes = "TokenashiTokeari".getBytes();
        byte[] xorResult = new byte[inputBytes.length];
        int[] array = {50, 3, 10, 2, 21, 53, 1, 17, 73, 28, 14, 25, 1, 4, 0, 72, 116, 41, 7, 4, 9, 65, 26, 27, 73, 26, 0, 31, 69, 41, 23, 27, 49, 65, 75, 42, 25, 46, 14};
        for (int i = 0; i < inputBytes.length; i++) {
            xorResult[i] = (byte) (inputBytes[i] ^ tokenBytes[i % tokenBytes.length]);
        }
        int i2 = xorResult.length;
        if (i2 != array.length) {
            return "No no no! Rabbit shakes its head.";
        }
        for (int i3 = 0; i3 < xorResult.length; i3++) {
            if ((xorResult[i3] & UByte.MAX_VALUE) != array[i3]) {
                return "No no no! Rabbit shakes its head.";
            }
        }
        return "Rabbit is very happy.";
    }
}

按照这个算法还原回去,用java写的还原:
[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public static void main(String[] args) {
        // 定义固定的 token 字符串和 array 数组
        byte[] tokenBytes = "TokenashiTokeari".getBytes();
        int[] array = {50, 3, 10, 2, 21, 53, 1, 17, 73, 28, 14, 25, 1, 4, 0, 72, 116, 41, 7, 4, 9, 65, 26, 27, 73, 26, 0, 31, 69, 41, 23, 27, 49, 65, 75, 42, 25, 46, 14};
        // 初始化一个 byte 数组来存储 flag 的字节
        byte[] flagBytes = new byte[array.length];
        // 根据 array 和 tokenBytes 反向推导出 flag 的每个字节
        for (int i = 0; i < array.length; i++) {
            // 计算 xorResult[i] (即 array[i]),并通过 XOR 计算出 flagBytes[i]
            int xorResult = array[i] & 0xFF// array[i] 的低 8 位
            byte tokenByte = tokenBytes[i % tokenBytes.length];  // token 字符串的字节
            // 反向推导出 flagBytes[i]
            flagBytes[i] = (byte) (xorResult ^ tokenByte);
        }
        // 将 byte 数组转换为字符串,得到 flag
        String flag = new String(flagBytes);
        System.out.println("推测的 flag 是: " + flag);
    }

最后还原出来的结果是flag{Try Harder! Flag is Not Here. OwO},这个flag明显不对,但是当我把这个输入到这个apk里的时候它提示我通过了,用frida打印这个方法,提示是native方法,说明这个java方法是被转换为了native函数的
[Asm] 纯文本查看 复制代码
1
2
3
4
5
6
7
8
function dumpjava() {
    Java.perform(function () {
        var targetClass = Java.use('com.android.refantazio.rabbitHole');
        console.log(targetClass.callRabbit);
        console.log(ptr(targetClass.callRabbit).add(30).readPointer());
});
[attach]2737261[/attach]
}

那就去ida里看看librefantazio.so干了啥。
[Asm] 纯文本查看 复制代码
1
2
3
4
0x7D522C:
    sub_7D5068(v1);
    v2 = sub_1065070("frida");
    qword_1237AB8 = sub_7D69EC(v2);

有很多地方都有明显的frida字串特征,这是一个frida库,在JNIOnLoad里启动的,直接用CE搜Java.use,就能搜出来它的脚本:
156475fcb9849828414d3543b3758150.png
[JavaScript] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function(e) {
    var a = Java.use("java.lang.String");
    // 如果输入的字符串长度不为41,返回错误信息
    if (41 != e.length) {
        return a.$new("No no no! Rabbit shakes its head.");
    }
    let t = [];
    let n = "n1cTfOwO".length;
    // 初始化t数组为0-255
    for (let i = 0; i < 256; i++) {
        t[i] = i;
    }
    let o = 0;
    // 使用"n1cTfOwO"进行置换初始化
    for (let i = 0; i < 256; i++) {
        o = (o + t[i] + "n1cTfOwO".charCodeAt(i % n)) % 256;
        [t[i], t[o]] = [t[o], t[i]];
    }
    let r = 0;
    o = 0;
    // 定义一个预定的数字数组
    let i = [
        59, 67, 58, 32, 172, 94, 161, 232, 59, 225,
        56, 210, 206, 94, 123, 253, 112, 252, 41, 136,
        71, 102, 81, 80, 128, 39, 22, 44, 176, 41,
        205, 197, 5, 247, 68, 151, 127, 29, 251, 58,
        85
    ];
    // 遍历输入字符串进行比对
    for (let n = 0; n < e.length; n++) {
        o = (o + t[r = (r + 1) % 256]) % 256;
        [t[r], t[o]] = [t[o], t[r]];
        let l = t[(t[r] + t[o]) % 256];
        // 如果解密结果与预定数字数组不匹配,返回错误信息
        if ((e.charCodeAt(n) ^ l) != i[n]) {
            return a.$new("No no no! Rabbit shakes its head.");
        }
    }
    // 如果匹配,返回成功信息
    return a.$new("Rabbit is very happy.");
}

这个一眼RC4算法,Key是n1cTfOwO
QIZLRFKDZ6X(2XJL5DBWE@D.png
解密得出flag为 flag{Fr1da_GuM_J5_1s_S0_Pow3rFu11l1l!!!!}
0x2 碎碎念
这道题比较坑的是frida有的版本不适配导致走了很多弯路,我一度觉得是不是hook了JAVA String类的getBytes、toString之类的一些方法,魔改了frida,使用了一些比较底层的接口,所以找不到它真正修改的地方,就一直在看frida的实现原理,结果后面问了一手发现就是frida的bug。不过在寻找解决方法的过程中阅读了frida的源码,也学到了很多frida具体的实现原理。
样本链接:
https://wwuz.lanzouv.com/ih3NN2fg4h1g
密码:3x5q

免费评分

参与人数 4吾爱币 +9 热心值 +4 收起 理由
正己 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
LingMj + 1 我很赞同!
wuhuhu + 1 + 1 我很赞同!
xuanxuanba + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

头像被屏蔽
devildomlu2 发表于 2024-11-18 16:49
提示: 作者被禁止或删除 内容自动屏蔽
zlniubi0729 发表于 2024-11-18 17:20
feihaoge 发表于 2024-11-18 18:46
CoinsBtc 发表于 2024-11-18 19:07
谢谢分享
s1986q 发表于 2024-11-18 19:27
谢谢楼主分享,ctf在哪里
 楼主| jbczzz 发表于 2024-11-18 20:31
s1986q 发表于 2024-11-18 19:27
谢谢楼主分享,ctf在哪里

已添加到文章末尾
butterball 发表于 2024-11-18 22:29
感谢楼主分享
MGGA4689 发表于 2024-11-19 09:13
感谢楼主分享!
zyh5028 发表于 2024-11-19 11:15
感谢分享!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-22 02:04

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表