纯纯摸鱼,然后靠队友带飞,都太强了😭😭😭一定好好当端茶倒水小弟

S3qUenCEs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
You are given an array a consisting n(1=<n<=10^6) integers a1,a2,...,an(-10^9<=ai<=10^9 for each 1<=i<=n) and an integer k(1<=k<=n).
You can do any number of operations.In each option you can choose an interval [l,r] with a length of k and multiply the number al,al+1,...
ar in array a by -1.You have to output the maximum value of the sum of the array a after any number of options.

You need to answer 100 independent questions.Each question has a time limit of 5 seconds.

Each question just like this:

Input:
5 3
3 4 -3 7 -6

Output:
17

算法思路:

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
import pwn
from pwnlib.tubes.remote import remote

maxx = 1000000001
n = 0
m = 0

def exp(n, m, num):
mnn = [maxx for i in range(m)]
sta = [0 for i in range(m)]
tot = 0

for i in range(n):
if num[i] > 0:
mnn[i % m] = min(mnn[i % m], num[i])
tot += num[i]
else:
mnn[i % m] = min(mnn[i % m], -num[i])
sta[i % m] ^= 1
tot += -num[i]
tot0 = 0
tot1 = 0

for i in range(m):
if sta[i]:
tot1 += mnn[i]
else:
tot0 += mnn[i]

return (tot - min(tot0, tot1) * 2)

r = remote("172.51.9.71", 9999)
# while True:
# r.interactive()

for i in range(100):
s = r.recvuntil(b'Challenge Input:\n')
n, m = r.recvline().strip().split(b' ')
n = int(n)
m = int(m)

list = r.recvline().strip()
list = list.split(b' ')
num = []
for x in list:
num.append(int(x))

# print(n, m)
# print(num)

result = exp(n, m, num)
print(i, result)
res = r.sendline(str(result).encode())

r.interactive()

comeongo

本来想着这题能不能抢上血的,结果做到下午四点,急死了

1667955353406

1667955396002

1667955460124

1667955604297

golang逆向,需要获得name和pass

动调到此处可以看出name和pass的长度都是16,check有两个,都要满足。

check1:

找到一串密文,再动调进入encoding函数发现

是base58的表,于是解密密文,解出来是GoM0bi13G3tItEzF,经过测试输入

发现check1检查的是name和pass的前八位,拆开即可。

check2:
在第一个memcmp处发现一串密文

动调进encoding函数发现是base64,解密得到_NubcfFq
再测试输入发现是对中间的9-12位进行操作的,且有9,12,-12的偏移,解出来是_BinorRe,那么还剩最后四位

动调到byte_compare,前面有个地方有个rax,和name和pass的最后四位有关系,密文就是那个unk,然后再点进去runtime_other发现最后两位的差分别是63和31,于是很容易得到,name的倒数34位是一个简单偏移,解出来是gG,name和pass的最后四位可以设方程
2x + 63 + 2 = 161
2x + 31 + 3 = 100
求出name和pass的最后两位就是o@0!


最后找到pass的倒数34位vG

于是最终的flag就是MD5(flag{GoM0bi13_BingGo@G3tItEzForRevG0!})

这个题的附件pass的倒数3和4位你无论输入什么都能通过题目的flag检测,但是真flag需要md5校验以后才是对的…做到还剩最后两位的时候真就根据自然语言跟出题人对脑电波当misc做了

unlmbda

先放三篇博客:

python中的lambda函数用法 lambda演算 Y分钟入门lambda演算 SKI组合子演算

其中重点关注一下 lambda演算 => SKI演算 => SK演算 => Iota演算的过程,本题需要做的就是从 Iota 算子开始的这样一个逆过程。

前面的代码逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
arr = []
l = lambda _ : (_ (lambda _ : lambda __: lambda ___ : (_ (___)) (__ (___))))(lambda _ : lambda __: _)
I = lambda _ : arr[_(lambda _ : _ + 1)(0)]

T = lambda x: lambda y: x
F = lambda x: lambda y: y

flag = input('your flag please: ')
buffer = flag[0 : 8]
arr.clear()
for c in buffer:
bits = ord(c)
for j in range(8):
if bits & 1 == 1:
arr.append(T)
else:
arr.append(F)
bits >>= 1

先看对 flag 的处理,发现是每 8 位做一次 check,输进去以后还要把字符转化为 8 位二进制数然后放进 arr 数组里。

观察到有两个 python 的 lambda 函数,同时 checker 里面也可以发现有 I 存在,猜测可以通过上面的那个 lambda 函数把 Iota 算子给转化成常数,也就是 arr 的下标。

1
2
l = lambda _ : (_ (lambda _ : lambda __: lambda ___ : (_ (___)) (__ (___))))(lambda _ : lambda __: _)
I = lambda _ : arr[_(lambda _ : _ + 1)(0)]

同时根据 SKI 和 Iota 的对应规则

1667956710863

我们可以对Iota组合子进行正则匹配,转化为 SKI 组合子。

就拿第一个 checker_0来举例子吧(谔谔,有点大,大概长这样)

1667958379705

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
l = lambda _ : (_ (lambda _ : lambda __: lambda ___ : (_ (___)) (__ (___))))(lambda _ : lambda __: _)
toID = lambda x : x(lambda y : y + 1)(0)

stack = []
bj = False
ind = 0
getID = []

# 括号匹配
for i in range(len(checker)):
c = checker[i]
if c == 'I':
bj = True
if bj:
if c == '(':
stack.append(i)
elif c == ')':
value = stack.pop()
if len(stack) == 0:
bj = False
getID.append(checker[value + 1: i])

# 这里通过题目给定的lambda表达式自动会把提取出来的Iota算子给转化成常数,真几把神奇
for ind in getID:
index = toID(eval(ind))
checker = checker.replace('I(' + ind + ')', 'arr[' + str(index) + ']')

