zoukankan      html  css  js  c++  java
  • #writeup# picoctf 2018 be-quick-or-be-dead 系列

    picoctf

    be-quick-or-be-dead-1

    image-20200503010543744

    程序逻辑比较简单:

    • 设定一个时间计数器,一旦某个函数运行超时就退出
    • get_key :生成一个固定的key值
    • print_flag : 将 flag 打印出来。因为get_key 超时,导致无法执行到该函数
    • 这题如果爆破,估计直接能出结果(将set_timer直接nop掉。)
    • 看别人的wp,patch可以将超时设置到8s,或者将set_timer 直接retn

    从IDA 获取数据

    flag = 0x0000000000601080

    ret = []
    for b in get_bytes(flag, 59): // 返回的是str
      ret.append(hex(ord(b)))
    print ret

    得到16进制的数组

    ['0x52', '0x4b', '0x9e', '0x8a', '0x60', '0x76', '0xbb', '0x9e', '0x53', '0x4a', '0x84', '0xba', '0x47', '0x4d', '0x89', '0x8d', '0x43', '0x50', '0xa2', '0x81', '0x48', '0x4b', '0x93', '0x82', '0x77', '0x57', '0x93', '0x8b', '0x4c', '0x41', '0x98', '0x96', '0x59', '0x43', '0x8f', '0x9c', '0x74', '0x41', '0x92', '0x88', '0x5c', '0x57', '0x89', '0x84', '0x59', '0x4b', '0x92', '0x8b', '0x71', '0x46', '0xcd', '0x86', '0x19', '0x43', '0x9c', '0x86', '0x55', '0x5f', '0x0']

    核心加密算法

    // key = 0xE5FD2222LL
    __int64 __fastcall decrypt_flag(int key)
    {
     __int64 result; // rax
     int v2; // [rsp+0h] [rbp-14h]
     unsigned int i; // [rsp+10h] [rbp-4h]

     v2 = key;
     for ( i = 0; ; ++i )
    {
       result = i;
       if ( i > 0x39 )
         break;
       flag[i] ^= *((_BYTE *)&v2 + (signed int)i % 4);
       if ( (signed int)i % 4 == 3 )
         ++v2;
    }
     return result;
    }

    核心就在这句话:

     flag[i] ^= *((_BYTE *)&v2 + (signed int)i % 4);

    v2 是一个int类型:

    • &v2: 取int的地址,也就是一个指向v2的整型指针
    • (byte *)&v2 :将 int 类型指针转换为 byte 类型
    • (byte *)&v2 + i % 4:每次取一个 byte
    • 所以整句的意思是,每次取 key 的一个byte出来,进行xor
    • 后面 i%4 == 3,也就是四次就把 key + 1

    写出解密代码。

    flag = ['0x52', '0x4b', '0x9e', '0x8a', '0x60', '0x76', '0xbb', '0x9e', '0x53', '0x4a', '0x84', '0xba', '0x47', '0x4d', '0x89', '0x8d', '0x43', '0x50', '0xa2', '0x81', '0x48', '0x4b', '0x93', '0x82', '0x77', '0x57', '0x93', '0x8b', '0x4c', '0x41', '0x98', '0x96', '0x59', '0x43', '0x8f', '0x9c', '0x74', '0x41', '0x92', '0x88', '0x5c', '0x57', '0x89', '0x84', '0x59', '0x4b', '0x92', '0x8b', '0x71', '0x46', '0xcd', '0x86', '0x19', '0x43', '0x9c', '0x86', '0x55', '0x5f', '0x0']

    key = ['0x22', '0x22', '0xFD', '0xE5']  #转换为数组
    ret = []
    for i in range(0x3a):
       c = int(flag[i], 16)
       c ^= int(key[i % 4], 16)
       ret.append(chr(c))
       print key
       if i % 4 == 3:
           key[0] = hex(int(key[0], 16) + 1)

    print ''.join(ret)

    得到结果:

    picoCTF{why_bother_doing_unnecessary_computation_d0c6aace}

    be-quick-or-be-dead-2

    相比于第一题,有两点变化:

    • set_timer 超时默认从1s变为8s
    • get_key 中使用了 fib 函数来生成key,而不是直接返回

    尝试着用python递归实现fib,发现直接超出了递归的最大限制次数:

    # fib递归的算法会严重超时
    def fib(a1):
       v1 = 0
       v3 = 0
       if(a1 > 1):
           v1 = fib(a1 - 1)
           v3 = v1 + fib(a1 - 2)
       else:
           v3 = a1
       return v3

    提示 max recursion depth exceeded

    image-20200504102044209

    典型的斐波拉契数列,wiki 一下就知道,有多种算法实现:

    • 递归实现
    • 非递归(利用数组,循环即可)
    • 快速矩阵幂 (涉及到矩阵乘法)

    非递归的方法最简单,自己实现一个:

    from ctypes import *
    def fib2(n):
       input= {}
       input[0] = 0
       input[1] = 1
       for i in range(2, n+1):
           input[i] = input[i-1] + input[i-2]
           input[i] = c_uint(input[i]).value
       return hex(input[n])

    个人遇到的最大问题是python中的变量,如何转换为 c 语言类型。

    使用 ctype 类,文档中给出了各种数据类型的关键字:

    ctypes typeC typePython type
    c_bool_Boolbool (1)
    c_charchar1-character string
    c_wcharwchar_t1-character unicode string
    c_bytecharint/long
    c_ubyteunsigned charint/long
    c_shortshortint/long
    c_ushortunsigned shortint/long
    c_intintint/long
    c_uintunsigned intint/long
    c_longlongint/long
    c_ulongunsigned longint/long
    c_longlong__int64 or long longint/long
    c_ulonglongunsigned __int64 or unsigned long longint/long
    c_floatfloatfloat
    c_doubledoublefloat
    c_longdoublelong doublefloat
    c_char_pchar * (NUL terminated)string or None
    c_wchar_pwchar_t * (NUL terminated)unicode or None
    c_void_pvoid *int/long or None

    本题需要将结果转换为 unsigned long,所以选择 c_uint 即可。注意一点,这里只做了类型转换,还需要提取value,否则系统会提示操作符类型不一致。

    image-20200504103917657

    接下来将 calculate_key 中的 call fib => mov eax, 5D023A41

    image-20200504103544030

    运行patch之后的程序,得到flag

    image-20200504103701101

    be-quick-or-be-dead-3

    相比第二题,第三题仅仅是生成key的算法有区别:

    image-20200504104141214

    很明显,还是个递归算法导致运行超时。根据前面的经验,直接写出T(N)的算法:

    from ctypes import *
    def quick3(n):
       input = {}
       for i in range(5):
           input[i] = i*i + 9029
       for i in range(5, n + 1):
           v2 = input[i-1] - input[i-2]
           v4 = input[i-3] - input[i-4] + v2
           input[i] = v4 + 4660 * input[i-5]
           input[i] = c_uint(input[i]).value

       return hex(input[n])

    print quick3(0x186B5)  ##0x221d8eea

    在同样的位置进行patch,运行patch之后的程序,得到flag:

    Be Quick Or Be Dead 3
    =====================

    Calculating key...
    Done calculating key
    Printing flag:
    picoCTF{dynamic_pr0gramming_ftw_a0b0b7f8}

    至此,三道题目完成。

    小结

    通过三个习题新学习到以下内容

    • 如何利用 ida 的 keypatch 插件对程序进行patch
    • 如何利用ctypes进行数据类型转换
    • 加入 #encoding=utf-8 后,才能写中文注释
    • 程序的超时机制。例如这里是 3s 之后 alarm,并调用 alarm_handler 函数
    unsigned int set_timer()
    {
     if ( __sysv_signal(14, alarm_handler) == (__sighandler_t)-1LL )
    {
       printf(
         " Something went terribly wrong. Please contact the admins with "be-quick-or-be-dead-3.c:%d". ",
         59LL);
       exit(0);
    }
     return alarm(3u);
    }

  • 相关阅读:
    float保留指定位数的小数
    springmvc中拦截器的使用
    springmvc文件上传
    spring注入
    mybatis动态代理
    2017《JAVA技术》预备作业02 计科1502 郎春雨
    2017《JAVA技术》预备作业01 计科1502 郎春雨
    字符串占位符的使用
    Pyenv虚拟环境的创建(虚拟机)
    Git的基本使用
  • 原文地址:https://www.cnblogs.com/handt/p/12826212.html
Copyright © 2011-2022 走看看