夫天地者,万物之逆旅;光阴者,百代之过客。

0%

逆向题解(DAS-SECURITY CTF四月春季战)(20200425安恒CTF月赛)

总体难度不是很大,第三道题我赛后搞了六七个小时才做出,不是因为难,只是因为我对c语言的指针很不熟悉,影响了对验证逻辑的理解。

RE1 签到

1587878803272

v6[i-112]v5[i]

1
2
3
4
s = list('akhb~chdaZrdaZudqduvdZvvv|')
for i in range(len(s)):
s[i] = chr((ord(s[i])-1) ^ 6)
print(''.join(s))

encrypt3

1587879014441

1587879028636

首先输入一个即将输入字符串的长度n

然后输入字符串,记为input

然后程序使用enc_flag(网上查了查,意思是encrypt flag)对input加密(说解密也行叭~)

将结果输出,记为output.

分析关键代码:

v10=input[0] ^ input[1] ^ input[2] ^ ... ^ input[n-1]

output[i] = v10 ^ enc_flag[i]

enc_flag[i]已知。

可以看出,v10与input有关,但不管input的内容是什么,v10只是个char。所以可以爆破出所有的output。

程序中没有提示flag是input还是output,但是我们求出output后就已经发现了flag了。

我觉得input没法求,毕竟只需要逐位异或满足v10是某一特定值(爆破求出是64)即可,input的可能的字符串取值空间极大。

1
2
3
4
5
6
7
8
9
10
11
enc_flag = [38,44,33,39,59,35,34,115,117,114,113,33,36,117,118,119,35,120,38,114,117,113,38,34,113,114,117,114,36,112,115,118,121,112,35,37,121,61]
for i in range(0, 0x7f):
output = ''
for j in range(len(enc_flag)):
if not(32 <= (enc_flag[j] ^ i) <= 127):
break
else:
output += chr(enc_flag[j] ^ i)
if 'flag' in output:
print('v10=%d' % i)
print(output)

RE3 (忘了本题名称了)

输入四个int,经过sub_401863()进行变换,得到4个int,并和v9~v12比较数值,相等则输入的4个int的十六进制形式就是flag.

1587880072350

sub_401863() 函数:

第一部分:求出dword_408A40[]

1587880207049

虽然参数里有input,但是函数内部没用到input。用到了main中的v15~v18.

值是固定的,所以动态调试拿到,一共32个int.

之后进入一个32次的循环,循环内部有第2,3,4,5部分。

第二部分:求出v12

1587880490649

1587880769689

函数内部是一个4次循环,每次循环得到一个byte,合起来即一个INT。

对于每个byte:

v12[j] = input[i+2][j]^input[i+1][j]^input[i+3][j]^dword_408A40[i][j],

所以v12在形式上为:v12[0]v12[1]v12[2]v12[3]

按字节异或和按int异或其实是等同的,所以简化为:

v12 = input[i+2]^input[i+1]^input[i+3]^dword_408A40[i]

第三部分:求出v11

1587880746304

1587880788680

得到v11,4个_BYTE,即INT

v11[j] = byte_404100[16*(__int8(v12[j]>>4)) + (v12[j]&0xF)]

可以归纳为v11[j] = byte_404100[v12[j]]

byte_404100为已知的长度为0xff的数组,可以使用idc脚本导出二进制流文件。

导出后(记文件名称为dump),可以使用下面的代码提取成数组:

1
2
3
4
5
6
7
8
9
10
11
import binascii

with open('dump','rb')as f:
data = f.read()
num = []
for i in range(len(data)):
d = data[i : i+1][::-1] #b'\x00\x00C\xdf'
d = str(binascii.hexlify(d))[2:-1] #000043df
d = int(d,16) #17375
num.append(d)
print(num)

代码没优化,其实不需要这么多行,先这样放在这里吧。

第四部分:求出v10

1587880914872

__ROL4__是uint32循环左移,__ROL2__是uint16循环左移,__ROL1__是uint8循环左移

__ROR4__是uint32循环右移,__ROR2__是uint16循环右移,__ROR1__是uint8循环右移

这些名称都是ida的定义,可以在ida根目录\plugins\defs.h中详细查看:

1587881337330

