复现CISCN2024 Reverse的收获

androidso_ez

参考文章:https://www.cnblogs.com/sK07XdAy/p/18203747

静态分析

1、当有时候代码很长的时候,想自己猜猜加密含义时,可喂给chatgpt一试
2、Rot13,以前也经常见,可就是没有仔细看看,虽然很简单,但可以了解了解偏移量,喂给Cyberchef会更省事些
Rot13,顾名思义一般情况下偏移13,如下

//ch为大写字母
ch=(ch-'A'+13)%26+'A'
//ch为小写字母
ch=(ch='a'+13)%26+'a'

当代码块中的13改为16,即为偏移16,对于偏移13:chrot13(rot13(ch))而对于偏移16:chrot16(rot10(ch))
3、re真是一半靠蒙一半靠猜啊,猜测jiejie函数为Rc4加密,猜测Rc4加密后以此异或0x03,0x89,0x33,0xB8,0x54,0x0C,0x20,0x6A
img
4、对于源代码,其中有key算法为AES,加密却是DES的矛盾,查阅资料后有以下分析:

public SecretKeySpec​(byte[] key, String algorithm)
从给定的字节数组构造一个密钥。此构造函数不检查给定的字节是否确实指定了指定算法的密钥。例如,如果算法是DES,则此构造函数不检查key是否为8字节长,并且也不检查弱键或半弱键。
这句话从参考文章中偷的,也没看太懂
img
就虽然SecretKeySpec key = new SecretKeySpec(str2, "AES");但由于SecretKeySpec()函数原因,加密Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");依旧按照DES加密。

动态分析

1、看到一篇文章可以尽量避免程序闪退问题 https://blog.csdn.net/qq_65474192/article/details/139060012
img
也不知道为什么看的是class.dex,我看apk的操作系统是Android13啊,emo.

asm_re

参考文章:https://blog.csdn.net/Myon5/article/details/139046502
1、处理ida跑出来的汇编代码时,如果给了下图的信息,从图中可以获取的主要信息
img

Processor       : ARM   //ARM架构
Byte sex        : Little endian     //小端序
CODE64      //64位文件

2、如果给了机械码,我们可以把文本文件喂给gpt,输出机械码
img
之后到010editor中新建十六进制文件,ctrl+shift+v复制进去,之后按照给定的格式用ida打开
img
反编译即可
3、解密:emm...反编译后,还是看不懂,哎,就假装我看懂了吧。之后把cipher的部分喂给gpt把密文取出来,解密即可

#include <stdio.h>

int main() {
    unsigned int flag[] = {0x1FD7, 0x21B7, 0x1E47, 0x2027, 0x26E7, 0x10D7, 0x1127, 0x2007, 0x11C7, 0x1E47, 0x1017,
                        0x1017, 0x11F7, 0x2007, 0x1037, 0x1107, 0x1F17, 0x10D7, 0x1017,0x1F67, 0x1017, 0x11C7, 0x11C7, 0x1017, 0x1FD7, 0x1F17, 0x1107, 0x0F47, 0x1127, 0x1037, 0x1E47, 0x1037, 0x1FD7, 0x1107, 0x1FD7, 0x1107, 0x2787};
    for (int i = 0; i < 38; ++i) {
        flag[i] = flag[i] - 30;
        flag[i] ^= 0x4d;
        flag[i] -= 20;
        flag[i] /= 80;
        printf("%c", flag[i]);
    }
    return 0;
}

whereThel1b

1、看不懂,根本看不懂,果然就是快乐猜猜猜啊,也不能说没有收获
img
cpython阅读可以从string下手,很多函数看不懂,其实都有他们在py中简单的名称的,例如cpython中的__pyx_k_random在这里就能看到是random,之后交叉引用,就能尝试猜一猜如何加密了
2、对于base64可以爆破,每三个字符三个字符爆破,三个字符输出四个数据,check成功就输出.而且可以根据密文数据得到flag长度(56/4)*3

gdb_debug

1、随机数的深入研究:伪随机数列指的是以一个种子生成的随机数,它在相同的位置的值是相同的

seed=0x0;
srand(seed);
for (int j = 0; j < 38; ++j) {
    printf("0x%x, ",rand());
}

上面的代码会生成38个不同的随机数.
img

seed=0xf0000000;
for (int j = 0; j < 38; ++j) {
    srand(seed);
    printf("0x%x, ",rand());
}

而此代码只会生成38个0x26.

seed=0xf0000000;
for (int j = 0; j < 38; ++j) {
    srand(seed);
    rand();
    printf("0x%x, ",rand());
}

此代码会生成38个0x1e27.以此类推下去,不难理解伪随机数列的含义
2、在做伪随机数题目时,需要注意时linux文件还是windows文件,如果时linux文件要将脚本在linux系统下执行因为两个系统生成的随机数是不同的,但也都是伪随机数列

gcc -o hello hello.c

3、逆天,一定要注意程序生成了多少次随机数啊啊啊啊啊啊!!!!

//此处调用了38次rand()
for ( i = 0LL; ; ++i )
{
  len2 = strlen(input);
  if ( i >= len2 )
    break;
  rand_num = rand();
  *(v28 + i) = input[i] ^ rand_num;
}
//Fisher-Yates 洗牌算法的一种实现,更换数组位置
//此次调用了37次rand()
for ( j = 0LL; ; ++j )
{
    v8 = strlen(input);
    if ( j >= v8 )
    break;
    *(ptr + j) = j;
    }
for ( k = strlen(input) - 1; k; --k )
{
    v18 = rand() % (k + 1);
    v19 = *(ptr + k);
    *(ptr + k) = *(ptr + v18);
    *(ptr + v18) = v19;
}
//此处调用了38次rand
for ( n = 0LL; ; ++n )
{
    v13 = strlen(input);
    if ( n >= v13 )
      break;
    v12 = *(v31 + n);
    *(v31 + n) = rand() ^ v12;
}

故可以分别用rand1[38],rand2[37],rand[38]存放,这里很关键,如果用rand2[38],那么rand3[38]全是错的.
3、爆破的时候,一定要注意将cipher置回初始值,吐了,研究半天不知道哪里写错了,用gpt对了一下,才发现是cipher在爆破一次后值就变了,后续的爆破根本就是不成立的.
4、i*0x10000000 <=> i<<28,而不是i<<7
img
5、在搜索随机数的逆向的时候,发现一个挺好的题目: https://blog.csdn.net/weixin_45055269/article/details/112426573
这题也是随机数的一种出题的思路,改时间戳,将随机数异或图片的数据,动态调试,将时间戳改为图片的生成时间,再异或一遍,就得到原图了.

posted @ 2024-06-02 17:52  Un1corn  阅读(16)  评论(0编辑  收藏  举报