cnss_re

CNSS新生赛的逆向题题解

Reverse Guideline

教程,直接拿到flag

Welcome to Reverse World!

用ida打开即可看到flag

Find me

用ida看伪代码,介绍从三个方向去找flag:string、function和xref

shift-f12查看字符串找到第一部分

image-20230816194830839

左侧函数框最上方看到第二部分

image-20230816194850994

选中函数按x查看交叉引用

image-20230816194936131

按r把数字变成字符

最后拼接flag

maware

查看代码逻辑,先对输入异或,encode函数里是每四字节输入把bit置换,结果要求等于一个给定数组(shift+e导出数据)

写一个逆脚本即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include<stdio.h>

void encode(unsigned char *a1, unsigned char *a2, unsigned char *a3, unsigned char *a4)
{
unsigned char v5; // [rsp+2Ch] [rbp-4h]
unsigned char v6; // [rsp+2Dh] [rbp-3h]
unsigned char v7; // [rsp+2Eh] [rbp-2h]
unsigned char v8; // [rsp+2Fh] [rbp-1h]

v5 = (*a1 << 7) | (*a2 >> 1);
v6 = ((*a4 >> 2) | (*a3 << 6)) ^ v5;
v7 = ((*a1 >> 1) | (*a2 << 7)) ^ v6;
v8 = ((*a3 >> 2) | (*a4 << 6)) ^ v7;
*a1 = v5;
*a2 = v6;
*a3 = v7;
*a4 = v8;
}

void decode(unsigned char *a1, unsigned char *a2, unsigned char *a3, unsigned char *a4){
unsigned char m1, m2, m3, m4;
m1 = *a1;
m2 = *a2 ^ *a1;
m3 = *a3 ^ *a2;
m4 = *a4 ^ *a3;

*a4 = ((m2 << 2) | (m4 >> 6));
*a3 = ((m4 << 2) | (m2 >> 6));
*a2 = ((m1 << 1) | (m3 >> 7));
*a1 = ((m3 << 1) | (m1 >> 7));
}

int main(){
unsigned char cipher[] =
{
0x3F, 0x8F, 0xA3, 0xBC, 0x8D, 0x27, 0x7A, 0x67, 0xE2, 0x03,
0xA2, 0xE0, 0xAC, 0xEA, 0x95, 0x8B, 0xA3, 0xED, 0xCC, 0xB6,
0x32, 0x8C, 0x94, 0x52, 0x82, 0x8A, 0x14, 0xC6, 0xF5, 0xAE,
0x68, 0x73, 0x00
};
for(int i = 28; i >= 0; i--){
decode(&cipher[i], &cipher[i+1], &cipher[i+2], &cipher[i+3]);
}
for(int i = 0; i < 32; i++){
cipher[i] ^= 0x11;
printf("%c", cipher[i]);
}

return 0;
}

encode

代码逻辑是输入一个字符串,经过encode函数后要求等于一个给定的字符串

image-20230816195750648

encode函数是每三个字符进行处理,一开始没看懂,把61那里变成字符('=')后大概猜到了是base64,里面的alpha数组确实是个表,但是和标准的表不一样,写个脚本就可解密

You've got a flag!

考点是patch程序

流程是要求让coin小于0,每轮coin会自减去1/4,但是小于一个值的时候就会加上1

查看指令后是用addsd实现的,那么很显然就会想到把它改成subsd即可

在option-general可以选择查看字节码,再在网上搜对应subsd的字节码即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def modify_machine_code(filename, original_code, new_code):
with open(filename, "rb") as file:
content = file.read()

if original_code in content:
modified_content = content.replace(original_code, new_code)
# print("机器码找到了")
with open(filename, "wb") as file:
file.write(modified_content)
print("机器码修改成功")
else:
print("未找到原始机器码")

# 文件名
filename = "You've got a flag!.exe"

# 原始机器码
original_code = b"\xF2\x0F\x5B\xC8"
# 新的机器码
new_code = b"\xF2\x0F\x5C\xC8"

modify_machine_code(filename, original_code, new_code)

diannaobaozhale

考点是看汇编指令

其实整个流程就是把flag输出了,照着走一遍即可,坑的话可能就是jle这个指令的意思是Jump if Less than or Equal,所以程序一共要循环10次而不是9次

Pyfuck

考点算是变量混淆?把值变成了这个fuck的表示,打印一遍就知道是啥了,再异或回去即可

CNSS Rev Challenge

c#写的一个小游戏,最后要把让看板娘跑到语句后面。

逆了半天也没发现怎么改配置文件,最后在hurrison的提示下,了解到可以在程序运行到那个阶段的时候查看内存,搜索字符串就能拿到flag了

用ida或cheat engine都可以

EzArm

考点是arm逆向,出题人手写的arm汇编指令,太牛了

先装个qemu环境,用gdb调试程序,发现程序从最开始跳到了10000的地方,那么程序起始的地址应该是10000,然后就可以在ida中看到伪代码了,整个流程不难,经过某些运算后要等于一个数组,有个关键数组是动态生成的,所以ida里直接找不到,根据地址在gdb里查看即可

最后的exp如下:

1
2
3
4
5
6
7
secret = [0x6E, 0x6D, 0x79, 0x7E, 0x82, 0x53, 0x3D, 0x84, 0x55, 0x5F, 
0x4A, 0x6B, 0x79, 0x6B, 0x73, 0x6A, 0x72, 0x66, 0x3D, 0x86,
0x55, 0x6C, 0x46, 0x67, 0x6A, 0x48, 0x77]
x = [1, 0xc, 0xb]

for i in range(0x1b):
print(chr(((secret[i] - x[(i+1)%3]) % 256) ^x[i % 3]), end="" )

Brainfuck++

不会

Eazy Blockchain

evm逆向,给了bytecode和check函数

运行后90w行的调试信息,看关键逻辑,一共分为两部分。

这个地方是对输入的bytes进行判断,如果长度不是0x20就revert了,所以可以得知flag是32字节的.

image-20230821105646840

后面就是漫长的审代码,这里把calldata存到栈里:

image-20230821105908007

接下来是一个字节一个字节的加密操作,第一步的加密是用8字节的key对输入进行异或操作,把几个key找到后就可恢复这一部分。

第二部分和第一部分类似,但我实在是累的一,这已经到90多万行,不想再一个个调,发现逻辑也是异或后,直接写了个shell脚本进行字节翻转,但没有完全自动化,最后还是手调的flag。

1
2
3
4
5
6
#!/bin/sh
rm output.txt
python demo.py > output.txt
grep "EQ" output.txt | wc -l
grep -B 4 "EQ" output.txt | grep -v "^--$" | tail -n 3 | head -n 1

最后到return就代表跑通了

image-20230821110811948