直接打开就能看到伪代码,逐步分析
开始判断是否加上了flag外壳,并判断了长度
Input = 0i64;
v170 = 0i64;
v171 = 0i64;
sub_140001020("Input your flag here:\r\n", argv, envp);
sub_140001080("%46s", (const char *)&Input);
if ( (_BYTE)Input != 'S'
|| *(_WORD *)((char *)&Input + 1) != 'na'
|| *(_DWORD *)((char *)&Input + 3) != 'roFg'
|| BYTE7(Input) != '{'
|| BYTE8(v171) != '}' ) // SangFor{******************}
{ // 动调确定len=41
goto LABEL_155; // Error
}
长度判断需要在汇编层面看(最开始动调的时候,没有注意长度问题,输入任意长度的值,在套了正确外壳的情况下,依旧会跳转到LABEL_155),仔细看判断{
,与}
之间判断的值长度,就可以知道具体输入了多少值
然后进行了一大堆的值进行
v5 = 0;
I = 0i64;
while ( 1 ) // 能输入的值:[0-9][A-Z]
{
v7 = *((char *)&Input + I + 8);
if ( (unsigned __int8)(*((_BYTE *)&Input + I + 8) - '0') > 9u )
break;
v8 = v5 - 0x30;
LABEL_11:
++I;
v5 = 16 * (v7 + v8);
if ( I >= 4 ) // 将四个字符转换成十六进制。比如说输入1234,转换成0x1234
{
v9 = v5 >> 4;
v10 = 1;
goto LABEL_14;
}
}
if ( (unsigned __int8)(v7 - 'A') <= 5u )
{
v8 = v5 - '7';
goto LABEL_11;
}
吐槽一下,明明可以写函数或者循环实现这个操作,但是它就是要一个一个来写….后面判断也是….
然后两两数字为一组,进行运算,并将运算后的值和对应值比较,类似于解二元一次方程。
这里我是直接动调看输出值和两个输入值有什么关联,多次尝试得出关系,并没有仔细看内部的位运算,理论上可以直接模拟代码获取两个输入值
#两者关系
flag0*flag1=num1
flag0-flag1=num2
搓脚本:
def Save(mul,sub):
for a in range(0xffff):
b=(a-(sub))&0xffff
if(a*b==mul):
print("%X%X"%(a,b),end="")
if __name__=="__main__":
mul=[0x249E15C5,0x34C7EAE2,0x637973BA,0xE5FD104]
sub=[0xFFFF59BC,0x216B,0x819D,0x9393]
print("SangFor{",end="")
for i in range(len(mul)):
Save(mul[i],sub[i])
print("}")
flag:
SangFor{2C7BD2BF862564BAED0B6B6EA94F15BC}