Rev

SU_BBRE

给的是汇编,简单阅读一下,翻译如下

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void function3(const char* key, int key_len, unsigned char* perm_table) {
int j = 0;
int i;

// Initialize permutation table
for(i = 0; i < 256; i++) {
perm_table[i] = i;
}

// Generate permutation
for(i = 0; i < 256; i++) {
unsigned char tmp;
j = (j + perm_table[i] + key[i % key_len]) & 0xFF;
// Swap values
tmp = perm_table[i];
perm_table[i] = perm_table[j];
perm_table[j] = tmp;
}
}

void function4(unsigned char* state, char* data, int len) {
int i = 0, j = 0;
int k;

for(k = 0; k < len; k++) {
i = (i + 1) & 0xFF;
j = (j + state[i]) & 0xFF;

// Swap values
unsigned char tmp = state[i];
state[i] = state[j];
state[j] = tmp;

// XOR operation
unsigned char t = state[(state[i] + state[j]) & 0xFF];
data[k] ^= t;
}
}

void function5(const char* key, int key_len, char* data, int data_len) {
unsigned char perm_table[256];
function3(key, key_len, perm_table);
function4(perm_table, data, data_len);
}

void function1() {
char input[16];
int i;
const char check[] = "AmbMSIN("; // 4D626D41h, 294E4953h, 28h

printf("hhh,you find me:\n");
scanf("%s", input);

for(i = 0; i <= 8; i++) {
if((input[i] - i) != check[i]) {
exit(0);
}
}
printf("congratulate!!!\n");
exit(0);
}

int function0(char* src) {
char dest[16];
strcpy(dest, src);
return 0;
}

int function2(char* input) {
const unsigned char check[] = {
0x2F, 0x5A, 0x57, 0x65,
0x14, 0x8F, 0x69, 0xCD,
0x93, 0x29, 0x1A, 0x55,
0x18, 0x40, 0xE4, 0x5E
};

function5("suctf", 5, input, 16);

for(int i = 0; i <= 15; i++) {
if((unsigned char)input[i] != check[i]) {
exit(0);
}
}
}

int main(int argc, char* argv[]) {
char input[108]; // Size 0x6C

printf("please input your flag:");
scanf("%19s", input);

function2(input);
function0(input);

return 0;
}

可以得知以下信息:

  • 输入的总长为19位
  • 前16位输入用了function3function4加密,不难看出是 RC4,key是suctf
  • function1没有调用,但是里面有输入、提示信息以及8位的加密,可能需要将控制流转移到function1
  • function0用到了危险函数strcpy,dest只有16位,而输入最多有19位,明显可以溢出,这溢出的3位刚好可以将控制流转到function1function1地址为0x40223D,由于需要小端序则对应3D 22 40则溢出的三位转为字符为="@

1
2
3
4
enc = [0x41,0x6d,0x62,0x4d,0x53,0x49,0x4e,0x29,0x28]
flag = ''.join([chr(enc[i] + i) for i in range(len(enc))])
print(flag)
#AndPWNT00