# 最后再把 Iota 算子替换成 SKI 算子
checker = checker.replace('l(l(l(l(l))))', 'S')
checker = checker.replace('l(l(l(l)))', 'K')
checker = checker.replace('l(l)', 'I')

print(checker)

转化完之后瞬间少了很多

1
2

S(S(K(S(K(S))(K)))(S))(K(K))(arr[4])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[0])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[42])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[28])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[20])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[39])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[56])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[30])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[27])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[2])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[52])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[62])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[48])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[58])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[60])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[6])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[23])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[19])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[12])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[43])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[57])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[44])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[53])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[55])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[51])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[25])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[26])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[29])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[17])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[38])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[40])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[32])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[37])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[18])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[11])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[3])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[50])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[21])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[8])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[33])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[16])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[47])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[22])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[9])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[59])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[41])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[5])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[34])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[10])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[61])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[45])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[15])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[1])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[7])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[35])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[54])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[24])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[46])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[36])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[14])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[31])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[13])(K(I))(S(S(K(S(K(S))(K)))(S))(K(K))(arr[49])(S(S(K(S(K(S))(K)))(S))(K(K))(arr[63])(K)(K(I)))(K(I))))(K(I)))))))))(K(I))))(K(I)))(K(I)))))(K(I))))))(K(I))))(K(I)))))(K(I)))))(K(I))))(K(I)))))(K(I))))(K(I))))))(K(I)))(K(I))))(K(I)))(K(I))))(K(I)))(K(I)))(K(I))))(K(I))))))(K(I))))(K(I))))(K(I)))(K(I)))(K(I)))(K(I)))(K(I)))(K(I)))(K(I))

通过观察发现有很多重复的运算,比如K(I),S(S(K(S(K(S))(K)))(S))

1667958891599

1667958854654

通过查询可知1667958920830

原来 K 就是 TrueK(I) 就是 FalseS(S(K(S(K(S))(K)))(S))(K(K))则对应一种 flip 操作,即# FLIP b e t = if b then t else e。再写正则匹配替换

1
2
3
checker = checker.replace('K(I)', 'F')
checker = checker.replace('S(S(K(S(K(S))(K)))(S))(K(K))', 'IF')
checker = checker.replace('K', 'T')
1
IF(arr[4])(IF(arr[0])(IF(arr[42])(IF(arr[28])(IF(arr[20])(IF(arr[39])(IF(arr[56])(IF(arr[30])(F)(IF(arr[27])(IF(arr[2])(F)(IF(arr[52])(IF(arr[62])(F)(IF(arr[48])(F)(IF(arr[58])(F)(IF(arr[60])(IF(arr[6])(F)(IF(arr[23])(IF(arr[19])(IF(arr[12])(IF(arr[43])(F)(IF(arr[57])(IF(arr[44])(IF(arr[53])(F)(IF(arr[55])(IF(arr[51])(IF(arr[25])(F)(IF(arr[26])(F)(IF(arr[29])(F)(IF(arr[17])(IF(arr[38])(F)(IF(arr[40])(IF(arr[32])(F)(IF(arr[37])(F)(IF(arr[18])(IF(arr[11])(F)(IF(arr[3])(IF(arr[50])(F)(IF(arr[21])(F)(IF(arr[8])(IF(arr[33])(F)(IF(arr[16])(F)(IF(arr[47])(IF(arr[22])(F)(IF(arr[9])(IF(arr[59])(F)(IF(arr[41])(F)(IF(arr[5])(F)(IF(arr[34])(IF(arr[10])(F)(IF(arr[61])(F)(IF(arr[45])(IF(arr[15])(IF(arr[1])(F)(IF(arr[7])(IF(arr[35])(F)(IF(arr[54])(F)(IF(arr[24])(F)(IF(arr[46])(F)(IF(arr[36])(F)(IF(arr[14])(F)(IF(arr[31])(IF(arr[13])(F)(IF(arr[49])(IF(arr[63])(T)(F))(F)))(F))))))))(F)))(F))(F))))(F)))))(F)))(F))))(F))))(F)))(F))))(F)))(F)))))(F))(F)))(F))(F)))(F))(F))(F)))(F)))))(F)))(F)))(F))(F))(F))(F))(F))(F))(F)

由于是 if b then t else e,我们发现化简完以后只有一个 T: IF(arr[63])(T)(F)也就是说只有 arr[63]F时返回 T,然后就是纯纯体力活,把整个表达式从里到外,把每一位都按照arr[id](F/T)(T/F)的形式,找出每一位是 0 还是 1。

处理完以后得到flag{JelL0_wy_De@r_l@mBdA_ExprESzl0n_wITh_iOt@!}

windows_call

动调到输入的地方,前面的一大堆是md5,可不用管

1668098033972

flag长度为46,格式为flag{hex},hex长度为40且全部为数字和大写字母。

再往下看

1668098726995

还是一些对md5的操作,这里放一下 md5的源码 ,发现能找到很明显的常量特征

1668098791033

那么这一段代码也不用看了,接着往下看

1668098866579

发现flag中包裹的hex原来有40位,我们知道一般AES的题明文一般都是16的整数倍,那么多出来的这8位干啥去了呢?

我们发现前8位被转成了4位16进制数被赋值给了key1和key2,且 iv 和 key 都是和这前八位有关系的,同时在check函数中找到一长串条件

1668099743620

显然v42就是加密后的密文,长度为32位,后面的条件是对前8位的限制,我们可以用z3脚本解出

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
from z3 import *

