REVERSE-PRACTICE-BUUCTF-13

firmware

.bin(二进制)文件,由题目提示知是路由器固件逆向
参考:逆向路由器固件之解包 Part1
linux安装好binwalk和firmware-mod-kit
binwalk会分析二进制文件中可能的固件头或者文件系统,然后输出识别出的每个部分以及对应的偏移量
binwalk该二进制文件,发现包含了squashfs文件系统(squashfs是linux下的一种只读压缩文件系统类型)
firmware-binwalk
binwalk -e 该二进制文件,提取出各部分数据到各个文件,相当于解压固件,解压出来的文件放在当前目录的/_firmware.bin.extracted文件夹下(注意此时的suqashfs-root文件夹是否为空)
firmware-binwalk-e
使用firmware-mod-kit解包提取出来的squashfs文件
/*******************************************************
firmware-mod-kit的一些用法:
extract-firmware.sh 解包固件
build-firmware.sh 重新封包
check_for_upgrade.sh 检查更新
unsquashfs_all.sh 解包提取出来的squashfs文件
*******************************************************/
firmware-fmk
解包squashfs文件出来的文件放在了当前目录的squashfs-root-1文件夹中
(实际上,在本人虚拟机binwalk -e 该二进制文件后,生成文件夹/_firmware.bin.extracted中的squashfs-root文件夹,已经是解包squashfs文件出来的文件夹了,即squashfs-root文件夹和squashfs-root-1文件夹中包含的文件是相同的,如果squashfs-root文件夹下内容为空,是由于sasquatch安装有问题导致的,可以通过重新安装sasquatch解决,参考:dir815_FW_102.bin路由器固件解压碰到的坑
firmware-fmk-res
在/_firmware.bin.extracted/squashfs-root/tmp目录或/_firmware.bin.extracted/squashfs-root-1/tmp目录下有一个backdoor文件
backdoor查壳发现有upx壳,脱壳后拖入ida分析
在字符串窗口找到网址
firmware-address
交叉引用网址,在initConnection函数中找到端口
firmware-port

[ACTF新生赛2020]Oruga

elf文件,无壳,ida分析
main函数逻辑清晰,获取输入,检验输入是否为“actf{”开头,验证输入内容,以及最后一个字符是否为“}”
oruga-logic
进入check函数,分析可知是一个16x16的迷宫,和常规迷宫的按一次方向键则往该方向前进一格的机制不同,该迷宫的前进机制为,按一次方向键,则往该方向一直前进,直到撞到墙,具体分析知道,在map为零的位置时,可以持续前进,到达第一个非零的位置就停下来,然后减一个步长回到前一个零的位置,再读input的内容更新步长,更新步长实际上就是换方向

_BOOL8 __fastcall check(__int64 input)
{
  int index; // [rsp+Ch] [rbp-Ch]
  signed int input_index; // [rsp+10h] [rbp-8h]
  signed int step; // [rsp+14h] [rbp-4h]

  index = 0;
  input_index = 5;
  step = 0;
  while ( map[index] != '!' )                   // 终点为"!"
  {
    index -= step;                              // 由第48行代码可知,由于index叠加走到了非零的位置,这时需要减一个步长,回到前一个零位置
    if ( *(_BYTE *)(input_index + input) != 'W' || step == -16 )// 由input的内容决定步长step
    {
      if ( *(_BYTE *)(input_index + input) != 'E' || step == 1 )
      {
        if ( *(_BYTE *)(input_index + input) != 'M' || step == 16 )
        {
          if ( *(_BYTE *)(input_index + input) != 'J' || step == -1 )
            return 0LL;
          step = -1;                            // J-左
        }
        else
        {
          step = 16;                            // M-下
        }
      }
      else
      {
        step = 1;                               // E-右
      }
    }
    else
    {
      step = -16;                               // W-上
    }
    ++input_index;                              // 读下一个input的字符
    while ( !map[index] )                       // 该while循环体只能在map[index]=='0'时执行
    {
      if ( step == -1 && !(index & 0xF) )       // 判断边界的四个if
        return 0LL;
      if ( step == 1 && index % 16 == 15 )
        return 0LL;
      if ( step == 16 && (unsigned int)(index - 240) <= 0xF )
        return 0LL;
      if ( step == -16 && (unsigned int)(index + 15) <= 0x1E )
        return 0LL;
      index += step;                            // index按照当前的步长循环叠加,即按下一个方向键,就会沿着这个方向一直走,直到第一个非零的位置停下
    }
  }
  return *(_BYTE *)(input_index + input) == '}';
}

把map提取出来,制成16x16的迷宫,按照前进机制走完迷宫即可,起始点为左上角的[0,0],终止点为“!”(0x21),W-上,M-下,J-左,E-右,路线即为flag
oruga-flag

[Zer0pts2020]easy strcmp

elf文件,无壳,ida分析
main函数,有一个比较字符串的if语句决定输出的内容,其他什么也没有
easystrcmp-logic
来到start函数,发现在调用main函数前,先调用了fini函数和init函数
easystrcmp-start
fini函数直接返回了,分析init函数,可以知道,在调用main函数前,程序将.init_array段地址到.fini_array段地址之间的函数全部执行一遍,命令行参数作为段之间函数的参数
easystrcmp-init
这里可以看到,.init_array段和.fini_array段之间有3个函数,依次分析知道,重要的是sub_795函数
easystrcmp-segfunc
sub_795->sub_6EA,分析sub_6EA函数,计算输入的长度,将输入的内容顺序地与qword_201060数组的元素相减,然后去到main函数和那段字符串比较
easystrcmp-sub_6EA
写脚本即可得到flag
easystrcmp-script

[GXYCTF2019]simple CPP

exe程序,运行后提示输入flag,输入错误退出程序,无壳,ida分析
交叉引用字符串来到sub_140001290函数

__int64 sub_140001290()
{
  bool v0; // si
  __int64 v1; // rax
  __int64 v2; // r8
  unsigned __int8 *v3; // rax
  unsigned __int8 *v4; // rbx
  int v5; // er10
  __int64 v6; // r11
  _BYTE *input_copy; // r9
  void **v8; // r8
  __int64 arr[3]; // rdi
  __int64 arr[2]; // r15
  __int64 arr[1]; // r12
  __int64 arr[0]; // rbp
  signed int v13; // ecx
  unsigned __int8 *v14; // rdx
  __int64 v15; // rdi
  __int64 *v16; // r14
  __int64 v17; // rbp
  __int64 v18; // r13
  _QWORD *v19; // rdi
  __int64 v20; // r12
  __int64 v21; // r15
  __int64 v22; // rbp
  __int64 v23; // rdx
  __int64 v24; // rbp
  __int64 v25; // rbp
  __int64 v26; // r10
  __int64 v27; // rdi
  __int64 v28; // r8
  bool v29; // dl
  __int64 v30; // rax
  void *v31; // rdx
  const char *v32; // rax
  __int64 v33; // rax
  _BYTE *v34; // rcx
  __int64 v36; // [rsp+20h] [rbp-68h]
  void *input; // [rsp+30h] [rbp-58h]
  unsigned __int64 input_len; // [rsp+40h] [rbp-48h]
  unsigned __int64 v39; // [rsp+48h] [rbp-40h]

  v0 = 0;
  input_len = 0i64;
  v39 = 15i64;
  LOBYTE(input) = 0;
  LODWORD(v1) = printf(std::cout, "I'm a first timer of Logic algebra , how about you?");
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v1, sub_140001B90);
  printf(std::cout, "Let's start our game,Please input your flag:");
  sub_140001DE0(std::cin, &input, v2);          // 读取input
  std::basic_ostream<char,std::char_traits<char>>::operator<<(std::cout, sub_140001B90);
  if ( input_len - 5 > 25 )                     // input不需要GXY{}包住
  {
    LODWORD(v33) = printf(std::cout, "Wrong input ,no GXY{} in input words");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v33, sub_140001B90);
    goto LABEL_45;
  }
  v3 = sub_1400024C8(32ui64);                   // 开辟一块大小为32的,元素类型为unsigned int8的地址空间给v3
  v4 = v3;
  if ( v3 )                                     // 新开辟的地址空间全部赋0值
  {
    *v3 = 0i64;
    *(v3 + 1) = 0i64;
    *(v3 + 2) = 0i64;
    *(v3 + 3) = 0i64;
  }
  else
  {
    v4 = 0i64;
  }
  v5 = 0;
  if ( input_len > 0 )
  {
    v6 = 0i64;
    do
    {
      input_copy = &input;
      if ( v39 >= 16 )
        input_copy = input;
      v8 = &Dst;
      if ( qword_140006060 >= 0x10 )
        v8 = Dst;                               // Dst="i_will_check_is_debug_or_not",长度为28
      v4[v6] = input_copy[v6] ^ *(v8 + v5++ % 27);// input和Dst异或,结果放入v4
      ++v6;
    }
    while ( v5 < input_len );
  }
  arr[3] = 0i64;
  arr[2] = 0i64;
  arr[1] = 0i64;
  arr[0] = 0i64;
  if ( input_len > 30 )                         // input长度验证
    goto LABEL_28;
  v13 = 0;
  if ( input_len <= 0 )
    goto LABEL_28;
  v14 = v4;                                     // v14=v4,是input和Dst异或的结果
  do                                            // do循环体,实际上是把v14分成四段,前三段长度为8,分别放入v12,v11,v10中,剩下的放在v9
                                                // 依次记为arr[0],arr[1],arr[2],arr[3],重要的是算出arr[0~3]
  {
    v15 = *v14 + arr[3];
    ++v13;
    ++v14;
    switch ( v13 )
    {
      case 8:
        arr[0] = v15;
        goto LABEL_24;
      case 16:
        arr[1] = v15;
        goto LABEL_24;
      case 24:
        arr[2] = v15;
LABEL_24:
        v15 = 0i64;
        break;
      case 32:
        printf(std::cout, "ERRO,out of range");
        exit(1);
        break;
    }
    arr[3] = v15 << 8;
  }
  while ( v13 < input_len );
  if ( arr[0] )
  {
    v16 = sub_1400024C8(32ui64);
    *v16 = arr[0];                              // arr[0~3]放入v16[0~3]中
    v16[1] = arr[1];
    v16[2] = arr[2];
    v16[3] = arr[3];
    goto LABEL_29;
  }
