控制流平坦化着实很头大,还好现在遇到的还没有特别难的题目。
程序是64位elf文件,提示的信息也够多了:
定位到关键函数就是一堆ollvm操作,先放个主函数方便记录:
1 int __cdecl main(int argc, const char **argv, const char **envp) 2 { 3 signed int v3; // eax 4 int v4; // ST38_4 5 signed int *v5; // rsi 6 signed int v7; // [rsp+2Ch] [rbp-54h] 7 char input; // [rsp+40h] [rbp-40h] 8 int v9; // [rsp+78h] [rbp-8h] 9 int v10; // [rsp+7Ch] [rbp-4h] 10 11 v9 = 0; 12 printf("input your flag:", argv, envp); 13 gets(&input); 14 v10 = general_inspection(sudoku); 15 v7 = 0x9471480F; 16 while ( 1 ) 17 { 18 while ( 1 ) 19 { 20 while ( v7 == -2071121728 ) 21 { 22 v4 = blank_num(sudoku); // 记录9*9数组里面0的个数 23 v5 = mem_alloc(v4); // 就是申请空间函数 48*40 24 trace(sudoku, v5, v4); 25 check(sudoku); 26 check1(&input); // 第一次输入变换 27 check3(&input); 28 v9 = 0; 29 v7 = -303742386; 30 } 31 if ( v7 != -1804515313 ) 32 break; 33 v3 = -2071121728; 34 if ( v10 ) 35 v3 = 664169471; 36 v7 = v3; 37 } 38 if ( v7 == -303742386 ) 39 break; 40 if ( v7 == 664169471 ) 41 { 42 printf("error"); 43 check(sudoku); 44 v9 = 0; 45 v7 = -303742386; 46 } 47 } 48 return v9; 49 }
第14行函数貌似没啥用,调用没得反应,22行函数是统计数组里面的零个数,主要如图:
1 case -516195663: 2 ++v6; 3 v5 = 710936108; 4 break; 5 case 710936108: 6 v5 = -1026222996; 7 break; 8 case 1046773218: 9 v1 = -1585203536; 10 if ( v8 < 9 ) 11 v1 = -1892951115; 12 v5 = v1; 13 break; 14 case 1058605341: 15 ++v8; 16 v5 = 1046773218; 17 break; 18 case 1501457574: 19 v3 = 710936108; 20 if ( !(*a1)[9 * v8 + v7] ) // 9*9的数组 21 v3 = -516195663; // 跳到v6++处,记录0的个数 22 v5 = v3; 23 break; 24 } 25 } 26 return v6;
返回的结果可以动态调试得到:40
23行函数就是分配内存空间函数,里面的代码可以忽略:
24行和25行函数没得用,也没返回值,估计是其他函数也可能是代码膨胀用的。
check1函数就开始重要了,它是对输入的flag进行处理函数,进来之后,根据动调的顺序或者根据那些olllvm的顺序可以得到大概步骤:
首先:字符前后位置两两互换
然后隔着两位进行字符互换:
最后一个异或处理函数:
用python复现出大概逻辑如下: (参考https://blog.csdn.net/wlz_lc_4前辈博客)
1 def check1(): 2 v12 = len(flag) 3 for i in range(len(flag)): #显示前后两位互换数值 4 (flag[i],flag[i+1]) = (flag[i+1],flag[i]) 6 for i in range(0,len(flag),2): #隔着两位进行互换 7 (flag[i],flag[i+1]) = (flag[i+1],flag[i]) 9 for i in range(len(flag)): #最后的处理 10 flag[i] = ((flag[i]&0xf3)|(~flag[i]&0xc)) - 20
check3函数里面就是主要的判断比较了,打开里面有一个check2:
点进去康康,给出关键函数逻辑:
1 break; 2 v8 = -1129833658; 3 if ( D0g3[9 * v15 + v14] != sudoku[9 * v15 + v14] )// step3 直接和数独比较 4 v8 = -528396247; 5 v11 = v8; 6 } 7 if ( v11 != -1613667829 ) 8 break; 9 v11 = -2119125118; 10 } 11 if ( v11 != -1369143226 ) 12 break; 13 v14 = 0; 14 v11 = -740861019; 15 } 16 if ( v11 != -1244045086 ) 17 break; 18 D0g3[9 * v15 + v14] = v16[v13++];// step2 填充数组 19 v11 = 1611237474; 20 } 21 if ( v11 != -1129833658 ) 22 break; 23 v11 = -90011013; 24 } 25 if ( v11 != -740861019 ) 26 break; 27 v4 = -1613667829; 28 if ( v14 < 9 ) 29 v4 = 705300330; 30 v11 = v4; 31 } 32 if ( v11 != -528396247 ) 33 break; 34 v12 = 0; 35 v11 = 1954800504; 36 } 37 if ( v11 != -512482015 ) 38 break; 39 v14 = 0; 40 v11 = 564268595; 41 } 42 if ( v11 != -334121999 ) 43 break; 44 v15 = 0; 45 v11 = -1998111552; 46 } 47 if ( v11 != -94879051 ) 48 break; 49 v3 = -334121999; 50 if ( v15 < 9 ) 51 v3 = -1369143226; 52 v11 = v3; 53 } 54 if ( v11 != -90011013 ) 55 break; 56 ++v14; 57 v11 = 564268595; 58 } 59 if ( v11 != -2671583 ) 60 break; 61 v1 = strlen(s); 62 v2 = 2101131376; 63 if ( v15 < v1 ) 64 v2 = 441246003; 65 v11 = v2; 66 } 67 if ( v11 == 396170963 ) 68 break; 69 switch ( v11 ) 70 { 71 case 430996436: 72 ++v15; 73 v11 = -2671583; 74 break; 75 case 441246003: 76 v16[v15] = s[v15] - 0xDD55348 + 0xDD55318;// ??? 77 v11 = 430996436; 78 break; 79 case 564268595: 80 v7 = 1954800504; 81 if ( v14 < 9 ) 82 v7 = -1658909923; 83 v11 = v7; 84 break; 85 case 705300330: 86 v5 = 1611237474; 87 if ( !D0g3[9 * v15 + v14] ) // step1 如果是0,就用输入的来填充
还原一下:
1 def check2():
v13=0
6 for i in range(9): 7 for j in range(9): 8 if dog3[9 * i + j] == 0: //如果数组对应是0,就用输入的来替换 9 dog3[9 *i + i] = flag[v13] 10 v13 += 112 for i in range(9): 13 for j in range(9): 14 if dog3[9 * i + j] != sudoku[9 * i + j]:16 print("error")
最后比较的是sudo数组里面的值和填充之后的dog3数组,所以需要知道处理之后的sudo数组,可以动态调试看到:
之前的:
动调看到处理之后的:
大概就ok了,开始解密:
sudoku = [1, 4, 5, 3, 2, 7, 6, 9, 8, 8, 3, 9, 6, 5, 4, 1, 2, 7, 6, 7, 2, 8, 1, 9, 5, 4, 3, 4, 9, 6, 1, 8, 5, 3, 7, 2, 2, 1, 8, 4, 7, 3, 9, 5, 6, 7, 5, 3, 2, 9, 6, 4, 8, 1, 3, 6, 7, 5, 4, 2, 8, 1, 9, 9, 8, 4, 7, 6, 1, 2, 3, 5, 5, 2, 1, 9, 3, 8, 7, 6, 4] dog3 = [1, 0, 5, 3, 2, 7, 0, 0, 8, 8, 0, 9, 0, 5, 0, 0, 2, 0, 0, 7, 0, 0, 1, 0, 5, 0, 3, 4, 9, 0, 1, 0, 0, 3, 0, 0, 0, 1, 0, 0, 7, 0, 9, 0, 6, 7, 0, 3, 2, 9, 0, 4, 8, 0, 0, 6, 0, 5, 4, 0, 8, 0, 9, 0, 0, 4, 0, 0, 1, 0, 3, 0, 0, 2, 1, 0, 3, 0, 7, 0, 4] flag = [] for i in range(81): if sudoku[i] != dog3[i]: tmp = ord(str(sudoku[i])) + 20 flag.append( tmp&0xf3 | ~tmp&0xc ) print(flag) for i in range(0,40,2): (flag[i], flag[i+1]) = (flag[i+1], flag[i]) #两位操作 for i in range(20): (flag[i],flag[i+20]) = (flag[i+20], flag[i]) for i in range(40): print(chr(flag[i]),end='')
得到flag:
[68, 70, 65, 75, 70, 68, 73, 71, 70, 74, 64, 65, 68, 70, 64, 69, 71, 74, 74, 64, 68, 75, 69, 69, 70, 73, 75, 71, 74, 73, 65, 64, 71, 70, 74, 69, 69, 65, 64, 70]
KDEEIFGKIJ@AFGEJAEF@FDKADFGIJFA@FDE@JG@J
题目链接:https://buuoj.cn/files/6d2e274e718058cb123066b0db5d0dd3/attachment?token=eyJ1c2VyX2lkIjo1NTY4LCJ0ZWFtX2lkIjpudWxsLCJmaWxlX2lkIjo3MTV9.Xo61hA.USfgGn2ckFmd3urYgF_YaE--T4s