flat
题目名字,流程图都指向了控制流平坦化
通过阅读 https://blog.csdn.net/yangbostar/article/details/6204724 了解了
顺序流/条件流/循环控制流平坦化 及 一些变形
静态分析
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char v4; // al
int v5; // ecx
char v6; // al
int v7; // ecx
char v8; // al
int v9; // ecx
char v10; // al
int v11; // ecx
char v12; // al
int v13; // ecx
int v15; // [rsp+4Ch] [rbp-154h]
char dest; // [rsp+50h] [rbp-150h]
int v17; // [rsp+E8h] [rbp-B8h]
int index; // [rsp+ECh] [rbp-B4h]
char arr[64]; // [rsp+F0h] [rbp-B0h]
char s[108]; // [rsp+130h] [rbp-70h]
int v21; // [rsp+19Ch] [rbp-4h]
v21 = 0;
printf("please input string:\n", argv, envp);
gets(s);
v17 = strlen(s);
index = 5;
v15 = -1046111848;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( v15 == -2012804730 )
{
memcpy(&dest, "J", 0x90uLL); // J2261C63-3I2I-EGE4-IBCC-IE41A5I5F4HB
v4 = fun_check1(s); // 判断len
v5 = -1855144052;
if ( v4 & 1 )
v5 = 1890735184; // 这个分支值走的是default
v15 = v5;
} // 第4个while结束
if ( v15 != -1855144052 )
break;
v15 = -1153978545;
printf("what a shame !!!\n");
} // 第3个while结束
if ( v15 != -1612797716 )
break;
v10 = fun_check4(s);
v11 = -1855144052;
if ( v10 & 1 )
v11 = -842747696;
v15 = v11; // 第2个while结束
}
if ( v15 == -1153978545 )
break;
switch ( v15 )
{
case -1046111848:
v3 = -2012804730;
if ( index < v17 - 1 )
v3 = -370117910;
v15 = v3;
break;
case -842747696:
v12 = fun_check5(arr, (int *)&dest);
v13 = -1855144052;
if ( v12 & 1 )
v13 = -194644933;
v15 = v13;
break;
case -370117910:
arr[index - 5] = s[index]; // arr赋值块
v15 = 805361575;
break;
case -194644933:
v15 = -1153978545;
printf("you got it !\n");
break;
case 805361575: // index++块
++index;
v15 = -1046111848;
break;
case 1222385267:
v8 = fun_check3(s); // }
v9 = -1855144052;
if ( v8 & 1 )
v9 = -1612797716;
v15 = v9;
break;
default:
v6 = fun_check2(s); //flag{
v7 = -1855144052;
if ( v6 & 1 )
v7 = 1222385267;
v15 = v7;
break;
}
}
return 0;
}
从开头跟着分支值跟一遍(或从回显信息那从后往前推),把各块实现的功能拼接,大致流程为
首先 arr[i] = flag{后的字符直到字符串结束
之后 memcpy dest为J2261C63-3I2I-EGE4-IBCC-IE41A5I5F4HB,然后call func1判断len范围0到50
之后 call func2 判断input前几位为 ‘flag{’
之后 call func3 判断input[41] = ‘}’
之后 call func4 判断input[23,18,13,28] = ‘-’
最后 call func5
从头跟一遍
输入范围’0’-‘9’,local_arr[i] = arr[i]+17,i++
输入范围’a’-‘z’,local_arr[i] = arr[i] - 0x30,i++
输入范围’A’-‘Z’,i++
输入为‘-',local_arr[i] = arr[i],i++
从尾跟一遍
v16 = 1 <- index == 36 <- index++ <- local_arr[i] == dest[i]
所以将J2261C63-3I2I-EGE4-IBCC-IE41A5I5F4HB中数字-17,字母+0x30,套上flag{}
推出 flag{9bbfa2fc-c8b8-464d-8122-84da0e8e5d71}
一些思考
- 从头(程序开始)跟一遍,从尾(回显信息,return的值)跟一遍,多分支要想办法减少条件分支
- 一个块由两部分构成:实现的功能和分支值的改变。做题时追踪分支值,拼接各块实现的功能
- 整个程序的出口是最外层while的break
- 题目里嵌套的很多while(1)的原因是保证在执行完一个块后,能够回到开始,其功能和 https://blog.csdn.net/yangbostar/article/details/6204724 中每个case下的goto是一样的
- switch,while(expression)本质都是if。在平坦化中,一个if就代表了一个分支值,一个分支值就对应了一个块,分支值和它对应的块有以下几种表现形式
if(分支值1)
分支值1的块
elif(分支值2)
分支值2的块
else
其它分支值的块
switch(分支值)
case(分支值1)
1的块
case(分支值2)
2的块
default:
其它的块
while(分支值1)
{
1的块
}
if(不等于分支值2)
break
2的块
py交易
python字节码
python字节码操作栈,和wasm相似
https://blog.csdn.net/wenxueliu/article/details/80919947 看不懂的字节码链接里找一下
比较
COMPARE_OP 0 (<)
COMPARE_OP 4 (>)
COMPARE_OP 2 (==)
列表及循环
a = [1, 2, 3]
for e in a:
b = e
LOAD_CONST 0 (1)
LOAD_CONST 1 (2)
LOAD_CONST 2 (3)
BUILD_LIST 3
STORE_NAME 0 (a)
SETUP_LOOP 16 (to 28)
LOAD_NAME 0 (a)
GET_ITER
FOR_ITER 8 (to 26)
STORE_NAME 1 (e)
LOAD_NAME 1 (e)
STORE_NAME 2 (b)
JUMP_ABSOLUTE 16 //调到 FOR_ITER
POP_BLOCK
LOAD_CONST 3 (None)
RETURN_VALUE
函数调用
a=2
print(a)
LOAD_CONST 0 (2)
STORE_NAME 0 (a)
LOAD_NAME 1 (print)
LOAD_NAME 0 (a)
CALL_FUNCTION 1
POP_TOP
LOAD_CONST 1 (None)
RETURN_VALUE
静态分析
cd到目录
python generateCFG.py ./example/py交易.pyc
分析生成的最后一张图(总流程图)
从开头跟一遍,大致流程为
一堆load_const
build_list 38,store a
一堆load_const,
build_list 38,store b
一堆load_const,
build_list 38 ,store c
build_list 0,store d(新建空列表d)
setup_loop
load global range
load_const 38
call func 1
GET_ITER
FOR_ITER
store i
load d,load_attr append(d.append(后面所有的结果))
load a,load i,subscr(a[i])
load data,load i,subscr(data[i])
multiply(data[i]*a[i])
load data,load i,subscr(data[i])
multiply((data[i]*a[i])*data[i])
load b,load i,subscr(b[i])
load data,subscr(data[i])
multipal(data[i]*b[i])
add((data[i]*b[i])+(data[i]*a[i])*data[i])
load c,load i,subscr(c[i])
add((data[i]*b[i])+(data[i]*a[i])*data[i]+c[i])
call func 1
pop_top结束
同样方式建完list e后,load d,load e,cmp
若相等:get global p_s(盲猜得到回显字符串)
call func 0(盲猜是print函数)
pop_top
不相等:load global p_f
call func 0
pop_top
总结一下流程就是
构建了四个长度为38的list a,b,c,e,要满足如下关系:
a[i]*data[i]*data[i] + b[i]*data[i] + c[i] == e[i]
一元二次方程
解题脚本
import z3
s = z3.Solver()
x = [z3.Int("x%d"%i) for i in range(38)]
for i in range(len(x)):
s.add(0<x[i])
a = [13433, 4747, 17752, 33060, 31051, 48809, 29988, 6421, 20021, 38888, 24844, 20706, 11713, 34938, 12865, 6085, 37391, 32840, 31964, 27194, 8701, 48142, 27066, 28626, 37431, 39142, 46795, 21771, 44280, 40628, 35013, 18583, 5418, 4347, 43929, 9934, 46892, 19868]
b = [13711, 7074, 79833, 42654, 23241, 41412, 61795, 6373, 19304, 1363, 1682, 66279, 76134, 60748, 10355, 63484, 30491, 34005, 51393, 38029, 7241, 4998, 18562, 16935, 66677, 51321, 13771, 49108, 52166, 8851, 16900,31682,16684,12046,16764,64315,76742,14022]
c = [832832835, -924053193, -307134635, -527578092, 998625960, -715102211, 3572182, -963194083, -475718185, -361574731, -678171563, 107566155, 608670527, 254218946, -81206308, -284228457, 373369420, 659110852, 165298084, -389004184, 893094421,-868933443, 44838205, -98551062, -59800920, -575871298, -748337118, 696390966, 427210246,-266607884, -555200820, -594235119, -233255094, 229291711, 711922719, 14476464, -783373820, 892608580]
e = [973988289, -867920193, -132362266, -172451190, 1471255182, -242282199, 321870424, -897049789, -428663209, -256350703, -613466537, 321254055, 641759727, 344601346, -40281788, -217030057, 476060216, 767746297, 503093626, -102198850, 984358207, -415480559, 322813233, 178032672, 48876640, -467362638, -260077296, 923436845, 536082660, -138702820, -210365307, -397666023, -215329942, 274852104, 818217684, 41479433, -632022956 ,1204798830]
flag = ''
for i in range(37):
s.add(x[i]*x[i]*a[i] + x[i]*b[i] + c[i]==e[i])
if s.check() == z3.sat:
m = s.model()
for i in range(len(x)):
flag += chr(m[x[i]].as_long())
print(flag+"}")
#flag{bfe043d228d49fffaeb54fe18cf8e118}