题目下载:下载
拖入查壳工具,无壳,64位程序,载入IDA。
因为代码较长,就不截图了,分块分析。(部分变量进行了改名)
通过上面知道Block是输入,可能为flag。其中有一个if语句,容易知道不能走这里,所以v44要小于等于30。然后分配了一些内存空间给v7,v7赋值给v8。
第二部分:
其中第86行是一个异或,只有这一个地方是异或运算,所以这里应该是关键加密。整体是一个do...while循环,把Block的地址给了v11,又因为v45==15小于16,所以if语句不会执行,然后把off_7FF66C3C6048地址给了v12,if语句还是不会执行(条件不成立),然后进行关键异或,v11与v12遍历异或赋值给v8。
接下来是另一个关键代码:
把异或后的结果给了v18,然后do...while循环。经过分析知道,这是在获取异或后的v8的值,是在进行字节叠加,首先从v8的第一个字节开始,赋值给v19,然后v19在左移8位,在加上v8的第二个字节,这不就是相当于把v8的值在给v19嘛,但是不同的是,当v17为8,16,24时便将v19分别进行赋值转移给v16,v15,v14,v13,在重新开始,所以就是相当于把v8数值分成4组,前三组分别8Byte,最后一组4字节(后面也算出)。
最后部分:
这里发现有很多已知数值,和v8分4组过后的4个未知数,用a0,a1,a2,a3表示,在这里用z3约束器能求解
from z3 import *
s=Solver()
a0=BitVec('a0',64)
a1=BitVec('a1',64)
a2=BitVec('a2',64)
a3=BitVec('a3',64)
s.add((a2 & ~a1 & a0) | a2 & ((a1&a0) | a1 & ~a0 | ~(a1 | a0))==577031497978884115)
s.add((a2&~a0 | a1&a0 | a2&~a1 | a0&~a1)==4483974544037412639)
s.add(((a2&~a0 | a1&a0 | a2&~a1 | a0&~a1)^a3)==4483974543195470111)
s.add((a2&~a0 | a1&a0 | a1&a2)==(~a0 & a2)|864693332579200012)
s.add((a2&~a0)==1176889593874)
if s.check()==sat:
answer=s.model()
for i in answer:
print("%s = 0x%x"%(i,answer[i].as_long()))
结果为
as_long()方法从模型对象中提取具体值。
所以就知道v8的值了。在回到关键异或
要求v11,需要知道v12,在off_7FF66C3C6048交叉引用, 所以v12也已知(也可以通过动态调试获取v12),然后就可以求出v11即flag
v8 =[0x3e,0x3a,0x46,0x05,0x33,0x28,0x6f,0x0d,0x1,0x08,0x48,0x00,0x00,0x80,0x00,0x04,0x8,0x02,0x07,0x17,0x15,0x3e,0x30,0x13,0x32,0x31,0x06,0x00]
v12 = "i_will_check_is_debug_or_not"
flag = ""
for i in range(len(v8)-1):
flag += chr((ord(v12[i%27])) ^ v8[i])
print(flag)
求出为 We1l_D0nim+k_és[lgebra_am_i,发现并不是flag,原来原本题目中有个条件,即第二组数据为e!P0or_a,所以对上面的进行修改
flag为 We1l_D0ne!P0or_algebra_am_i