RCTF-2015-notsequence WP

前言

  • 这道题目有多解,但是只有一组解是flag。再次提醒自己,搞逆向的数学一定要学好,至少大学之前的数学还能有印象!

分析

  • 程序无花无壳,直接IDA反编译。
    在这里插入图片描述
  • 结构很清晰,分别来看这两个 check 函数。
    在这里插入图片描述
  • 通过测试可以得知这些 下标 的含义,如果我们用符号来作为输出,就会很清晰了:
    在这里插入图片描述
  • 然后这个函数检测的就是行 i 的数字之和 v3 = 2i。其实数学好的话,仅看这个函数就立马想到是 杨辉三角 了。我当时没有想到用符号来作为输出,直观地感受下标表示的含义,所以没有看出来。
  • 再来看第二个 check 函数。
    在这里插入图片描述
  • 这个函数其实就是检测 杨辉三角 的第二个性质,这个性质不好描述,后面脚本会呈现出来。
  • 这两个函数看完之后,我第一反应就是建立方程求解。从第一个 check 函数的返回值必须是 20 来看,我们一共有 209 个未知数。那也用不着慌,祭出大杀器—— z3

脚本

from z3 import*
for i in range(210):
	x=[Int('x%d'%i)for i in range(210)]

f=Solver()
for i in range(210):
	f.add(x[i]!=0)


f.add(x[0]==1<<0)
f.add(x[1]+x[2]==1<<1)
f.add(x[3]+x[4]+x[5]==1<<2)
f.add(x[6]+x[7]+x[8]+x[9]==1<<3)
f.add(x[10]+x[11]+x[12]+x[13]+x[14]==1<<4)
f.add(x[15]+x[16]+x[17]+x[18]+x[19]+x[20]==1<<5)
f.add(x[21]+x[22]+x[23]+x[24]+x[25]+x[26]+x[27]==1<<6)
f.add(x[28]+x[29]+x[30]+x[31]+x[32]+x[33]+x[34]+x[35]==1<<7)
f.add(x[36]+x[37]+x[38]+x[39]+x[40]+x[41]+x[42]+x[43]+x[44]==1<<8)
f.add(x[45]+x[46]+x[47]+x[48]+x[49]+x[50]+x[51]+x[52]+x[53]+x[54]==1<<9)
f.add(x[55]+x[56]+x[57]+x[58]+x[59]+x[60]+x[61]+x[62]+x[63]+x[64]+x[65]==1<<10)
f.add(x[66]+x[67]+x[68]+x[69]+x[70]+x[71]+x[72]+x[73]+x[74]+x[75]+x[76]+x[77]==1<<11)
f.add(x[78]+x[79]+x[80]+x[81]+x[82]+x[83]+x[84]+x[85]+x[86]+x[87]+x[88]+x[89]+x[90]==1<<12)
f.add(x[91]+x[92]+x[93]+x[94]+x[95]+x[96]+x[97]+x[98]+x[99]+x[100]+x[101]+x[102]+x[103]+x[104]==1<<13)
f.add(x[105]+x[106]+x[107]+x[108]+x[109]+x[110]+x[111]+x[112]+x[113]+x[114]+x[115]+x[116]+x[117]+x[118]+x[119]==1<<14)
f.add(x[120]+x[121]+x[122]+x[123]+x[124]+x[125]+x[126]+x[127]+x[128]+x[129]+x[130]+x[131]+x[132]+x[133]+x[134]+x[135]==1<<15)
f.add(x[136]+x[137]+x[138]+x[139]+x[140]+x[141]+x[142]+x[143]+x[144]+x[145]+x[146]+x[147]+x[148]+x[149]+x[150]+x[151]+x[152]==1<<16)
f.add(x[153]+x[154]+x[155]+x[156]+x[157]+x[158]+x[159]+x[160]+x[161]+x[162]+x[163]+x[164]+x[165]+x[166]+x[167]+x[168]+x[169]+x[170]==1<<17)
f.add(x[171]+x[172]+x[173]+x[174]+x[175]+x[176]+x[177]+x[178]+x[179]+x[180]+x[181]+x[182]+x[183]+x[184]+x[185]+x[186]+x[187]+x[188]+x[189]==1<<18)
f.add(x[190]+x[191]+x[192]+x[193]+x[194]+x[195]+x[196]+x[197]+x[198]+x[199]+x[200]+x[201]+x[202]+x[203]+x[204]+x[205]+x[206]+x[207]+x[208]+x[209]==1<<19)

