[BUUCTF]REVERSE——[GXYCTF2019]simple CPP

[GXYCTF2019]simple CPP

附件

步骤

  1. 例行检查,64位程序,无壳
    在这里插入图片描述
  2. 试运行一下程序,看看大概的情况
    在这里插入图片描述
  3. 64位ida载入,首先检索字符串,查看有关flag的信息
    在这里插入图片描述
    根据有关flag的字符串,找到关键函数
    main()太长了,就不直接贴了,一段一段看
  4. 逆向逆向,我们肯定是从下往上看程序。
    首先是关于flag的判断,满足168的if判断,就是正确的flag,flag用GXY{}包裹
    在这里插入图片描述
    sub_7FF603EF19C0()函数里面太长了,应该不是什么运算,应该是自己写的功能函数,结合sub_7FF603EF19C0调用时候的字符串,猜测这个函数的作用是输出参数字符串。根据171~175,可以猜测,flag的字符串存放在Block中
  5. 往上看,看一下if判断里的各个参数从哪里来的
    在这里插入图片描述

算法有点乱,整理一下
v38 = v18[2];
v19 = v18[1];
v20 = v18[0];

v22 = v19 & v20
v21[0] = v19 & v20
得到v22=v21[0]= v19 & v20=v18[1]&v18[0]

v23 = v38 & ~v20
v21[1] = v23;
得到 v23 =v21[1]= v38 & ~v20=v18[2]&~v18[0]

v24 = ~v19;
得到 v24 = ~v19=~v18[1]

v25 = v38 & v24;
v21[2] = v38 & v24;
得到 v21[2] = v38 & v24=v25=v18[2]& ~v18[1]

v26 = v20 & v24;
v21[3] = v26;
得到v26 = v20 & v24=v21[3]=v18[0]&~v18[1]

v23=v21[1]=1176889593874
得到 v18[2]&~v18[0]=1176889593874

v27 = v23 | v22 | v25 | v26
得到 v27 =(v18[1]&v18[0]) | (v18[1]&v18[0]) |(v18[2]& ~v18[1]) | (v18[0]&~v18[1])=4483974544037412639

v28 = v18[1];
v29 = v18[2];
v30 = v25 & *v18 | v29 & (v22 | v28 & ~*v18 | ~(v28 | *v18))=577031497978884115i64
得到v30 =(v18[2]& ~v18[1]) & v18[0] | v18[2] & ((v18[1]&v18[0]) | v18[1] & ~v18[0] | ~(v18[1] | v18[0]))=577031497978884115

v31 = v27 == 4483974544037412639i64;
v27 ^ v18[3] == 4483974543195470111i64 )
得到 v183^(v18[1]&v18[0]) | (v18[1]&v18[0]) |(v18[2]& ~v18[1]) | (v18[0]&~v18[1])=4483974544037412639

最后所有的表达式都换成了用v18[0],v18[1],v18[2],v18[3]去表示。利用z3库求解一下这个方程
x,y,z,w分别代表v18[0]~v18[3]

 from z3 import *

x,y,z,w=BitVecs('x y z w',64)

s=Solver()

s.add((~x)&z==1176889593874)
s.add(((z&~x)|(x&y)|(z&(~y))|(x&(~y)))^w==4483974543195470111)
s.add(((z&~y)&x|z&((x&y)|y&~x|~(y|x)))==577031497978884115)
s.add(((z&~x)|(x&y)|(z&~y)|(x&~y))==4483974544037412639)
s.add(((z&(~x)) | (x&y) | y & z) == (((~x)& z)|864693332579200012))

s.check()
m = s.model()
for i in m:
    print("%s = 0x%x"%(i,m[i].as_long()))

