CISCN2021初赛WriteUp

Posted Hellsegamosken

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CISCN2021初赛WriteUp相关的知识,希望对你有一定的参考价值。

第一次打 ctf,啥也没学,赛前一道题没做过,了解了下 IDA 的使用就上了。

glass

200 分 reverse,安卓逆向,.apk 改 .zip 解压,classes.dex 转 classes.jar 后打开,找到 MainActivity 开始读代码。
在这里插入图片描述
发现调用了 public native boolean checkFlag(String paramString); 这样一个函数,但这个函数并没有写出来。搜了一下 native 关键字,发现是用来调用本地 C 代码的。然后在 glass\\lib\\armeabi-v7a 里 找到了 libnative-lib.so,直接拖进 IDA 打开。
在这里插入图片描述
分析一波,发现 sub_F0C 是用来把接收的 Java String 转换成 char*。然后主要剩下 sub_FFC, sub_1088, sub_10D4, 这三个函数。进一步分析,猜测程序的主要逻辑是把 flag 经过这三个函数处理,和内存中的一段数据比较。
在这里插入图片描述

因此只要把这段数据解密回去就行了。由于没有任何前置知识,于是只能进去一行一行读明白。

FFC:
在这里插入图片描述
大概就是会得到 v7 这个数组。有几个疑点:

  1. 第八行为什么是 260 个字节而不是 256。
  2. 变量 v7 既没初始化也没有修改。(我就当做 v7 恒为零做的,不行)
  3. 莫名其妙的有个 sub_126C,貌似没有任何影响。

赛后才知道,事实上这是在进行 RC4 加密,而 RC4 加密 v7 应该是不停自增的。

然后 10D4 是对原文进行可逆的异或运算,很好解密。1088 是 拿已知的 v7 数组和原文进行运算,也很好解密。但 1088 里面有一个问题:
在这里插入图片描述
这里面 a1 就是主函数的 v7,值域是 [0, 255],因此第十八行 v5 + a1[v4] 可能会越界。尝试对 256 取模,无果。赛后知道,这里确实应该取模,但因为比赛的时候上面 FFC 里面的 v7 没自增,于是没有解出。

我不知道为什么 IDA 里面会出现这种情况,里面的算法和 RC4 不完全一样,因此完全按照 IDA 里面的代码写的脚本跑不出 flag。别人都是百度 RC4 解密直接解出 flag。

插一嘴,我看别的 WP,1088 和 10D4 都是暴力枚举解密的,不是逆运算回去的。这样可能根本不需要每行代码都看明白?毕竟 RC4 应该是能一眼看出来的。

赛后修改过的脚本如下:

# include <bits/stdc++.h>
# define ll long long
# define db double
# define ld long double
# define pb push_back
# define fir first
# define sec second
# define rep(i, l, r) for (int i = l; i <= r; i++)
# define per(i, r, l) for (int i = r; i >= l; i--)
using namespace std;
typedef pair <int, int> P;

int read() {
    int x = 0; char c = getchar(), flag = '+';
    while (!isdigit(c)) flag = c, c = getchar();
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
    return flag == '-' ? -x : x;
}
char v12[1000];

void FFC(unsigned a1[], unsigned a2[], int a3) {
    const int ty = 1;
    int ii = 0, jj = 0;
    unsigned v12[256];
    for (int i = 0; i < 256; i++) a1[i] = i, v12[i] = a2[i % 8];
    while (ii != 256) {
        int v10 = a1[ii];
        jj = (jj + v10 + v12[ii]) % 256;
        a1[ii++] = a1[jj];
        a1[jj] = v10;
    }
}

void rev(unsigned s[], int len1, unsigned a3[], int len3) {
    for (int j = 0; j < len1; j += len3) {
        for (int k = 0; k < len3 && j + k < len1; k++) {
            s[j + k] ^= a3[k];
        }
    }
    for (int i = 0; i < len1; i += 3) {
        unsigned a = s[i], b = s[i + 1], c = s[i + 2];
        s[i] = b ^ c;
        s[i + 1] = b ^ a;
        s[i + 2] = b ^ a ^ c;
    }
}
void sub_1088(unsigned result[], unsigned a2[], int a3) {
    int v3 = 0, v4 = 0, v5 = 0;
    int loop_39[39];
    memset(loop_39, 0, sizeof(loop_39));
    while(a3) {
        --a3;
        v4 = (v4 + 1) % 256;
        v5 = result[v4];

        v3 = (v3 + v5) % 256;
        result[v4] = result[v3];
        result[v3] = v5;
        //if (v5 + result[v4] > 255) cout<<"#";
        loop_39[38 - a3] = result[(v5 + result[v4]) % 256];
    }
    for (int i = 0; i < 39; i++) {
        a2[i] ^= loop_39[i];
    }
}
int main() {
    unsigned v6[256], v7[256];
    unsigned v3[] = {0xA3, 0x1A, 0xE3, 0x69, 0x2F, 0xBB, 0x1A, 0x84, 0x65, 0xC2, 0xAD, 0xAD, 0x9E, 0x96, 0x05, 0x02, 0x1F, 0x8E, 0x36, 0x4F, 0xE1, 0xEB, 0xAF, 0xF0, 0xEA, 0xC4, 0xA8, 0x2D, 0x42, 0xC7, 0x6E, 0x3F, 0xB0, 0xD3, 0xCC, 0x78, 0xF9, 0x98, 0x3F};
    
    memset(v7, 0, sizeof(unsigned) * 256);

    v6[0] = '1';
    v6[1] = '2';
    v6[2] = '3';
    v6[3] = '4';
    v6[4] = '5';
    v6[5] = '6';
    v6[6] = '7';
    v6[7] = '8';
    int v4 = 8;
    FFC(v7, v6, v4);

    rev(v3, 39, v6, v4);
    sub_1088(v7, v3, 39);
    // for (int i = 0; i < 39; i++) {
    //     printf("%d ", v3[i]);
    // }
    // cout << endl;
    for (int i = 0; i < 39; i++) {
        printf("%c", v3[i]);
    }
    return 0;
}
/* Hellsegamosken */

以上是关于CISCN2021初赛WriteUp的主要内容,如果未能解决你的问题,请参考以下文章

第十五届全国大学生信息安全竞赛(ciscn初赛) 部分writeup

第十五届全国大学生信息安全竞赛(ciscn初赛) 部分writeup

第15届全国大学生知识竞赛场景实操 2022ciscn初赛 部分writeup

[CISCN2021]初赛

[CISCN2021] 初赛 easy_source

[CISCN2021] 初赛 easy_source