f.add(x[0]+x[1]+x[3]+x[6]+x[10]+x[15]+x[21]+x[28]+x[36]+x[45]+x[55]+x[66]+x[78]+x[91]+x[105]+x[120]+x[136]+x[153]+x[171]==x[191])
f.add(x[2]+x[4]+x[7]+x[11]+x[16]+x[22]+x[29]+x[37]+x[46]+x[56]+x[67]+x[79]+x[92]+x[106]+x[121]+x[137]+x[154]+x[172]==x[192])
f.add(x[5]+x[8]+x[12]+x[17]+x[23]+x[30]+x[38]+x[47]+x[57]+x[68]+x[80]+x[93]+x[107]+x[122]+x[138]+x[155]+x[173]==x[193])
f.add(x[9]+x[13]+x[18]+x[24]+x[31]+x[39]+x[48]+x[58]+x[69]+x[81]+x[94]+x[108]+x[123]+x[139]+x[156]+x[174]==x[194])
f.add(x[14]+x[19]+x[25]+x[32]+x[40]+x[49]+x[59]+x[70]+x[82]+x[95]+x[109]+x[124]+x[140]+x[157]+x[175]==x[195])
f.add(x[20]+x[26]+x[33]+x[41]+x[50]+x[60]+x[71]+x[83]+x[96]+x[110]+x[125]+x[141]+x[158]+x[176]==x[196])
f.add(x[27]+x[34]+x[42]+x[51]+x[61]+x[72]+x[84]+x[97]+x[111]+x[126]+x[142]+x[159]+x[177]==x[197])
f.add(x[35]+x[43]+x[52]+x[62]+x[73]+x[85]+x[98]+x[112]+x[127]+x[143]+x[160]+x[178]==x[198])
f.add(x[44]+x[53]+x[63]+x[74]+x[86]+x[99]+x[113]+x[128]+x[144]+x[161]+x[179]==x[199])
f.add(x[54]+x[64]+x[75]+x[87]+x[100]+x[114]+x[129]+x[145]+x[162]+x[180]==x[200])
f.add(x[65]+x[76]+x[88]+x[101]+x[115]+x[130]+x[146]+x[163]+x[181]==x[201])
f.add(x[77]+x[89]+x[102]+x[116]+x[131]+x[147]+x[164]+x[182]==x[202])
f.add(x[90]+x[103]+x[117]+x[132]+x[148]+x[165]+x[183]==x[203])
f.add(x[104]+x[118]+x[133]+x[149]+x[166]+x[184]==x[204])
f.add(x[119]+x[134]+x[150]+x[167]+x[185]==x[205])
f.add(x[135]+x[151]+x[168]+x[186]==x[206])
f.add(x[152]+x[169]+x[187]==x[207])
f.add(x[170]+x[188]==x[208])
f.add(x[189]==x[209])
while(f.check()==sat):
    res=f.model()
    ans=[]
    for i in x:
        ans.append(res[i])
        f.add(Or(i!=res[i]))
    print(ans)

后续分析

  • 结果只报出来了一个解

[1, 3, -1, 6, -1, -1, 11, -1, -1, -1, 20, -1, -1, -1, -1, 37, -1, -1, -1, -1, -1, 70, -1, -1, -1, -1, -1, -1, 135, -1, -1, -1, -1, -1, -1, -1, 264, -1, -1, -1, -1, -1, -1, -1, -1, 521, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1034, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2059, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8205, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16398, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 32770, -1, -1, -1, -1, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 65552, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 131089, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 262162, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 524445, -18, -17, -16, -15, -1, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1]

  • 之前脚本还写错了,解不出来,我也是醉了。于是去看了别人的 WP 才发现是 杨辉三角 ,恍然大悟,直拍大腿,真是菜啊,气不过 (`ヘ´)=3。记得大一的时候老师还让我们编程打印 杨辉三角 ,当时我也去了解过它的特性,现在却。
  • 但是我再想了想,我之前的方法应该也是可以得到正确答案的,只是存在多解而已。于是我坚持把脚本修改好了才得到如上所示。
  • 但是实际运行只得到一组解。主要是因为求多解的代码写的不对。由于这题的特殊性,input[0] 必为 1,所以 f.add(Or(i!=res[i])) 这句代码已经排除了所有的解。又实在没想到其他的写法,有知道的人请告诉我一下哈~(不要告诉我写 209 个循环,会出人命的!)

多解分析

  • 程序没有限制输入的数值不能为负数,并且没有检查 杨辉三角 的一个重要性质—— 当前行处于中间位置的数字等于对应上一行的两个数字之和。
  • 最后还是再写一下 杨辉三角 以加深印象!

杨辉三角

def Triangles():
    yang=[1]
     while(True): 
          yield yang
          yang=[1]+[yang[i]+yang[i+1] for i in range(len(yang)-1)]+[1]
n=0
for t in Triangles():
    if n>20:
            break
    n+=1
    print(t)


[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
[1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1]
[1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1]
[1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13, 1]
[1, 14, 91, 364, 1001, 2002, 3003, 3432, 3003, 2002, 1001, 364, 91, 14, 1]
[1, 15, 105, 455, 1365, 3003, 5005, 6435, 6435, 5005, 3003, 1365, 455, 105, 15, 1]
[1, 16, 120, 560, 1820, 4368, 8008, 11440, 12870, 11440, 8008, 4368, 1820, 560, 120, 16, 1]
[1, 17, 136, 680, 2380, 6188, 12376, 19448, 24310, 24310, 19448, 12376, 6188, 2380, 680, 136, 17, 1]
[1, 18, 153, 816, 3060, 8568, 18564, 31824, 43758, 48620, 43758, 31824, 18564, 8568, 3060, 816, 153, 18, 1]
[1, 19, 171, 969, 3876, 11628, 27132, 50388, 75582, 92378, 92378, 75582, 50388, 27132, 11628, 3876, 969, 171, 19, 1]
[1, 20, 190, 1140, 4845, 15504, 38760, 77520, 125970, 167960, 184756, 167960, 125970, 77520, 38760, 15504, 4845, 1140, 190, 20, 1]

总结

  • 杨辉三角两大特性:
  • i 行数字之和等于 2ii=0 表示第一行
  • 每一行处在中间位置的数字等于对应上一行两个数字之和
  • 时刻回忆大学之前的数学知识
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值