复现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
4、对于源代码,其中有key算法为AES,加密却是DES的矛盾,查阅资料后有以下分析:
public SecretKeySpec(byte[] key, String algorithm)
从给定的字节数组构造一个密钥。此构造函数不检查给定的字节是否确实指定了指定算法的密钥。例如,如果算法是DES,则此构造函数不检查key是否为8字节长,并且也不检查弱键或半弱键。
这句话从参考文章中偷的,也没看太懂
就虽然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
也不知道为什么看的是class.dex,我看apk的操作系统是Android13啊,emo.
asm_re
参考文章:https://blog.csdn.net/Myon5/article/details/139046502
1、处理ida跑出来的汇编代码时,如果给了下图的信息,从图中可以获取的主要信息
Processor : ARM //ARM架构
Byte sex : Little endian //小端序
CODE64 //64位文件
2、如果给了机械码,我们可以把文本文件喂给gpt,输出机械码
之后到010editor中新建十六进制文件,ctrl+shift+v复制进去,之后按照给定的格式用ida打开
反编译即可
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、看不懂,根本看不懂,果然就是快乐猜猜猜啊,也不能说没有收获
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个不同的随机数.
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
5、在搜索随机数的逆向的时候,发现一个挺好的题目: https://blog.csdn.net/weixin_45055269/article/details/112426573
这题也是随机数的一种出题的思路,改时间戳,将随机数异或图片的数据,动态调试,将时间戳改为图片的生成时间,再异或一遍,就得到原图了.