[CISCN2024]androidso_re题解以及崩溃原因分析

前言

虽然没有参加CISCN,但在初赛赛题中遇到一个APK逆向的题目,对于最近学习安卓技能的我来说,正好是个练习的机会。对此题目,锐评一下:出题人可能在测题时不够严谨,或者故意为之,导致很多人可以直接hook关键点,秒破,但部分人却频繁遇到崩溃问题,导致这道题在不同选手手中的难度不一致。然而正好我就是那个频繁崩溃的那一批,由此分析了一下崩溃的原因并且分享一下解决方法。

分析

file

看上面的MainActivity其实可以发现程序非常简单明了,感觉都可以一把秒。主要在legal里面,首先对flag格式做了判断,后续才对flag内容进行判断,如果格式不通过则不会触发jni的内容。

paramString.length() == 38 && paramString.startsWith("flag{") && paramString.charAt(paramString.length() - 1) == '}' && !inspect.inspect(paramString.substring(5, paramString.length() - 1));

如上则为判断语句,规定了当Flag长度为38,且格式满足flag{xxx}的时候才进入inspect,主要判断逻辑也是在inspect中完成。

file

审计inspect的代码,可以发现其实就是一个普通的DES,这道题的槽点和难点其实也就集中在这里了,我们会发现程序的Key和Iv都来自于Native层,其实审计Native层代码之后,大家都会发现算法繁琐且复杂,但是我们可以通过直接Hook的方法,去获取Iv与Key。
在这之前,我们得先看看运行效果。
发现如果格式不满足,程序正常返回Wrong

file

一旦格式满足,则程序直接崩溃了。
我们通过LogCat查看一下日志,究竟试什么原因崩溃。

file

查看日志,崩溃原因就有点显然了,总结如下:

程序在调用 JNI 函数 NewStringUTF 时传递了一个非法的 UTF-8 编码字符串。具体来说,错误日志指出输入的字节序列 0x9d 0x80 0x99 0xd4 0xac 0xc9 0xd3 0xf8 包含非法的 UTF-8 起始字节 0x9d。

NewStringUTF 函数期望接收一个合法的 UTF-8 编码的字符串,而不是任意字节序列。如果传入的字节序列不符合 UTF-8 编码规范,就会导致 JNI 检测到错误并抛出异常。

看到这里就有点抽象了为啥会有非UTF8编码的字符串呢,我们只能分析一下Native的逻辑了

在这之前我们先看一下正常来说如果Native层需要返回一个Jstring应该怎么写呢

extern "C" JNIEXPORT jstring JNICALL
Java_com_swdd_ahandroid_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

没错在return的时候 使用env->NewStringUTF(hello.c_str());来返回Jstring,所以这里出现的情况就是NewStringUTF抛出了异常,对应Native层代码如下:

file

首先我们得想办法知道这个导致崩溃的八个字节究竟是什么东西
多运行,多崩溃几次发现,如果多尝试几次使用Hook主动调用GetKey和程序自主触发产生的居然不一致,至此开始玄学起来了。

file

file

为了方便分析问题所在的地方,我们写脚本Hook NewStringUTF看看咋个事
hook脚本如下:

function hookNewStringUTF() {
    Java.perform(function () {
        var modules = Process.enumerateModules();
        var newStringUTFAddr = null;

        for (var i = 0; i < modules.length; i++) {
            var module = modules[i];
            try {
                var symbols = module.enumerateSymbols();
                for (var j = 0; j < symbols.length; j++) {
                    if (symbols[j].name.indexOf("NewStringUTF") >= 0) {
                        newStringUTFAddr = symbols[j].address;
                        console.log("Found NewStringUTF in module: " + module.name + " at: " + newStringUTFAddr + " with name: " + symbols[j].name);
                        break;
                    }
                }
            } catch (e) {
                console.warn("Failed to enumerate symbols for module: " + module.name);
            }
            if (newStringUTFAddr) {
                break;
            }
        }

        if (newStringUTFAddr) {
            // Hook NewStringUTF 函数
            Interceptor.attach(newStringUTFAddr, {
                onEnter: function(args) {
                    // 获取传入的 char* 参数
                    var inputStr = args[1].readCString();
                    console.log("NewStringUTF called with: " + inputStr);
                },
                onLeave: function(retval) {
                    // 如果需要修改返回值,可以在这里进行
                    console.log("NewStringUTF returning: " + retval);
                }
            });
        } else {
            console.log("NewStringUTF address not found!");
        }
    });
}

通过Hook得到的值如下,随后就崩溃了。

file

结尾

其实这个解决方法,看似无解,但是我发现当我们使用attach对于getkey时,程序则正常返回,并且变得可以调试进入,示例代码如下:

function hook() {
    let jni = Java.use("com.example.re11113.jni");
    var iv = jni.getiv();
    var getKeyBase = Module.findExportByName("libSecret_entrance.so", "Java_com_example_re11113_jni_getkey");

    Interceptor.attach(getKeyBase, {
        onEnter: function(args) {
        },
        onLeave: function(retval) {
        }
    });
}

一旦我们附加这个代码,原本一调试就未响应的程序,变得可以调试了

file

调试之后也可以看到正确的值
file

那这样,整个程序就变得微妙了起来,测试了很多遍,都是只有在使用上述方法attach上之后程序就不再崩溃,并且目前没法找到原因。
既然正常,这个题的题的题解便是如此了
HOOK代码如下

function hook() {
    let jni = Java.use("com.example.re11113.jni");
    var iv = jni.getiv();
    console.log("IV: "+iv);
    var getKeyBase = Module.findExportByName("libSecret_entrance.so", "Java_com_example_re11113_jni_getkey");

    Interceptor.attach(getKeyBase, {
        onEnter: function(args) {
        },
        onLeave: function(retval) {
        }
    });
    var key = jni.getKey();
    console.log("Key: " + key);
}

最后解des即可

file

评论

  1. imLZH1
    2 年前
    2024-6-18 19:06:35

    tql 哥哥呜呜呜

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