iv = [0 for i in range(16)]
key = [0 for i in range(16)]
a = BitVec('a', 32) # 这里注意是int32
b = BitVec('b', 32)
tmp1 = a ^ b
tmp2 = tmp1 & 0xFF
tmp3 = tmp1 >> 8
for i in range(16):
iv[i] = (tmp3 + i) ^ (tmp2 +i)

v28 = 0
for i in range(16):
v29 = (iv[i] ^ (i - 64)) & 0xFF
key[i] = v29
v28 += v29
solv = Solver()
solv.add(((a + 0x3800) & 0xFFFF) <= 0x800)
solv.add(((b + 0x3500) & 0xFFFF) <= 0x500)
solv.add(((b - a) & 0xFFFF) == 0x2B8)
solv.add(v28 == 0x8A8)
solv.add((b & 0xFF) == 0xA0)
solv.add((a & 0xFF00) < 0xCA00)

if solv.check() == sat:
m = solv.model()
print(m)
else:
print("OOps!")
a = 51688
b = 52384

print(hex(a), hex(b))
#0xc9e8 0xcca0

注意一下大小端序,前八位就是E8C9A0CC

然后重写一下 py 脚本获得 key 和 iv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
iv = [0 for i in range(16)]
key = [0 for i in range(16)]
a = 51688
b = 52384
tmp1 = a ^ b
tmp2 = tmp1 & 0xFF
tmp3 = tmp1 >> 8
for i in range(16):
iv[i] = (tmp3 + i) ^ (tmp2 +i)
v28 = 0
for i in range(16):
v29 = (iv[i] ^ (i - 64)) & 0xFF
key[i] = v29
v28 += v29
for i in iv:
print(chr(i), end = "")
print()
for i in key:
print(hex(i), end = "")

直接 cyberchef 就可以了

1668100691013

最后注意全部转成大写flag{E8C9A0CC8B9854CDD0AC321B790FC74EFA520FBC}

babyre

TLScallback回调函数用来反调试+修改S_box,比赛的时候确实没想到,当然估计就算想到了也做不出来这题。😥

先插一个小技巧:ida pro 的 debugger option 中,把这个选项勾上,能够断在进程开始处,就是从 ntdll 开始调试。

img

TLS回调函数里面有个反调试,但是注意不要把异常 9/0给绕过了,通过这个异常触发 VEH 进入handler 函数修改S_box

VEH相关可以看 这篇博客

1668517662335

但是注意触发条件的时候ida可能会出现警告,一定要选Yes,pass to app,不要像我一样傻逼

1668517791693

得到新的S_box

1
2
3

unsigned char S_box[] = {0x77, 0x68, 0x63, 0x6f, 0xe6, 0x7f, 0x7b, 0xd1, 0x24, 0x15, 0x73, 0x3f, 0xea, 0xc3, 0xbf, 0x62, 0xde, 0x96, 0xdd, 0x69, 0xee, 0x4d, 0x53, 0xe4, 0xb9, 0xc0, 0xb6, 0xbb, 0x88, 0xb0, 0x66, 0xd4, 0xa3, 0xe9, 0x87, 0x32, 0x22, 0x2b, 0xe3, 0xd8, 0x20, 0xb1, 0xf1, 0xe5, 0x65, 0xcc, 0x25, 0x1, 0x10, 0xd3, 0x37, 0xd7, 0xc, 0x82, 0x11, 0x8e, 0x13, 0x6, 0x94, 0xf6, 0xff, 0x33, 0xa6, 0x61, 0x1d, 0x97, 0x38, 0xe, 0xf, 0x7a, 0x4e, 0xb4, 0x46, 0x2f, 0xc2, 0xa7, 0x3d, 0xf7, 0x3b, 0x90, 0x47, 0xc5, 0x14, 0xf9, 0x34, 0xe8, 0xa5, 0x4f, 0x7e, 0xdf, 0xaa, 0x2d, 0x5e, 0x58, 0x4c, 0xdb, 0xc4, 0xfb, 0xbe, 0xef, 0x57, 0x59, 0x27, 0x91, 0x51, 0xed, 0x16, 0x6b, 0x44, 0x28, 0x8b, 0xbc, 0x45, 0xb7, 0x54, 0x9b, 0x86, 0x89, 0x2c, 0xe1, 0xa8, 0xa2, 0xce, 0x35, 0x4, 0xeb, 0xe7, 0xc6, 0xd9, 0x18, 0x7, 0xf8, 0x4b, 0x83, 0x50, 0x3, 0xd0, 0xb3, 0x6a, 0x29, 0x70, 0x49, 0xd, 0x67, 0x74, 0x95, 0x5b, 0xc8, 0x36, 0x3e, 0x84, 0x9c, 0x52, 0xfa, 0xac, 0x0, 0xca, 0x4a, 0x1f, 0xcf, 0xf4, 0x26, 0x2e, 0x1e, 0x5d, 0x12, 0x30, 0x48, 0xd6, 0xc7, 0xb8, 0x76, 0x85, 0x81, 0xf0, 0x6d, 0xf3, 0xdc, 0x23, 0x79, 0x99, 0xc1, 0x5a, 0xbd, 0x78, 0x42, 0xe0, 0xfe, 0x71, 0x6e, 0xba, 0x1c, 0xae, 0x6c, 0x31, 0x3a, 0x8, 0xb2, 0xa0, 0xd2, 0xfc, 0xc9, 0x60, 0xb, 0x5f, 0xa9, 0x9f, 0x9e, 0x64, 0x2a, 0xa1, 0x72, 0x5c, 0x17, 0xe2, 0x1a, 0x75, 0x21, 0x43, 0xad, 0x92, 0xd5, 0x9, 0x8a, 0xf5, 0xec, 0x8c, 0x5, 0x7d, 0xcd, 0x9a, 0x80, 0x8f, 0xa, 0x93, 0xfd, 0xda, 0x41, 0x3c, 0xcb, 0x98, 0xb5, 0x9d, 0x19, 0xab, 0xf2, 0x56, 0x7c, 0x55, 0x8d, 0x39, 0x1b, 0xa4, 0x40, 0xaf, 0x2};