LABEL_28:
  v16 = 0i64;
LABEL_29:
  v36 = v16[2];                                 // v36=arr[2]
  v17 = v16[1];                                 // v17=arr[1]
  v18 = *v16;                                   // v18=arr[0]
  v19 = sub_14000223C(32ui64);                  // 开辟一块大小为32的,元素类型为unsigned int8的地址空间给v19
  if ( IsDebuggerPresent() )                    // 反调试
  {
    printf(std::cout, "Hi , DO not debug me !");
    Sleep(0x7D0u);
    exit(0);
  }
  v20 = v17 & v18;                              // v20=arr[1]&arr[0]
  *v19 = v17 & v18;                             // v19[0]=arr[1]&arr[0]
  v21 = v36 & ~v18;                             // v21=arr[2]&(~arr[0])
  v19[1] = v21;                                 // v19[1]=arr[2]&(~arr[0])
  v22 = ~v17;                                   // v22=~arr[1]
  v23 = v36 & v22;                              // v23=arr[2]&(~arr[1])
  v19[2] = v36 & v22;                           // v19[2]=arr[2]&(~arr[1])
  v24 = v18 & v22;                              // v24=arr[0]&(~arr[1])
  v19[3] = v24;                                 // v19[3]=arr[0]&(~arr[1])
  if ( v21 != 0x11204161012i64 )                // 验证v19[1]==0x11204161012,即arr[2]&(~arr[0])==0x11204161012
  {
    v19[1] = 0i64;
    v21 = 0i64;
  }
  v25 = v21 | v20 | v23 | v24;                  // 需v25==0x3E3A4717373E7F1F成立,即(arr[2]&(~arr[0])) | (arr[1]&arr[0]) | (arr[2]&(~arr[1])) | (arr[0]&(~arr[1]))==0x3E3A4717373E7F1F
  v26 = v16[1];                                 // v26=arr[1]
  v27 = v16[2];                                 // v27=arr[2]
  v28 = v23 & *v16 | v27 & (v20 | v26 & ~*v16 | ~(v26 | *v16));// (arr[2]&(~arr[1])) & (arr[0]) | (arr[2]) & ((arr[1]&arr[0]) | (arr[1]) & ~(arr[0]) | ~((arr[1]) | (arr[0])))==0x8020717153E3013

  v29 = 0;
  if ( v28 == 0x8020717153E3013i64 )            // 由第171行代码可知,v0需要为1,v28==0x8020717153E3013成立
    v29 = v25 == 0x3E3A4717373E7F1Fi64;         // 验证v25==0x3E3A4717373E7F1F,相等返回1,不等返回0
  if ( (v25 ^ v16[3]) == 0x3E3A4717050F791Fi64 )// v16[3]=arr[3],即arr[3]^0x3E3A4717373E7F1F==0x3E3A4717050F791F
    v0 = v29;                                   // v0为v25==0x3E3A4717373E7F1F的返回值,等式成立返回1,不等返回0
  if ( (v21 | v20 | v26 & v27) != (~*v16 & v27 | 0xC00020130082C0Ci64) || v0 != 1 )// 需要v0为1,意味着v25==0x3E3A4717373E7F1F成立且((arr[2]&(~arr[0])) |(arr[1]&arr[0]) | (arr[1]) & (arr[2])) == (~(arr[0])& (arr[2]) | 0xC00020130082C0C)
  {
    printf(std::cout, "Wrong answer!try again");
    j_j_free(v4);
  }
  else
  {
    LODWORD(v30) = printf(std::cout, "Congratulations!flag is GXY{");// input不需要GXY{}包住
    v31 = &input;
    if ( v39 >= 16 )
      v31 = input;
    v32 = sub_140001FD0(v30, v31, input_len);
    printf(v32, "}");
    j_j_free(v4);
  }
LABEL_45:
  if ( v39 >= 0x10 )
  {
    v34 = input;
    if ( v39 + 1 >= 0x1000 )
    {
      v34 = *(input - 1);
      if ( (input - v34 - 8) > 0x1F )
        invalid_parameter_noinfo_noreturn();
    }
    j_j_free(v34);
  }
  return 0i64;
}

写解arr[0~3]的脚本
simplecpp-script
写逆异或运算得到flag的脚本,由flag的明文字符串可知,arr[1]的结果错误,原因是原方程组有多组解,参考别的师傅的wp,比赛给出了第二部分的flag,e!P0or_a,替换掉错误的8个字符,结果为We1l_D0ne!P0or_algebra_am_i
simplecpp-script

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

P1umH0

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

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

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

打赏作者

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

抵扣说明:

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

余额充值