抽卡题,抽flag。
先想一下怎么搞:
1.增加石头数量一直抽(有保底)
2.flag藏在哪个函数,内存里
3.增加中奖概率到100%
dnspy各类函数文件不太熟悉
Gacha是抽卡的意思,这里应该是抽卡的主逻辑
Gift内可以看到主要逻辑,以array
作为key
,"NSSCTF_2024_R#18"
为偏移(IV
),用SM4/CBC/PKCS7Padding
模式加密input
数组,最后保存字符串到flag
对象里
一,
也就是说如果静态分析直接求flag就是一个SM4_CBC的解密过程,检查一下发现SM4没有经过魔改,应该容易解出
- 密文就是input数组
- 密钥key是
array
数组,其计算方法很明显
先z3求一下
from z3 import *
bucket = [Int('bucket_%d' % i) for i in range(16)]
#bucket = [BitVec('bucket_%d' % i, 32) for i in range(16)]
#BitVec会因为位数多导致解出结果过大
solver = Solver()
solver.add(bucket[0] * 40 + bucket[1] * 65 + bucket[2] * -53 + bucket[3] * 70 + bucket[4] * -84 + bucket[5] * -38 + bucket[6] * 94 + bucket[7] * -39 +
bucket[8] * -91 + bucket[9] * -35 + bucket[10] * 54 + bucket[11] * 17 + bucket[12] * 45 + bucket[13] * 92 + bucket[14] * -29 + bucket[15] * 61 == 3004)
solver.add(bucket[0] * -15 + bucket[1] * 74 + bucket[2] * -89 + bucket[3] * -82 + bucket[4] * -92 + bucket[5] * 27 + bucket[6] * 21 + bucket[7] * -24 +
bucket[8] * -82 + bucket[9] * -58 + bucket[10] * -36 + bucket[11] * 64 + bucket[12] * -49 + bucket[13] * -22 + bucket[14] * 59 + bucket[15] * -47 == -674)
solver.add(bucket[0] * 67 + bucket[1] * -23 + bucket[2] * 63 + bucket[3] * -38 + bucket[4] * -32 + bucket[5] * 61 + bucket[6] * -71 + bucket[7] * 49 +
bucket[8] * 83 + bucket[9] * -92 + bucket[10] * -16 + bucket[11] * 65 + bucket[12] * -22 + bucket[13] * 12 + bucket[14] * -85 + bucket[15] * 74 == 945)
solver.add(bucket[0] * -49 + bucket[1] * 48 + bucket[2] * -11 + bucket[3] * 20 + bucket[4] * -14 + bucket[5] * 92 + bucket[6] * -19 + bucket[7] * 32 +
bucket[8] * 64 + bucket[9] * -77 + bucket[10] * 49 + bucket[11] * -19 + bucket[12] * 72 + bucket[13] * -64 + bucket[14] * 85 + bucket[15] * 54 == 1721)
solver.add(bucket[0] * 36 + bucket[1] * -21 + bucket[2] * -59 + bucket[3] * -54 + bucket[4] * -96 + bucket[5] * -81 + bucket[6] * -33 + bucket[7] * 31 +
bucket[8] * -41 + bucket[9] * -70 + bucket[10] * -27 + bucket[11] * 24 + bucket[12] * 95 + bucket[13] * -61 + bucket[14] * -17 + bucket[15] * -52 == -2198)
solver.add(bucket[0] * 78 + bucket[1] * -62 + bucket[2] * 70 + bucket[3] * -69 + bucket[4] * 38 + bucket[5] * 90 + bucket[6] * -52 + bucket[7] * 41 +
bucket[8] * 63 + bucket[9] * -65 + bucket[10] * -15 + bucket[11] * 59 + bucket[12] * -31 + bucket[13] * 54 + bucket[14] * 33 + bucket[15] * -57 == -1833)
solver.add(bucket[0] * 56 + bucket[1] * 75 + bucket[2] * 71 + bucket[3] * 78 + bucket[4] * -39 + bucket[5] * -84 + bucket[6] * 55 + bucket[7] * 54 +
bucket[8] * -12 + bucket[9] * -57 + bucket[10] * 32 + bucket[11] * -19 + bucket[12] * 13 + bucket[13] * -83 + bucket[14] * 11 + bucket[15] * -67 == 829)
solver.add(bucket[0] * 10 + bucket[1] * -97 + bucket[2] * 56 + bucket[3] * -61 + bucket[4] * 45 + bucket[5] * -22 + bucket[6] * 33 + bucket[7] * 81 +
bucket[8] * 32 + bucket[9] * 49 + bucket[10] * -19 + bucket[11] * -18 + bucket[12] * 80 + bucket[13] * -98 + bucket[14] * 79 + bucket[15] * -36 == -2551)
solver.add(bucket[0] * 24 + bucket[1] * -61 + bucket[2] * 91 + bucket[3] * 93 + bucket[4] * 76 + bucket[5] * 54 + bucket[6] * -33 + bucket[7] * -29 +
bucket[8] * -72 + bucket[9] * 20 + bucket[10] * 48 + bucket[11] * 79 + bucket[12] * 76 + bucket[13] * 68 + bucket[14] * 51 + bucket[15] * 25 == 2996)
solver.add(bucket[0] * -83 + bucket[1] * -77 + bucket[2] * -64 + bucket[3] * -38 + bucket[4] * -13 + bucket[5] * -85 + bucket[6] * 33 + bucket[7] * -76 +
bucket[8] * 27 + bucket[9] * 14 + bucket[10] * -79 + bucket[11] * -63 + bucket[12] * -78 + bucket[13] * 53 + bucket[14] * -73 + bucket[15] * 61 == -2315)
solver.add(bucket[0] * 84 + bucket[1] * -67 + bucket[2] * 57 + bucket[3] * 26 + bucket[4] * 94 + bucket[5] * 20 + bucket[6] * -71 + bucket[7] * -88 +
bucket[8] * -28 + bucket[9] * -13 + bucket[10] * -40 + bucket[11] * 76 + bucket[12] * -14 + bucket[13] * 33 + bucket[14] * 76 + bucket[15] * -75 == -150)
solver.add(bucket[0] * -60 + bucket[1] * 88 + bucket[2] * -66 + bucket[3] * -72 + bucket[4] * 41 + bucket[5] * 49 + bucket[6] * 48 + bucket[7] * -77 +
bucket[8] * -42 + bucket[9] * 25 + bucket[10] * -50 + bucket[11] * -84 + bucket[12] * 40 + bucket[13] * 50 + bucket[14] * -83 + bucket[15] * -27 == -1919)
solver.add(bucket[0] * -16 + bucket[1] * -53 + bucket[2] * -21 + bucket[3] * -44 + bucket[4] * 26 + bucket[5] * -56 + bucket[6] * -90 + bucket[7] * -93 +
bucket[8] * -73 + bucket[9] * 48 + bucket[10] * 15 + bucket[11] * -43 + bucket[12] * -61 + bucket[13] * -24 + bucket[14] * 71 + bucket[15] * 67 == -1199)
solver.add(bucket[0] * 55 + bucket[1] * -34 + bucket[2] * -22 + bucket[3] * 60 + bucket[4] * 93 + bucket[5] * -95 + bucket[6] * 50 + bucket[7] * 36 +
bucket[8] * -48 + bucket[9] * -26 + bucket[10] * -94 + bucket[11] * -35 + bucket[12] * 21 + bucket[13] * -27 + bucket[14] * 91 + bucket[15] * -76 == -1163)
solver.add(bucket[0] * 64 + bucket[1] * -50 + bucket[2] * -23 + bucket[3] * -70 + bucket[4] * -78 + bucket[5] * 34 + bucket[6] * 26 + bucket[7] * 64 +
bucket[8] * -72 + bucket[9] * 10 + bucket[10] * -96 + bucket[11] * 61 + bucket[12] * -15 + bucket[13] * 31 + bucket[14] * 36 + bucket[15] * 50 == -266)
solver.add(bucket[0] * -27 + bucket[1] * 86 + bucket[2] * -61 + bucket[3] * 89 + bucket[4] * -53 + bucket[5] * 10 + bucket[6] * -42 + bucket[7] * 92 + bucket[8] * -48 + bucket[9] * 13 + bucket[10] * 84 + bucket[11] * -71 + bucket[12] * 93 + bucket[13] * 54 + bucket[14] * -69 + bucket[15] * -30 == 892
)
if solver.check() == sat:
model = solver.model()
for i in range(16):
print(f"bucket[{i}] = {model[bucket[i]]};")
else:
print("No solution found")
# bucket[0] = 1;
# bucket[1] = 14;
# bucket[2] = 1;
# bucket[3] = 17;
# bucket[4] = 2;
# bucket[5] = 1;
# bucket[6] = 1;
# bucket[7] = 0;
# bucket[8] = 2;
# bucket[9] = 4;
# bucket[10] = 1;
# bucket[11] = 17;
# bucket[12] = 2;
# bucket[13] = 0;
# bucket[14] = 2;
# bucket[15] = 16;
然后找bucket数组,在Check()函数里发现了一堆方程,用于验证bucket数组的值
然后每个+53就可以得到array也就是key了
- IV很显然是"NSSCTF_2024_R#18"
在线解密一下
二,
尝试在这里下断点但是直接抽卡不运行这一段代码,无法在这里断下来
只能修改代码逻辑
我们找到这个GetRandomItem函数,发现要经过Check才可以出发生成flag的Gift函数
即bucket必须满足上面我们z3解出来的那个值,我们直接把bucket赋值
在GachaHistoryBucket中,修改GetBucket()或者直接修改bucket初始值都可以
修改初始值的话记得选择编辑类
,不然编译不通过
抽取的部分也要改一下
我们要的flag在这里,修改一下使得它必然被执行
保存一下运行
将得到的bucket写死在
保存后再次运行,直接抽中。