我们用这个S_box生成一个invS_box用于解密,生成过程可以参考 如何由AES中S盒推导出逆S盒

代码如下:

1
2
3
4
5
6
7
S_box = [0x77, 0x68, 0x63, 0x6f, 0xe6, 0x7f, 0x7b, 0xd1, 0x24, 0x15, 0x73, 0x3f, 0xea, 0xc3, 0xbf, 0x62, 0xde, 0x96, 0xdd, 0x69, 0xee, 0x4d, 0x53, 0xe4, 0xb9, 0xc0, 0xb6, 0xbb, 0x88, 0xb0, 0x66, 0xd4, 0xa3, 0xe9, 0x87, 0x32, 0x22, 0x2b, 0xe3, 0xd8, 0x20, 0xb1, 0xf1, 0xe5, 0x65, 0xcc, 0x25, 0x1, 0x10, 0xd3, 0x37, 0xd7, 0xc, 0x82, 0x11, 0x8e, 0x13, 0x6, 0x94, 0xf6, 0xff, 0x33, 0xa6, 0x61, 0x1d, 0x97, 0x38, 0xe, 0xf, 0x7a, 0x4e, 0xb4, 0x46, 0x2f, 0xc2, 0xa7, 0x3d, 0xf7, 0x3b, 0x90, 0x47, 0xc5, 0x14, 0xf9, 0x34, 0xe8, 0xa5, 0x4f, 0x7e, 0xdf, 0xaa, 0x2d, 0x5e, 0x58, 0x4c, 0xdb, 0xc4, 0xfb, 0xbe, 0xef, 0x57, 0x59, 0x27, 0x91, 0x51, 0xed, 0x16, 0x6b, 0x44, 0x28, 0x8b, 0xbc, 0x45, 0xb7, 0x54, 0x9b, 0x86, 0x89, 0x2c, 0xe1, 0xa8, 0xa2, 0xce, 0x35, 0x4, 0xeb, 0xe7, 0xc6, 0xd9, 0x18, 0x7, 0xf8, 0x4b, 0x83, 0x50, 0x3, 0xd0, 0xb3, 0x6a, 0x29, 0x70, 0x49, 0xd, 0x67, 0x74, 0x95, 0x5b, 0xc8, 0x36, 0x3e, 0x84, 0x9c, 0x52, 0xfa, 0xac, 0x0, 0xca, 0x4a, 0x1f, 0xcf, 0xf4, 0x26, 0x2e, 0x1e, 0x5d, 0x12, 0x30, 0x48, 0xd6, 0xc7, 0xb8, 0x76, 0x85, 0x81, 0xf0, 0x6d, 0xf3, 0xdc, 0x23, 0x79, 0x99, 0xc1, 0x5a, 0xbd, 0x78, 0x42, 0xe0, 0xfe, 0x71, 0x6e, 0xba, 0x1c, 0xae, 0x6c, 0x31, 0x3a, 0x8, 0xb2, 0xa0, 0xd2, 0xfc, 0xc9, 0x60, 0xb, 0x5f, 0xa9, 0x9f, 0x9e, 0x64, 0x2a, 0xa1, 0x72, 0x5c, 0x17, 0xe2, 0x1a, 0x75, 0x21, 0x43, 0xad, 0x92, 0xd5, 0x9, 0x8a, 0xf5, 0xec, 0x8c, 0x5, 0x7d, 0xcd, 0x9a, 0x80, 0x8f, 0xa, 0x93, 0xfd, 0xda, 0x41, 0x3c, 0xcb, 0x98, 0xb5, 0x9d, 0x19, 0xab, 0xf2, 0x56, 0x7c, 0x55, 0x8d, 0x39, 0x1b, 0xa4, 0x40, 0xaf, 0x2]
invS_box = [0] * 256
for i in range(256):
line = (S_box[i] & 0xf0) >> 4
rol = S_box[i] & 0xf
invS_box[(line * 16) + rol] = i
print(invS_box)

生成的逆S_box如下

1
2
3

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

下面开始分析主函数加密部分 ,发现是个 AES ,密钥已经知道了,输入32位字符串转成 16 个 2 位 hex,但是有魔改:

  • S_box改变
  • 把加密过程中的行列操作给调换了,这点非常坑,得面向黑盒动调很长时间才能搞明白,由于他是把行列操作对象给调换了,所以无论是加密还是解密之前,我们都需要把明文/密文给置换一下,(或者就都尝试一次看看哪个和动调结果能对的上)
  • 第二次加密对key进行了改变

通过动态调试,发现在第一次addRoundKey之前,函数对密文矩阵进行了一次转置,之后便是一模一样的AES加密算法。

1668514562720

可以发现第一次加密只是加密了前16位,对后16位没有进行操作。

1668514735189

我们可以通过本地的加密算法获得一样的结果,得到这个输出结果我应该用了一下午吧大概。

然后就是第二次AES加密,在第二次开始之前,先把密钥与上一轮加密得到的密文进行异或。

1668514970160

然后就是对第二段hex数据进行加密,和第一轮一样,把行列转置了一下

1668515014402

最后dump出密文解密即可

1
2
3
4
5
6
7
8
9
10
unsigned char enc[] =
{
0x33, 0x89, 0xCB, 0x14, 0x86,
0x53, 0xBB, 0x2B, 0x19, 0x42,
0x3D, 0x16, 0xE4, 0xDC, 0xC1,
0x5A, 0x85, 0x2C, 0x02, 0xB3,
0xAA, 0x07, 0x58, 0x0B, 0x2A,
0x32, 0x41, 0x34, 0x1A, 0xD5,
0x0D, 0xC1
};