在这里插入图片描述

  1. 我们的目的是找到block里的值,继续往上看,看看v18里的数据是哪儿来的
    在这里插入图片描述
    v18里的数据来自v11,v12,v13,v14,而v11这些数据的值又跟v40,v6有关,继续往上看
    在这里插入图片描述
    程序一开始调用sub_7FF603EF19C0()输出刚开始运行时看到的那两句话,根据运行时候的猜测,sub_7FF603EF1DE0()的作用是让我们输入数据,存储在block中。根据51行和53行猜测,v40-5要大于25,那个5应该是GXY{},所以flag的长度是25,v40里存放的是flag的长度。
    然后到76行开始,从76~85,应该是将我们输入的数据跟qword_7FF603EF6048进行了异或操作后存放到v6中。
    直接点击qword_7FF603EF6048看到的值是0,但是0异或任何一个数都是它本身,感觉不太对,但还是尝试了一下当它等于0的时候,那么v11这些的值就是v6中的值,就是我们输入的字符串,就是说我们用z3求解出来的数就是我们的flag,转换成字符串看一下
    明显不对。
    在这里插入图片描述
    所以说程序里肯定有函数将qword_7FF603EF6048进行了赋值
    右击该参数,选择jump to xref,查看那里引用了这个参数
    在这里插入图片描述
    会发现有个sub_7FF603EF1720的函数,对它进行了赋值
    在这里插入图片描述
    memcpy和memove这两个函数比较显眼,他们常用于拷贝字节,所以这里应该是将i_will_check_is_debug_or_not拷贝到了qword_7FF603EF6048中,当qword_7FF603EF6048=i_will_check_is_debug_or_not的时候,尝试一下
Dst = 'i_will_check_is_debug_or_noi_wil'
flag=[0x3E,0x3A,0x46,0x05,0x33,0x28,0x6F,0x0D,0x8C,0x00,0x8A,0x09,0x78,0x49,0x2C,0xAC,0x08,0x02,0x07,0x17,0x15,0x3E,0x30,0x13,0x32,0x31,0x06]
s=''
for i in range(len(flag)):
    s+=chr(ord(Dst[i]) ^ flag[i])
    #s+=chr(flag[i])
print(s)

在这里插入图片描述
看到中间是乱码,奇怪,没招了,百度看了其他师傅wp后得知,原因是方程不止一个解,而比赛的时候给出了第二部分的结果是e!P0or_a

from z3 import *

x,y,z,w=BitVecs('x y z w',64)

s=Solver()

s.add((~x)&z==1176889593874)
s.add(((z&~x)|(x&y)|(z&(~y))|(x&(~y)))^w==4483974543195470111)
s.add(((z&~y)&x|z&((x&y)|y&~x|~(y|x)))==577031497978884115)
s.add(((z&~x)|(x&y)|(z&~y)|(x&~y))==4483974544037412639)
s.add(((z&(~x)) | (x&y) | y & z) == (((~x)& z)|864693332579200012))

s.check()
m = s.model()
for i in m:
    print("%s = 0x%x"%(i,m[i].as_long()))

w = [0x32,0x31,0x06]
z = [0x08,0x02,0x07,0x17,0x15,0x3E,0x30,0x13]
y = "e!P0or_a"
x = [0x3e,0x3a,0x46,0x05,0x33,0x28,0x6f,0x0d]
Dst = 'i_will_check_is_debug_or_noi_wil'

flag=[0x3E,0x3A,0x46,0x05,0x33,0x28,0x6F,0x0D,0x8C,0x00,0x8A,0x09,0x78,0x49,0x2C,0xAC,0x08,0x02,0x07,0x17,0x15,0x3E,0x30,0x13,0x32,0x31,0x06]
s=''
for i in range(len(flag)):
    s+=chr(ord(Dst[i]) ^ flag[i])
    #s+=chr(flag[i])
print(s)

b=""
for i in range(len(x)):
    b+=chr(ord(Dst[i]) ^ x[i])
print(b)

在这里插入图片描述
从We1l_D0n后开始把äeéb’ _ó改成e!P0or_a即是最后的flag

flag{We1l_D0ne!P0or_algebra_am_i}

参考wp:https://www.cnblogs.com/LLeaves/p/13522069.html

这位师傅是动调做的该题,可以发现动调比较容易搞清楚程序逻辑,我动调比较菜,硬是头铁静态给理顺了程序。好吧其实是我不会用ida动调
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/mcmuyanga/article/details/113628506
cpp