(我tm一开始没注意v10的计算同时包含左移和右移,我眼瞎,以为都是__ROL4__

第五部分:求a[i+4]

1587881415207

1587881426743

a[i+4][j] = a[i][j] ^ v10[j]

求四个byte,合起来一个int.

按字节异或和按int异或其实是等同的,所以简化为:

a[i+4] = a[i] ^ v10

归纳

程序输入4个int,然后通过32次循环,每次循环求出一个input[i+4]

这里的input不完全代表输入,input[0]到input[3]是输入,但是此后的input[4]到input[35]都是求出来的。

验证input[32],input[33],input[34],input[35]是否和main中的v9~v12相等。

为了不影响理解,我把input[]这个数组起个别名:数组a[]

我们把代数式整合一下:

1
2
3
4
5
6
7
v12 = a[i+1] ^ a[i+2] ^ a[i+3] ^ dword_408A40[i]

v11[j] = byte_404100[v12[j]]

v10 = __ROR4__(v11, 14) ^ __ROL4__(v11, 10) ^ v11 ^ __ROL4__(v11, 2) ^ __ROR4__(v11, 8)

a[i+4] = a[i] ^ v10

对于上面的算式,我们已知的有dword_408A40[],byte_404100[]a[32]~a[35]

我们需要求出的是a[0]~a[3]

所以先求v12,再求v11,再求v10,最后求a[i],直到倒着求出a[0]~a[3]

脚本

脚本没整理,有些臃肿,大家将就看吧。

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
'''
循环左移和循环右移
from https://blog.csdn.net/C_chuxin/article/details/83691674
我嫌弃这两函数不够简洁,自己实现了这个需求,所以此段代码作废。
def circular_shift_left(int_value,k,bit = 8):
bit_string = '{:0%db}' % bit
bin_value = bit_string.format(int_value) # 8 bit binary
bin_value = bin_value[k:] + bin_value[:k]
int_value = int(bin_value,2)
return int_value

def circular_shift_right (int_value,k,bit = 8):
bit_string = '{:0%db}' % bit
bin_value = bit_string.format(int_value) # 8 bit binary
bin_value = bin_value[-k:] + bin_value[:-k]
int_value = int(bin_value,2)
return int_value
'''

def __ROL4__(value, k):
k %= 32
return (((value << k)&0xffffffff) | ((value >> (32-k))&0xffffffff))&0xffffffff

def __ROR4__(value, k):
k %= 32
return (((value >> k)&0xffffffff) | ((value << (32-k))&0xffffffff))&0xffffffff

byte_404100 = [214, 144, 233, 254, 204, 225, 61, 183, 22, 182, 20, 194, 40, 251, 44, 5, 43, 103, 154, 118, 42, 190, 4, 195, 170, 68, 19, 38, 73, 134, 6, 153, 156, 66, 80, 244, 145, 239, 152, 122, 51, 84, 11, 67, 237, 207, 172, 98, 228, 179, 28, 169, 201, 8, 232, 149, 128, 223, 148, 250, 117, 143, 63, 166, 71, 7, 167, 252, 243, 115, 23, 186, 131, 89, 60, 25, 230, 133, 79, 168, 104, 107, 129, 178, 113, 100, 218, 139, 248, 235, 15, 75, 112, 86, 157, 53, 30, 36, 14, 94, 99, 88, 209, 162, 37, 34, 124, 59, 1, 33, 120, 135, 212, 0, 70, 87, 159, 211, 39, 82, 76, 54, 2, 231, 160, 196, 200, 158, 234, 191, 138, 210, 64, 199, 56, 181, 163, 247, 242, 206, 249, 97, 21, 161, 224, 174, 93, 164, 155, 52, 26, 85, 173, 147, 50, 48, 245, 140, 177, 227, 29, 246, 226, 46, 130, 102, 202, 96, 192, 41, 35, 171, 13, 83, 78, 111, 213, 219, 55, 69, 222, 253, 142, 47, 3, 255, 106, 114, 109, 108, 91, 81, 141, 27, 175, 146, 187, 221, 188, 127, 17, 217, 92, 65, 31, 16, 90, 216, 10, 193, 49, 136, 165, 205, 123, 189, 45, 116, 208, 18, 184, 229, 180, 176, 137, 105, 151, 74, 12, 150, 119, 126, 101, 185, 241, 9, 197, 110, 198, 132, 24, 240, 125, 236, 58, 220, 77, 32, 121, 238, 95, 62, 215, 203, 57, 72]

dword_408A40 = [0xF9,0x86,0x21,0xF1,0x61,0x2B,0x66,0x41,0x9A,0xB1,0x6A,0x5A,0x77,0x20,0xA9,0x7B,0xF4,0x60,0x73,0x36,0x61,0x0C,0x6A,0x77,0xB3,0x89,0xBB,0xB6,0x51,0x31,0x76,0x24,0x7C,0x30,0x20,0xA5,0xBD,0x4D,0x58,0xB7,0xED,0x53,0x07,0xC3,0x57,0x5B,0xE5,0x7E,0x8C,0x60,0x88,0x69,0xB7,0x95,0xD8,0x30,0xAF,0x14,0xBA,0x44,0xA1,0x95,0x44,0x10,0x28,0xB4,0x20,0xD1,0xA3,0x5F,0xB5,0x73,0x66,0x49,0x87,0xCC,0x39,0x44,0x24,0x92,0x1F,0x64,0x9E,0xE8,0x5A,0x01,0xCA,0x98,0x60,0x90,0x15,0xC7,0x2E,0xFD,0xE1,0x99,0x0C,0xD8,0x9B,0xB7,0xB0,0x15,0x21,0x1D,0xEB,0x8A,0x22,0x0E,0x81,0x0C,0x78,0xF1,0x54,0x36,0x8D,0x42,0x96,0x34,0x29,0x62,0xE5,0x72,0xCF,0x01,0x12,0xA0,0x24,0x91]
dword_408A40_2 = [0] * 32
for i in range(32):
#四个byte合并成一个int
#dword_408A40_2[i] = hex(dword_408A40[i*4+3] * 2**24 + dword_408A40[i*4+2] * 2**16 + dword_408A40[i*4+1] * 2**8 + dword_408A40[i*4])
dword_408A40_2[i] = dword_408A40[i*4+3] * 2**24 + dword_408A40[i*4+2] * 2**16 + dword_408A40[i*4+1] * 2**8 + dword_408A40[i*4]
dword_408A40 = dword_408A40_2
#print(dword_408A40)

global a
a = [0] * 36
a[35] = 3229185894
a[34] = 2011540633
a[33] = 835020779
a[32] = 1191593383


def get_v12(i):
return a[i+1] ^ a[i+2] ^ a[i+3] ^ dword_408A40[i]

def get_v11(v12):
#print(hex(v12))
v12 = hex(v12)[2:]
if len(v12)<8:
v12 = (8-len(v12))*'0' + v12
v11 = ''
for j in range(4):
v12_j = int('0x' + v12[j*2:j*2+2], 16)
v11_j = hex(byte_404100[v12_j])[2:]
if len(v11_j)<2:
v11_j = (2-len(v11_j))*'0' + v11_j
#print(byte_404100[v12_j], ' ', v12_j, ' ', v11_j)
v11 += v11_j
#v11 = '0x' + v11[6:8] + v11[4:6] + v11[2:4] + v11[0:2]
#print(v11)
#print()
return int(v11, 16)

def get_v10(v11):
#return circular_shift_right(v11, 14) ^ circular_shift_left(v11, 10) ^ v11 ^ circular_shift_left(v11, 2) ^ circular_shift_right(v11, 8)
return __ROR4__(v11, 14) ^ __ROL4__(v11, 10) ^ v11 ^ __ROL4__(v11, 2) ^ __ROR4__(v11, 8)

def get_a_i(i):
return a[i+4] ^ get_v10(get_v11(get_v12(i)))

def solve():
import ctypes
for i in range(31, -1, -1):
a[i] = get_a_i(i)
for i in range(4):
#a[i] = ctypes.c_int(a[i]).value
print(a[i])
print('或')
for i in range(4):
a[i] = ctypes.c_int(a[i]).value
print(a[i])
print('一个是int,一个是unsigned int,反正内存中存储的十六进制是相同的')

solve()

结果有两组,分别是int和uint(内存中存储的十六进制是相同的)

img