完整exp

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#include <cstdio>
#include <iostream>
#include <stdint.h>
#include <memory.h>
using namespace std;
typedef enum
{
AES_CYPHER_128,
AES_CYPHER_192,
AES_CYPHER_256,
} AES_CYPHER_T;
/*
* Encryption Rounds
*/
int g_aes_key_bits[] =
{
/* AES_CYPHER_128 */
128,
/* AES_CYPHER_192 */
192,
/* AES_CYPHER_256 */
256,
};
int g_aes_rounds[] =
{
/* AES_CYPHER_128 */
10,
/* AES_CYPHER_192 */
12,
/* AES_CYPHER_256 */
14,
};
int g_aes_nk[] =
{
/* AES_CYPHER_128 */
4,
/* AES_CYPHER_192 */
6,
/* AES_CYPHER_256 */
8,
};
int g_aes_nb[] =
{
/* AES_CYPHER_128 */
4,
/* AES_CYPHER_192 */
4,
/* AES_CYPHER_256 */
4,
};

static const uint32_t g_aes_rcon[] =
{
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
0x40000000, 0x80000000,
0x1b000000, 0x36000000, 0x6c000000, 0xd8000000, 0xab000000, 0xed000000,
0x9a000000
};
static const uint8_t g_aes_sbox[256] = {0x77, 0x68, 0x63, 0x6f, 0xe6, 0x7f, 0x7b, 0xd1, 0x24, 0x15, 0x73, 0x3f, 0xea, 0xc3, 0xbf, 0x62, 0xde, 0x96, 0xdd, 0x69, 0xee, 0x4d, 0x53, 0xe4, 0xb9, 0xc0, 0xb6, 0xbb, 0x88, 0xb0, 0x66, 0xd4, 0xa3, 0xe9, 0x87, 0x32, 0x22, 0x2b, 0xe3, 0xd8, 0x20, 0xb1, 0xf1, 0xe5, 0x65, 0xcc, 0x25, 0x1, 0x10, 0xd3, 0x37, 0xd7, 0xc, 0x82, 0x11, 0x8e, 0x13, 0x6, 0x94, 0xf6, 0xff, 0x33, 0xa6, 0x61, 0x1d, 0x97, 0x38, 0xe, 0xf, 0x7a, 0x4e, 0xb4, 0x46, 0x2f, 0xc2, 0xa7, 0x3d, 0xf7, 0x3b, 0x90, 0x47, 0xc5, 0x14, 0xf9, 0x34, 0xe8, 0xa5, 0x4f, 0x7e, 0xdf, 0xaa, 0x2d, 0x5e, 0x58, 0x4c, 0xdb, 0xc4, 0xfb, 0xbe, 0xef, 0x57, 0x59, 0x27, 0x91, 0x51, 0xed, 0x16, 0x6b, 0x44, 0x28, 0x8b, 0xbc, 0x45, 0xb7, 0x54, 0x9b, 0x86, 0x89, 0x2c, 0xe1, 0xa8, 0xa2, 0xce, 0x35, 0x4, 0xeb, 0xe7, 0xc6, 0xd9, 0x18, 0x7, 0xf8, 0x4b, 0x83, 0x50, 0x3, 0xd0, 0xb3, 0x6a, 0x29, 0x70, 0x49, 0xd, 0x67, 0x74, 0x95, 0x5b, 0xc8, 0x36, 0x3e, 0x84, 0x9c, 0x52, 0xfa, 0xac, 0x0, 0xca, 0x4a, 0x1f, 0xcf, 0xf4, 0x26, 0x2e, 0x1e, 0x5d, 0x12, 0x30, 0x48, 0xd6, 0xc7, 0xb8, 0x76, 0x85, 0x81, 0xf0, 0x6d, 0xf3, 0xdc, 0x23, 0x79, 0x99, 0xc1, 0x5a, 0xbd, 0x78, 0x42, 0xe0, 0xfe, 0x71, 0x6e, 0xba, 0x1c, 0xae, 0x6c, 0x31, 0x3a, 0x8, 0xb2, 0xa0, 0xd2, 0xfc, 0xc9, 0x60, 0xb, 0x5f, 0xa9, 0x9f, 0x9e, 0x64, 0x2a, 0xa1, 0x72, 0x5c, 0x17, 0xe2, 0x1a, 0x75, 0x21, 0x43, 0xad, 0x92, 0xd5, 0x9, 0x8a, 0xf5, 0xec, 0x8c, 0x5, 0x7d, 0xcd, 0x9a, 0x80, 0x8f, 0xa, 0x93, 0xfd, 0xda, 0x41, 0x3c, 0xcb, 0x98, 0xb5, 0x9d, 0x19, 0xab, 0xf2, 0x56, 0x7c, 0x55, 0x8d, 0x39, 0x1b, 0xa4, 0x40, 0xaf, 0x2};
static const uint8_t g_inv_sbox[256] = {155, 47, 255, 135, 124, 227, 57, 130, 196, 222, 233, 203, 52, 142, 67, 68, 48, 54, 165, 56, 82, 9, 106, 213, 129, 243, 215, 251, 191, 64, 163, 158, 40, 217, 36, 178, 8, 46, 161, 102, 109, 139, 209, 37, 118, 91, 162, 73, 166, 194, 35, 61, 84, 123, 148, 50, 66, 250, 195, 78, 238, 76, 149, 11, 253, 237, 185, 218, 108, 112, 72, 80, 167, 141, 157, 132, 94, 21, 70, 87, 134, 104, 152, 22, 114, 248, 246, 100, 93, 101, 182, 146, 212, 164, 92, 204, 202, 63, 15, 2, 208, 44, 30, 143, 1, 19, 138, 107, 193, 175, 189, 3, 140, 188, 211, 10, 144, 216, 171, 0, 184, 179, 69, 6, 247, 228, 88, 5, 231, 173, 53, 133, 150, 172, 116, 34, 28, 117, 223, 110, 226, 249, 55, 232, 79, 103, 220, 234, 58, 145, 17, 65, 240, 180, 230, 115, 151, 242, 207, 206, 198, 210, 121, 32, 252, 86, 62, 75, 120, 205, 90, 244, 154, 219, 192, 254, 29, 41, 197, 137, 71, 241, 26, 113, 170, 24, 190, 27, 111, 183, 98, 14, 25, 181, 74, 13, 96, 81, 127, 169, 147, 201, 156, 239, 45, 229, 122, 159, 136, 7, 199, 49, 31, 221, 168, 51, 39, 128, 236, 95, 177, 18, 16, 89, 186, 119, 214, 38, 23, 43, 4, 126, 85, 33, 12, 125, 225, 105, 20, 99, 174, 42, 245, 176, 160, 224, 59, 77, 131, 83, 153, 97, 200, 235, 187, 60};