SUCTF{We1com3ToReWorld="@AndPWNT00}

SU_minesweeper

读入长度100

读入处理

这400位01代表是否有雷

读入的映射如下

根据这个扫雷

数字代表附近3*3区域有几颗雷,0xFF代表未知。

1
[0x03, 0x04, 0xFF, 0xFF, 0xFF, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0x04, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x04, 0x06, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0x05, 0x06, 0x04, 0xFF, 0x05, 0xFF, 0x04, 0x07, 0xFF, 0x08, 0xFF, 0x06, 0xFF, 0xFF, 0x06, 0x06, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0x03, 0xFF, 0x05, 0x06, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x05, 0x04, 0x05, 0x07, 0x06, 0xFF, 0xFF, 0x04, 0xFF, 0x02, 0x01, 0xFF, 0xFF, 0xFF, 0x03, 0x04, 0xFF, 0xFF, 0x05, 0x04, 0x03, 0xFF, 0xFF, 0x07, 0x04, 0x03, 0xFF, 0xFF, 0x01, 0x01, 0xFF, 0xFF, 0x04, 0x03, 0xFF, 0x02, 0xFF, 0x04, 0x03, 0xFF, 0xFF, 0x02, 0xFF, 0x05, 0x04, 0xFF, 0xFF, 0x02, 0x02, 0xFF, 0xFF, 0x04, 0xFF, 0x04, 0xFF, 0x03, 0x05, 0x06, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0x01, 0x04, 0xFF, 0xFF, 0x07, 0x05, 0xFF, 0xFF, 0x03, 0x03, 0x02, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0x05, 0x07, 0xFF, 0x03, 0x02, 0x04, 0x04, 0xFF, 0x07, 0x05, 0x04, 0x03, 0xFF, 0xFF, 0x04, 0xFF, 0x02, 0x04, 0x05, 0xFF, 0xFF, 0x06, 0x05, 0x04, 0xFF, 0x02, 0xFF, 0xFF, 0x07, 0x04, 0xFF, 0xFF, 0x03, 0xFF, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x03, 0x02, 0x02, 0xFF, 0xFF, 0x02, 0x04, 0x03, 0x05, 0xFF, 0xFF, 0x05, 0xFF, 0x04, 0xFF, 0x06, 0xFF, 0xFF, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0xFF, 0x06, 0x06, 0xFF, 0x07, 0x06, 0x04, 0xFF, 0x04, 0x03, 0xFF, 0x04, 0x03, 0x05, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x06, 0x07, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0x05, 0xFF, 0x05, 0xFF, 0xFF, 0x06, 0x07, 0x07, 0xFF, 0x05, 0x06, 0x06, 0xFF, 0xFF, 0x02, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0xFF, 0xFF, 0x07, 0x07, 0x06, 0xFF, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0x03, 0x05, 0xFF, 0x07, 0xFF, 0x05, 0xFF, 0x06, 0xFF, 0x05, 0xFF, 0xFF, 0x07, 0x08, 0xFF, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0x05, 0x03, 0xFF, 0x04, 0x05, 0x05, 0x03, 0xFF, 0xFF, 0x06, 0x05, 0x05, 0x06, 0xFF, 0x06, 0x05, 0x02, 0x04, 0x03, 0x04, 0xFF, 0xFF, 0x03, 0x04, 0x04, 0x06, 0x05, 0xFF, 0x03, 0xFF, 0x05, 0x05, 0x05, 0xFF, 0xFF, 0x05, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0x04, 0xFF, 0x07, 0x07, 0x08, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0xFF, 0xFF, 0xFF, 0x04, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x03]

约束处理如下

Z3求解即可,然后根据字符映射计算出正确输入,求MD5

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from z3 import *
import hashlib
from typing import List

# 雷图: 0xFF表示未知格子,其他数字表示周围地雷数
MINESWQEEPER_MAP = [0x03, 0x04, 0xFF, 0xFF, 0xFF, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0x04, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x04, 0x06, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0x05, 0x06, 0x04, 0xFF, 0x05, 0xFF, 0x04, 0x07, 0xFF, 0x08, 0xFF, 0x06, 0xFF, 0xFF, 0x06, 0x06, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0x03, 0xFF, 0x05, 0x06, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x05, 0x04, 0x05, 0x07, 0x06, 0xFF, 0xFF, 0x04, 0xFF, 0x02, 0x01, 0xFF, 0xFF, 0xFF, 0x03, 0x04, 0xFF, 0xFF, 0x05, 0x04, 0x03, 0xFF, 0xFF, 0x07, 0x04, 0x03, 0xFF, 0xFF, 0x01, 0x01, 0xFF, 0xFF, 0x04, 0x03, 0xFF, 0x02, 0xFF, 0x04, 0x03, 0xFF, 0xFF, 0x02, 0xFF, 0x05, 0x04, 0xFF, 0xFF, 0x02, 0x02, 0xFF, 0xFF, 0x04, 0xFF, 0x04, 0xFF, 0x03, 0x05, 0x06, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0x01, 0x04, 0xFF, 0xFF, 0x07, 0x05, 0xFF, 0xFF, 0x03, 0x03, 0x02, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0x05, 0x07, 0xFF, 0x03, 0x02, 0x04, 0x04, 0xFF, 0x07, 0x05, 0x04, 0x03, 0xFF, 0xFF, 0x04, 0xFF, 0x02, 0x04, 0x05, 0xFF, 0xFF, 0x06, 0x05, 0x04, 0xFF, 0x02, 0xFF, 0xFF, 0x07, 0x04, 0xFF, 0xFF, 0x03, 0xFF, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x03, 0x02, 0x02, 0xFF, 0xFF, 0x02, 0x04, 0x03, 0x05, 0xFF, 0xFF, 0x05, 0xFF, 0x04, 0xFF, 0x06, 0xFF, 0xFF, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0xFF, 0x06, 0x06, 0xFF, 0x07, 0x06, 0x04, 0xFF, 0x04, 0x03, 0xFF, 0x04, 0x03, 0x05, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x06, 0x07, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0x05, 0xFF, 0x05, 0xFF, 0xFF, 0x06, 0x07, 0x07, 0xFF, 0x05, 0x06, 0x06, 0xFF, 0xFF, 0x02, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0xFF, 0xFF, 0x07, 0x07, 0x06, 0xFF, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0x03, 0x05, 0xFF, 0x07, 0xFF, 0x05, 0xFF, 0x06, 0xFF, 0x05, 0xFF, 0xFF, 0x07, 0x08, 0xFF, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0x05, 0x03, 0xFF, 0x04, 0x05, 0x05, 0x03, 0xFF, 0xFF, 0x06, 0x05, 0x05, 0x06, 0xFF, 0x06, 0x05, 0x02, 0x04, 0x03, 0x04, 0xFF, 0xFF, 0x03, 0x04, 0x04, 0x06, 0x05, 0xFF, 0x03, 0xFF, 0x05, 0x05, 0x05, 0xFF, 0xFF, 0x05, 0xFF, 0xFF, 0x04, 0xFF, 0xFF, 0x04, 0xFF, 0x07, 0x07, 0x08, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0xFF, 0xFF, 0xFF, 0x04, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x03]

# 最终结果的字符映射表
CHAR_MAP = {
'0': 'a', '1': 'b', '2': 'c', '3': 'd', '4': 'e', '5': 'f',
'6': '0', '7': '1', '8': '2', '9': '3', 'a': '4', 'b': '5',
'c': '6', 'd': '7', 'e': '8', 'f': '9'
}

#检查指定位置是否有雷1表示有雷,0表示无雷
def check_mine(mines, x, y):
if not (0 <= x <= 19 and 0 <= y <= 19):
return 0
idx = (20 * x + y) // 8
shift = (20 * x + y) & 7
return ((mines[idx] & 0xff) >> shift) & 1

#统计指定位置周围3*3中的地雷数量
def count_mine(mines, x, y):
return sum(
check_mine(mines, x + dx, y + dy)
for dx in range(-1, 2)
for dy in range(-1, 2)
)

def solve_minesweeper():
solver = Solver()
# 创建表示地雷分布的位向量
mine_field = [BitVec(f"v{i}", 8) for i in range(50)]

# 添加约束条件:每个已知数字格子周围的地雷数必须匹配
for i in range(20):
for j in range(20):
val = MINESWQEEPER_MAP[i * 20 + j]
if val != 0xFF: # 如果是已知数字的格子
adjacent_mines = count_mine(mine_field, i, j)
solver.add(val == adjacent_mines)

# 求解约束系统
if solver.check() != sat:
return "No solution found"

# 获取解并转换为十六进制字符串
model = solver.model()
hex_result = ''.join(
hex(model[v].as_long())[2:].rjust(2, "0")
for v in mine_field
)

# 使用字符映射表转换结果
res = ''.join(CHAR_MAP[c] for c in hex_result)

# 返回MD5哈希值
return hashlib.md5(res.encode()).hexdigest()

if __name__ == "__main__":
flag = 'SUCTF{'+solve_minesweeper()+'}'
print(flag)
#SUCTF{d661b98e4241de7423ef2d953098329d}

SU_Harmony

鸿蒙安装包的逆向

文件结构如下

modules.abclibs目录下的 so 文件应该比较重要,其他的应该是静态文件

这个.abc 应该对应的是安卓的 .dex

用基于 jadx 开发的 abc-decompiler 打开

EntryAbilityEntryBackupAbility里没看到什么验证代码或者加密,看看pages目录下的Index

可以快速定位到一个输入的验证函数,可以看到是调用了libentry.socheck函数

那我们用 IDA 打开 libentry.so看看

搜索字符串定位check函数,是sub_3750

又很多混淆,但是很多都是重复的,可以忽略,获取一些关键信息

长度为32

这里将输入经过sub_57B0加密后和密文进行比较

sub_57B0调用了几个函数进行加密,这几个函数也都有混淆,其实分析完会发现就是个大数运算

我们一个个函数分析,只看关键部分就行

sub_62F0大概就是将输入都转成字符的数字

sub_6D20应该就是个乘法,根据sub_57B0中的调用,两个参数是一样的,所以这一步是平方

sub_8270将两个参数逐位相乘,存入第三个参数中,根据sub_57B0中的调用,就是乘以2

sub_9890应该就是个加法,根据sub_57B0中的调用,是将平方的结果和乘以2的结果相加

sub_A8F0,应该是将两个参数相减,根据调用,就是减3

sub_C160是个除法,根据调用就是除以2

所以总结一下,就是计算方程(x^2+x*2-3)/2=enc(x+1)^2=2*enc+4

1
2
3
4
5
6
7
8
9
10
11
12
13
import math
from Crypto.Util.number import long_to_bytes

def decrypt(enc):
return int(-1 + math.sqrt(enc * 2 + 4))

enc = [999272289930604998, 1332475531266467542, 1074388003071116830, 1419324015697459326, 978270870200633520, 369789474534896558, 344214162681978048, 2213954953857181622]
flag = ''.join(
long_to_bytes(decrypt(value))[::-1].decode()
for value in enc
)
print(flag)
#SUCTF{Ma7h_WorldIs_S0_B3aut1ful}

SU_mapmap2

字符串定位到关键函数,简单理解一下,要求输入长度268

根据限制条件,限制了字符只能是aswd,同时从内存0x63F5300x63F538中定义了起始和终止状态

向四个方向移动,如果合法更新状态,否则回到初始状态

本来想动调看能不能导出地图,手动走的,调半天没调出来。可以看看出题人的方法

函数sub_4631C2和函数sub_4632EE里有一堆的嵌套调用,比较复杂,动调应该可以找到并模拟出来逻辑

可以尝试另一种方式,用idapython的continue_process()wait_for_next_event(WFNE_SUSP, -1)来实现自动调试。

  • continue_process()IDA API中的函数,用于继续执行被调试的程序,相当于调试器中按F9继续运行的操作程序会一直运行,直到遇到下一个断点或异常
  • wait_for_next_event(WFNE_SUSP, -1)用于等待下一个调试事件发生 ,WFNE_SUSP 是一个标志,表示等待程序暂停事件 ,-1 参数表示无限等待,直到事件发生

我们在for循环中下断点,获取以下三个地址的内容,用于每次设置状态

1
2
3
input_addr = rbp - 0x40
i_addr = rbp - 0x48
status_addr = rbp - 0x50

DFS搜索即可

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from typing import Dict
from idaapi import *

class PathFinder:
def __init__(self):
# 初始状态和目标状态
self.origin_status = get_qword(0x63F530)
self.final_status = get_qword(0x63F538)

# 获取寄存器和内存地址
rbp = get_reg_val('rbp')
self.input_addr = rbp - 0x40
self.i_addr = rbp - 0x48
self.status_addr = rbp - 0x50
self.path = get_qword(self.input_addr)

# 记录已访问状态和最终路径
self.visited: Dict[int, bool] = {}
self.path_found = ''

def _try_move(self, current_status, direction):
# 重置计数器和状态
patch_qword(self.status_addr, current_status)
patch_byte(self.path, ord(direction))
patch_qword(self.i_addr, 0)

# 运行程序获取新状态
continue_process()
wait_for_next_event(WFNE_SUSP, -1)
new_status = get_qword(self.status_addr)

print(f'方向:{direction} 新状态:{hex(new_status)}')
return new_status

def _DFS(self, current_status):
print(f"搜索状态: {hex(current_status)}")
if current_status == self.final_status:
return True
if current_status in self.visited:
return False
self.visited[current_status] = True

# 尝试所有可能的移动方向
for direction in ['w', 'd', 's', 'a']:
new_status = self._try_move(current_status, direction)
if (new_status != self.origin_status and
new_status != current_status and
self._DFS(new_status)):
self.path_found += direction
return True
return False

def find_path(self):
print(f'初始状态:{hex(self.origin_status)}, 目标状态:{hex(self.final_status)}')
if self._DFS(self.origin_status):
path = self.path_found[::-1] # 反转路径
print(f"找到路径: {path}")
return path
else:
print("未找到路径")
return None

finder = PathFinder()
finder.find_path()

MD5