2024CISCN—初赛安卓逆向androidso_re

解题过程:

这一题拿到手就是一个apk文件,不过在进行测试的时候遇到了很多无语的事情(现在的题目质量是挺堪忧的)

安装好后一眼看上去就是一个判断输入内容得:

直接进行反编译,这里首先是:放入到jadx中

这里就是对于flag进行了判断,首先判断的是格式开头以及结尾“flag{}”,以及长度,32为位。中间的内容都在inspect函数中:

紧跟着进入inspect函数:

 这里返现其中是先进行DES的加密,之后又是进行了base64的加密,最终与"JqslHrdvtgJrRs2QAp+FEVdwRPNLswrnykD/sZMivmjGRKUMVIC/rw=="进行比较,比较后,如果一样就是输出"You are right.",如果错了就是"You are wrong."。

这里的关键就是这个key和IV的值。进一步跟进进入jni类:

静态的在native层,这里可以直接hook,得到iv与key的值。下面就是,在联想模拟器中用的版本是7.1的但是一旦进行判断就会终止程序。调制了一会发现少了一个动态链接文件。这个文件就是libc++_shared.so

 一开始hook就会触发这个程序停止运行。试了好久,最后想着用arm64-v8版本的去直接运行应该可已规避这种问题,后来找了一圈发现在雷电模拟器可以运行一次,之后就是hook直接获取:

之后输入合适的flag{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}

// hook.js
Java.perform(function () {
    var jniClass = Java.use("com.example.re11113.jni");

    jniClass.getiv.implementation = function () {
        var iv = this.getiv(); // 获取原始返回值
        console.log("IV: " + iv); // 打印 IV 值
        return iv; // 返回原始值,不影响应用运行
    }
    jniClass.getkey.implementation = function () {
        var key = this.getkey(); // 获取原始返回值
        console.log("key: " + key); // 打印 IV 值
        return key; // 返回原始值,不影响应用运行
    }
});

 最后就是解密的脚本:

import base64
from Crypto.Cipher import DES

def decrypt_flag(encrypted_flag, key, iv):
    # 确保密钥长度为 8 个字节
    key = key.ljust(8, b'\0')
    
    cipher = DES.new(key, DES.MODE_CBC, iv)
    decrypted_bytes = cipher.decrypt(base64.b64decode(encrypted_flag))
    
    try:
        return decrypted_bytes.decode('utf-8').rstrip('\x00')
    except UnicodeDecodeError:
        return decrypted_bytes.decode('latin-1').rstrip('\x00')

def main():
    encrypted_flag = "JqslHrdvtgJrRs2QAp+FEVdwRPNLswrnykD/sZMivmjGRKUMVIC/rw=="
    
    # 通过Frida脚本拿到key和iv
    key = b'A8UdWaeq'
    iv = b'Wf3DLups'
    
    flag = decrypt_flag(encrypted_flag, key, iv)
    print("Decrypted flag:", flag)

if __name__ == '__main__':
    main()
#Decrypted flag: 188cba3a5c0fbb2250b5a2e590c391ce

最后成功的拿到flag

赛后反思:

这里后面发现还有一种方法,就是直接在so文件中逆出整个的加密过程,这个方法妙啊,但是有些慢。

下面来进行分析一下:

这里看到了码表,一般会直接猜base64加密: 其中先是rot13的加密过程,这里为了方便对比直接放上用C语言实现rot13加密的过程:

#include <stdio.h>

// ROT13 加密函数
void rot13(char *str) {
    char *p = str;
    while (*p) {
        if ((*p >= 'A' && *p <= 'Z')) {
            *p = ((*p - 'A' + 13) % 26) + 'A';
        } else if ((*p >= 'a' && *p <= 'z')) {
            *p = ((*p - 'a' + 13) % 26) + 'a';
        }
        p++;
    }
}

int main() {
    char text[100];

    printf("请输入要加密的文本: ");
    fgets(text, sizeof(text), stdin);

    // 去除换行符
    size_t len = strlen(text);
    if (len > 0 && text[len-1] == '\n') {
        text[len-1] = '\0';
    }

    rot13(text);

    printf("加密后的文本: %s\n", text);

    return 0;
}

这里就不放解密文本了,因位它是对称加密

下面放上IDA的so文件的关键部分:

所以进行rot13解密,后面发现这里的与标准的rot不同,

这里是变换为16位,65-49=97-81=16。后面就是base64

Wf3DLups这个是位移IV

下面是key:

这里很容易就发现可能是RC4加密:RC4加密是需要输入key值得,这里不难发现是有的:

YourRC4Key,而加密后的密文:TFSecret_Key

但是这里有个好玩得函数jiejie(姐姐函数)

这里先贴上一个RC4加解密的C语言代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// RC4 初始化函数
void rc4_init(unsigned char *s, unsigned char *key, int key_length) {
    int i, j = 0;
    unsigned char k[256];
    unsigned char tmp;

    // 初始化 S 数组
    for (i = 0; i < 256; i++) {
        s[i] = i;
        k[i] = key[i % key_length];
    }

    // 初始排列
    for (i = 0; i < 256; i++) {
        j = (j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
}

// RC4 加密/解密函数
void rc4_crypt(unsigned char *s, unsigned char *data, int data_length) {
    int i = 0, j = 0, t, k;
    unsigned char tmp;

    for (k = 0; k < data_length; k++) {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;

        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;

        t = (s[i] + s[j]) % 256;
        data[k] ^= s[t];
    }
}

int main() {
    unsigned char key[] = "mysecretkey";  // 密钥
    unsigned char data[] = "Hello, World!";  // 要加密或解密的数据
    int data_length = strlen((char *)data);
    unsigned char s[256];

    printf("原始数据: %s\n", data);

    // 初始化 RC4
    rc4_init(s, key, strlen((char *)key));

    // 加密
    rc4_crypt(s, data, data_length);
    printf("加密后的数据: %s\n", data);

    // 重新初始化 RC4 以便解密
    rc4_init(s, key, strlen((char *)key));

    // 解密
    rc4_crypt(s, data, data_length);
    printf("解密后的数据: %s\n", data);

    return 0;
}

仔细分析发现下面还有异或的操作: 

异或的是0x038933B8540C206A

后得到:A8UdWaeqÁ²k. 下面又有个细节,就是最后只要了8位

最终得key:A8UdWaeq

总结

其实刚开始想过去看so文件,但是呢,伪代码又臭又长,所以看到反编译后,在native层,就直接尝试了hook,结果就知道会在运行上出现问题,反正题目嘛,就是那样,具体会运行出现程序异常终止的原因,有的博主已经分析过了,在这里就不重复分析这里的原因了,这里主要还是记录做法。

本人菜鸟一枚,如有错误还请大佬多多指正。

  • 18
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值