void aes_add_round_key(AES_CYPHER_T mode, uint8_t *state,uint8_t *round, int nr)
{
uint32_t *w = (uint32_t *)round;
uint32_t *s = (uint32_t *)state;
int i;
for (i = 0; i < g_aes_nb[mode]; i++)
{
s[i] ^= w[nr * g_aes_nb[mode] + i];
}
}
uint8_t aes_xtime(uint8_t x)
{
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
uint8_t aes_xtimes(uint8_t x, int ts)
{
while (ts-- > 0)
{
x = aes_xtime(x);
}
return x;
}
uint8_t aes_mul(uint8_t x, uint8_t y)
{
return ((((y >> 0) & 1) * aes_xtimes(x, 0)) ^
(((y >> 1) & 1) * aes_xtimes(x, 1)) ^
(((y >> 2) & 1) * aes_xtimes(x, 2)) ^
(((y >> 3) & 1) * aes_xtimes(x, 3)) ^
(((y >> 4) & 1) * aes_xtimes(x, 4)) ^
(((y >> 5) & 1) * aes_xtimes(x, 5)) ^
(((y >> 6) & 1) * aes_xtimes(x, 6)) ^
(((y >> 7) & 1) * aes_xtimes(x, 7)));
}
uint8_t aes_sub_sbox(uint8_t val)
{
return g_aes_sbox[val];
}
uint32_t aes_sub_dword(uint32_t val)
{
uint32_t tmp = 0;
tmp |= ((uint32_t)aes_sub_sbox((uint8_t)((val >> 0) & 0xFF))) << 0;
tmp |= ((uint32_t)aes_sub_sbox((uint8_t)((val >> 8) & 0xFF))) << 8;
tmp |= ((uint32_t)aes_sub_sbox((uint8_t)((val >> 16) & 0xFF))) << 16;
tmp |= ((uint32_t)aes_sub_sbox((uint8_t)((val >> 24) & 0xFF))) << 24;
return tmp;
}
uint32_t aes_rot_dword(uint32_t val)
{
uint32_t tmp = val;
return (val >> 8) | ((tmp & 0xFF) << 24);
}
uint32_t aes_swap_dword(uint32_t val)
{
return (((val & 0x000000FF) << 24) |
((val & 0x0000FF00) << 8) |
((val & 0x00FF0000) >> 8) |
((val & 0xFF000000) >> 24));
}

void inv_shift_rows(AES_CYPHER_T mode, uint8_t *state)
{
uint8_t *s = (uint8_t *)state;
int i, j, r;
for (i = 1; i < g_aes_nb[mode]; i++)
{
for (j = 0; j < g_aes_nb[mode] - i; j++)
{
uint8_t tmp = s[i];
for (r = 0; r < g_aes_nb[mode]; r++)
{
s[i + r * 4] = s[i + (r + 1) * 4];
}
s[i + (g_aes_nb[mode] - 1) * 4] = tmp;
}
}
}
uint8_t inv_sub_sbox(uint8_t val)
{
return g_inv_sbox[val];
}
void inv_sub_bytes(AES_CYPHER_T mode, uint8_t *state)
{
int i, j;
for (i = 0; i < g_aes_nb[mode]; i++)
{
for (j = 0; j < 4; j++)
{
state[i * 4 + j] = inv_sub_sbox(state[i * 4 + j]);
}
}
}
void inv_mix_columns(AES_CYPHER_T mode, uint8_t *state)
{
uint8_t y[16] = { 0x0e, 0x0b, 0x0d, 0x09, 0x09, 0x0e, 0x0b, 0x0d,
0x0d, 0x09, 0x0e, 0x0b, 0x0b, 0x0d, 0x09, 0x0e };
uint8_t s[4];
int i, j, r;
for (i = 0; i < g_aes_nb[mode]; i++)
{
for (r = 0; r < 4; r++)
{
s[r] = 0;
for (j = 0; j < 4; j++)
{
s[r] = s[r] ^ aes_mul(state[i * 4 + j], y[r * 4 + j]);
}
}
for (r = 0; r < 4; r++)
{
state[i * 4 + r] = s[r];
}
}
}
void aes_key_expansion(AES_CYPHER_T mode, uint8_t *key, uint8_t *round)
{
uint32_t *w = (uint32_t *)round;
uint32_t t;
int i = 0;
do
{
w[i] = *((uint32_t *)&key[i * 4 + 0]);
}
while (++i < g_aes_nk[mode]);
do
{
if ((i % g_aes_nk[mode]) == 0)
{
t = aes_rot_dword(w[i - 1]);
t = aes_sub_dword(t);
t = t ^ aes_swap_dword(g_aes_rcon[i / g_aes_nk[mode] - 1]);
} else if (g_aes_nk[mode] > 6 && (i % g_aes_nk[mode]) == 4)
{
t = aes_sub_dword(w[i - 1]);
} else
{
t = w[i - 1];
}
w[i] = w[i - g_aes_nk[mode]] ^ t;
}
while (++i < g_aes_nb[mode] * (g_aes_rounds[mode] + 1));
}
int aes_decrypt(AES_CYPHER_T mode, uint8_t *data, int len, uint8_t *key)
{
uint8_t w[4 * 4 * 15];
/* round key */
uint8_t s[4 * 4] = { 0 };
/* state */
int nr, i, j;
/* key expansion */
aes_key_expansion(mode, key, w);
/* start data cypher loop over input buffer */
for (i = 0; i < len; i += 4 * g_aes_nb[mode])
{
/* init state from user buffer (cyphertext) */
for (j = 0; j < 4 * g_aes_nb[mode]; j++)
s[j] = data[i + j];
/* start AES cypher loop over all AES rounds */
for (nr = g_aes_rounds[mode]; nr >= 0; nr--)
{
/* do AddRoundKey */
aes_add_round_key(mode, s, w, nr);
if (nr > 0)
{
if (nr < g_aes_rounds[mode])
{
/* do MixColumns */
inv_mix_columns(mode, s);
}
/* do ShiftRows */
inv_shift_rows(mode, s);
/* do SubBytes */
inv_sub_bytes(mode, s);
}
}
/* save state (cypher) to user buffer */
for (j = 0; j < 4 * g_aes_nb[mode]; j++)
data[i + j] = s[j];
}
return 0;
}

int main()
{

uint8_t buf[] = {0x33, 0x89, 0xCB, 0x14, 0x86, 0x53, 0xBB, 0x2B, 0x19, 0x42, 0x3D, 0x16, 0xE4, 0xDC, 0xC1, 0x5A, 0x85, 0x2C, 0x02, 0xB3, 0xAA, 0x07, 0x58, 0x0B, 0x2A, 0x32, 0x41, 0x34, 0x1A, 0xD5, 0x0D, 0xC1};
uint8_t buf1[] = {0x33, 0x89, 0xCB, 0x14, 0x86, 0x53, 0xBB, 0x2B, 0x19, 0x42, 0x3D, 0x16, 0xE4, 0xDC, 0xC1, 0x5A};
uint8_t key[] = {0x17, 0x93, 0x38, 0xc, 0x11, 0xa7, 0xf7, 0x54, 0xf7, 0x89, 0xc8, 0x20, 0xd4, 0x1a, 0xfa, 0x25};
for(int i = 0; i < 4; i++)for(int j = 0; j < i; j++)swap(buf1[i * 4 + j], buf1[i + j * 4]); //由于题目中的AES和这个轮子的行列是相反的,所以需要在解密前置换一次
aes_decrypt(AES_CYPHER_128, buf1, sizeof(buf1), key);
for(int i = 0; i < 16; i++) printf("%c", buf1[i]);
for(int i = 0; i < 16; i++) key[i] = key[i] ^ buf[i]; // 注意这里是和初始密文进行异或
uint8_t buf2[] = {0x85, 0x2C, 0x02, 0xB3, 0xAA, 0x07, 0x58, 0x0B, 0x2A, 0x32, 0x41, 0x34, 0x1A, 0xD5, 0x0D, 0xC1};
for(int i = 0; i < 4; i++)for(int j = 0; j < i; j++)swap(buf2[i * 4 + j], buf2[i + j * 4]);
aes_decrypt(AES_CYPHER_128, buf2, sizeof(buf2), key);
for(int i = 0; i < 16; i++) printf("%c", buf2[i]);
return 0;
}

得到flag{LKsyByI7oHX0S9PiqY0G2qvpte4f30mC}

mcmc

使用了 控制流平坦化和虚假控制流混淆,但是一般的 deflat 脚本没有办法完全去除混淆,但是使用 D - 810 插件可以去除大部分混淆(但也就是让代码勉强能看)。

1679413507411

可以看到,在输入以后,先判断 flag 是否为 32 位,然后进行了一些加密操作,通过 findcrypt 插件可以发现

1679415547176

加密,通过交叉引用发现是在 init 函数中,但是整个流程还是很难看,而且通过对照 salsa20 加密的源码,也无法确定是否是 salsa20 加密,通过调试,发现只跑了LABEL_6 这里,说明上面的代码应该是复制了一份作为虚假控制流,不用管,在如图的地方下断起调试,这里的测试输入为 11112222333344445555666677778888

在经过第一步异或加密以后 flag 变成了这样,可以直接看出是对第 8, 16, 24, 32 位进行了异或。

1679414186748

然后是 sub_405480 函数,通过一些调整,可以看出是把 flag 分成 8 段,每段 4 bit,然后再分成两部分进行加密,我们步入该函数,发现也被混淆了,用 D - 810 修一下

1679414481218

发现即使是修过了以后,代码还是非常难看,选择嗯看汇编,调试到加密开始的地方

1679415129361

跟踪调试一下,得出加密逻辑如下:

1
2
3
4
5
6
7
def Trans(s):
c = [0] * 4
c[0] = (2 * s[0] + s[1] - s[2] + s[3]) & 0xFFFFFFFF
c[1] = (s[0] + s[1] + s[2] - s[3]) & 0xFFFFFFFF
c[2] = (s[0] - s[1] + s[2] - s[3]) & 0xFFFFFFFF
c[3] = (s[0] + 2 * s[1] - s[2] + 2 * s[3]) & 0xFFFFFFFF
return c

接着看主函数,后面接着的是之前 findcrypt 出来的 salsa20 加密函数 sub_401820

1679415746853

调进去看一眼,用 D - 810 修复一下

1679415845982

发现逻辑是先生成了一个 a1 表,a1[8] 类似于一个计数器,从0开始递增,为奇数时执行 v4 = plain[(i + 1) % a3];,为偶数时执行 v4 = plain[(i - 1) % a3];,接着调试,发现又进行了 plain[i] ^= (v4 + (int)v7) % 256; 的异或加密,而 v7 是之前获得的表 a1a1[a1[8]] 这一项。这里有两种方法获得 v7

  1. 我们考虑在之前赋值的那一步对表 a1 进行 dump,可以直接获得整个 table
  2. 考虑在执行异或操作的那一步对存储 v7 这个变量的寄存器进行 dump,也可以获得 table

获得的 table 如下

1
table = [0xD3, 0x1B, 0xCC, 0x7D, 0xEF, 0xB9, 0x0D, 0xC6, 0xA0, 0xDB, 0xE2, 0x07, 0xFB, 0x0F, 0xB7, 0x0E, 0xCB, 0x73, 0x3A, 0x8A, 0xC5, 0x4E, 0x3E, 0xEC, 0x0C, 0xF0, 0xA2, 0x48, 0x94, 0x70, 0x27, 0x1B]

到此整个加密流程就分析完了(好像跟Chacha20, Salsa20 加密算法没啥关系,白学了属于是),然而回到 main 一看发现少了啥东西,好像找不到 check 了,原来是 D - 810 的锅,不去混淆直接把密文先弄下来

1
cipher = [0x06, 0x08, 0x65, 0x04, 0x60, 0x03, 0x08, 0x01, 0x4A, 0x10, 0x32, 0x58, 0xEE, 0x97, 0x65, 0x84, 0x44, 0xF2, 0x10, 0x6B, 0xE8, 0x50, 0x24, 0x99, 0xF6, 0xE3, 0x21, 0x51, 0xC2, 0x5D, 0xBF, 0x32]

然后一步步写出解密脚本,先逆 sub_401820

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cipher = [0x06, 0x08, 0x65, 0x04, 0x60, 0x03, 0x08, 0x01, 0x4A, 0x10, 0x32, 0x58, 0xEE, 0x97, 0x65, 0x84, 0x44, 0xF2, 0x10, 0x6B, 0xE8, 0x50, 0x24, 0x99, 0xF6, 0xE3, 0x21, 0x51, 0xC2, 0x5D, 0xBF, 0x32]
table = [0xD3, 0x1B, 0xCC, 0x7D, 0xEF, 0xB9, 0x0D, 0xC6, 0xA0, 0xDB, 0xE2, 0x07, 0xFB, 0x0F, 0xB7, 0x0E, 0xCB, 0x73, 0x3A, 0x8A, 0xC5, 0x4E, 0x3E, 0xEC, 0x0C, 0xF0, 0xA2, 0x48, 0x94, 0x70, 0x27, 0x1B]

for i in range(0, 32, 2):
cipher[i + 1] = cipher[i + 1] ^ (cipher[i] + table[i + 1]) % 256
cipher[i] = cipher[i] ^ (cipher[i + 1] + table[i]) % 256

for i in cipher:
print(hex(i), end=", ")
print()
# 0xfa, 0x29, 0xd7, 0xe6, 0x69, 0x1a, 0xd4, 0xcf, 0x9f, 0x35, 0x71, 0x61, 0x8b, 0x6a, 0xcb, 0xf7, 0x54, 0x45, 0x3b, 0xf1, 0xc3, 0x66, 0xe3, 0x89, 0xe7, 0x5, 0xfb, 0x38, 0xc1, 0x6f, 0xb0, 0xe8
hex_str = ""
for i in range(32):
hex_str = hex(cipher[i])[2:].zfill(2) + hex_str
if i % 4 == 3:
print(hex_str, end=", ")
hex_str = ""
# e6d729fa, cfd41a69, 6171359f, f7cb6a8b, f13b4554, 89e366c3, 38fb05e7, e8b06fc1

然后再用 z3 - solversub_405480 函数解了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from z3 import *

cipher = [0xe6d729fa, 0xcfd41a69, 0x6171359f, 0xf7cb6a8b, 0xf13b4554, 0x89e366c3, 0x38fb05e7, 0xe8b06fc1]
plain = [BitVec("plain[%d]" % i, 32) for i in range(8)]
sol = Solver()

sol.add(cipher[4] == ((2 * plain[4] + plain[5] - plain[6] + plain[7]) & 0xFFFFFFFF), cipher[5] == ((plain[4] + plain[5] + plain[6] - plain[7]) & 0xFFFFFFFF), cipher[6] == ((plain[4] - plain[5] + plain[6] - plain[7]) & 0xFFFFFFFF), cipher[7] == ((plain[4] + 2 * plain[5] - plain[6] + 2 * plain[7]) & 0xFFFFFFFF))

sol.add(cipher[0] == ((2 * plain[0] + plain[1] - plain[2] + plain[3]) & 0xFFFFFFFF), cipher[1] == ((plain[0] + plain[1] + plain[2] - plain[3]) & 0xFFFFFFFF), cipher[2] == ((plain[0] - plain[1] + plain[2] - plain[3]) & 0xFFFFFFFF), cipher[3] == ((plain[0] + 2 * plain[1] - plain[2] + 2 * plain[3]) & 0xFFFFFFFF))

if sol.check():
m = sol.model()
for i in range(8):
print(bytes.fromhex(hex(m[plain[i]].as_long())[2:]).decode('utf-8')[::-1], end = "")
# 3ummer170ver_C0Gingcn0t(T0p0hhh2

最后再异或一下得到 flag{3ummer1s0ver_C0dingcn0tsT0p0hhhh}