您现在的位置是:首页 >学无止境 >NSSCTF-[深育杯 2021]Press网站首页学无止境
NSSCTF-[深育杯 2021]Press
下载链接:下载
载入IDA,查看内容
首先进入一个函数进行初始化,进入查看
unsigned __int64 sub_4007B6()
{
int v1; // [rsp+8h] [rbp-48h]
int i; // [rsp+Ch] [rbp-44h]
char src[56]; // [rsp+10h] [rbp-40h] BYREF
unsigned __int64 v4; // [rsp+48h] [rbp-8h]
v4 = __readfsqword(0x28u);
memset(tmp, 0, sizeof(tmp));
memset(s, 0, sizeof(s));
memset(flag, 0, sizeof(flag));
memset(enflag, 0, 0xC8uLL);
v2 = 0;
xiaobiao = 0; // index
dword_602268 = 0;
f_index = 0;
strcpy(src, "++++++++++[->++++++++++++++++<],[->-<]>>[-]+++++<*++.<");
strcpy(s, src);
v1 = 0;
for ( i = 0; i <= 199; ++i )
{
if ( s[i] == '[' )
{
data[i] = ++v1;
}
else if ( s[i] == ']' )
{
data[i] = v1--;
}
else
{
data[i] = 0;
}
}
return __readfsqword(0x28u) ^ v4;
}
开辟了tmp,s,flag,enflag空间,然后给一些值赋值初始化为0,s为"++++++++++[->++++++++++++++++<],[->-<]>>[-]+++++<*++.<",然后进行for循环,从循环中分析可以知道当遍历到'['或者']'的时候就会为1,其余情况为0,然后依次赋值给data数组。返回主函数继续观察。
然后读取flag文件把内容读取到flag中进入while循环,其中有一个sub_40094B()函数跟进:
这里面就类似一个虚拟机操作,看到switch条件就是遍历s这个字符串'++++++++++[->++++++++++++++++<],[->-<]>>[-]+++++<*++.<',先跟着流程走一遍。
第一次循环:
首先是10个'+',所以tmp[0]==10,然后是'[',但是tmp[0]是10不为0,所以不会进入循环,接着是‘-’,tmp[0]变为9,然后是‘>’,移动到tmp[1],然后是16个‘+’,tmp[1]==16。然后是‘<’号又移动到tmp[0]位置,然后是一个重点遇到‘]’号会进入到循环里面,这个for循环操作是不断j--,一直到data[xiaobiao + j] != data[xiaobiao],我们观看data数据可以知道这个条件不成立就是向前遍历到‘[’。即'xiabiao+j'是前面那一个'['位置,经过这个for循环xiabiao被修改变为‘xiaobiao + j - 1’,return的时候又自增1,所以经过这个for循环又遍历到前面的那个‘[’位置(每次遇到‘]’的时候都会回到前面的那个'['位置开始再一次遍历,直到tmp[v2]==0进入循环才停止)。
因为最开始tmp[0]==10,每一次tmp[1]都会加16,所以这次循环当tmp[0]==0时tmp[1]==10*16。然后就会进入到case '['里的循环修改下标为']'位置,然后return的时候下标自增+1就会从']'后的那个符合开始。
第二次循环:[->-<]
遇到',',即把读取的flag内容存放到tmp[0]中,然后遇到‘[’,此时tmp[v2]存放的是flag内容的第一个字符肯定不是0不会进入循环,然后tmp[0]-1,右移到tmp[1]然后减1,然后左移到tmp[0],遇到']',回到前面的'['处如果不是0就一直循环往复直到tmp[0]==0,所以当tmp[0]==0的时候tmp[1]==160-flag[0],进入到了‘case [’的循环体里面就会跳到'>>[-]+++++<*++.<',进入第三次循环。
第三次循环:>>[-]+++++<*++.<
经过两次'>>'会来到tmp[2],然后遇到'[',因为tmp[2]刚开始是0所以直接进入到循环体里面来到'+'号,经过5个加号tmp[2]==5,然后遇到'<'来到tmp[1]处,遇到'*'运算乘法操作tmp[1] *= tmp[2],tmp[1]==(160-flag[0])*5+2,然后遇到'.'号会把运算结果放入到enflag数组中,然后最后左移到tmp[0]再次经过如上操作直到读取完所有的flag内容放在enflag数组当中。
这里需要注意当遍历完成一次s=‘++++++++++[->++++++++++++++++<],[->-<]>>[-]+++++<*++.<’操作后tmp[1]==0,tmp[1]等于你刚刚求出来的那个flag内容,tmp[2]不是0,但是第二次循环遇到[-]时候tmp[2]会被清零。
知道上面这些就可以写代码了:
out=[0x60, 0xE1, 0x2F, 0x05, 0x79, 0x80, 0x5E, 0xE1, 0xC5, 0x57,
0x8B, 0xCC, 0x5C, 0x9A, 0x67, 0x26, 0x1E, 0x19, 0xAF, 0x93,
0x3F, 0x09, 0xE2, 0x97, 0x99, 0x7B, 0x86, 0xC1, 0x25, 0x87,
0xD6, 0x0C, 0xDD, 0xCF, 0x2A, 0xF5, 0x65, 0x0E, 0x73, 0x59,
0x1D, 0x5F, 0xA4, 0xF4, 0x65, 0x68, 0xD1, 0x3D, 0xD2, 0x98,
0x5D, 0xFE, 0x5B, 0xEF, 0x5B, 0xCC]
flag=""
b=0
for i in range(len(out)):
b+=160
for j in range(127):
if (((b-j)*5+2)&0xff==out[i]):
flag+=chr(j)
b=out[i]
break
import base64
print(base64.b64decode(flag))
p.z师傅也写了一个z3求法,学习一下:SangFor(深育杯)-Reverse(逆向) Press Write up_水番正文的博客-CSDN博客
from z3 import *
enflag = [0x60, 0xE1, 0x2F, 0x05, 0x79, 0x80, 0x5E, 0xE1, 0xC5, 0x57, 0x8B, 0xCC, 0x5C, 0x9A, 0x67, 0x26,
0x1E, 0x19, 0xAF, 0x93, 0x3F, 0x09, 0xE2, 0x97, 0x99, 0x7B, 0x86, 0xC1, 0x25, 0x87, 0xD6, 0x0C,
0xDD, 0xCF, 0x2A, 0xF5, 0x65, 0x0E, 0x73, 0x59, 0x1D, 0x5F, 0xA4, 0xF4, 0x65, 0x68, 0xD1, 0x3D,
0xD2, 0x98, 0x5D, 0xFE, 0x5B, 0xEF, 0x5B, 0xCC]
input = [BitVec('input[%d]' % i, 8) for i in range(56)]
#yyj = BitVec('yyj', 8)
s = Solver()
s.add( ((160 - input[0]) * 5 + 2) == enflag[0] ) #第一台液压机
for i in range(1, 56): #之后的液压机
s.add( (((160 + enflag[i - 1]) - input[i]) * 5 + 2) == enflag[i] )
if sat == s.check():
ans = s.model()
flag = ""
for i in range(56):
flag += chr(ans[input[i]].as_long()) #as_long()转成整数
print(flag)