中关村2019逆向 Reverse lebel:控制流平坦化 / python字节码分析

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}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

q1uTruth

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值