AVALON 南京邮电大学网络攻防训练平台 南京邮电大学网络攻防训练平台 RE方向 第十二题 Single

南京邮电大学网络攻防训练平台 RE方向 第十二题 Single

环境配置

系统 : Windows xp
程序 : single
要求 : 输入口令
使用工具 :IDA / 数独在线求解

开始分析

在ida中载入程序按f5并查看伪c代码:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 result; // rax@1
  __int64 v4; // rcx@1
  char *str; // [sp+0h] [bp-70h]@1
  __int64 v6; // [sp+68h] [bp-8h]@1

  v6 = *MK_FP(__FS__, 40LL);
  memset(&str, 0, 0x64uLL);
  puts("Input string:");
  scanf("%s", &str);
  format_control_40070E((const char *)&str);
  insert_40078B((const char *)&str, (__int64)byte_602080);
  check_400AD4((__int64)byte_602080);
  puts("Congratulations!");
  printf("flag{%s}", &str);
  result = 0LL;
  v4 = *MK_FP(__FS__, 40LL) ^ v6;
  return result;
}

看看负责格式控制的函数:

size_t __fastcall format_control_40070E(const char *str)
{
  size_t my_str; // rax@4
  int index; // [sp+1Ch] [bp-14h]@3

  if ( strlen(str) > 0x51 )
    GG_4006F6();
  for ( index = 0; ; ++index )
  {
    my_str = strlen(str);
    if ( index >= my_str )
      break;
    if ( str[index] <= 0x2F || str[index] > '9' )
      GG_4006F6();
  }
  return my_str;
}

输入数据不能大于0x51(81),并且字符范围是0-9否则调用gg函数。
而第二个函数负责插入数据:

size_t __fastcall insert_40078B(const char *my_str, __int64 a2)
{
  size_t len_mystr; // rax@2
  int index; // [sp+1Ch] [bp-14h]@1

  for ( index = 0; ; ++index )
  {
    len_mystr = strlen(my_str);
    if ( index >= len_mystr )
      break;
    if ( my_str[index] != 0x30 )
    {
      if ( !my_str[index] || *(_BYTE *)(index + a2) )
        GG_4006F6();
      *(_BYTE *)(index + a2) = my_str[index] - '0';
    }
  }
  return len_mystr;
}

这里,当数据不为0时,程序向固定表为0的同样位置插入数据:


0, 3, 0, 6, 0, 0, 0, 0, 0, 6, 0, 0, 0, 3, 2, 4, 9, 0, 0, 9, 0, 1, 0 7, 0, 6, 0, 7, 4, 6, 0, 0, 0, 0, 0, 0, 0, 1, 8, 0, 0, 0, 6, 3, 0, 0 0, 0, 0, 0, 0, 1, 4, 7, 0, 8, 0, 9, 0, 4, 0, 7, 0, 0, 7, 4, 2, 1, 0 0, 0, 6, 0, 0, 0, 0, 0, 3, 0, 1, 0

查看三个check函数:

__int64 __fastcall sub_400833(__int64 arr)
{
  signed int index_i; // [sp+18h] [bp-28h]@1
  signed int index_j; // [sp+1Ch] [bp-24h]@3
  signed int index_k; // [sp+1Ch] [bp-24h]@6
  char s[24]; // [sp+20h] [bp-20h]@3
  __int64 v6; // [sp+38h] [bp-8h]@1

  v6 = *MK_FP(__FS__, 40LL);
  for ( index_i = 0; index_i <= 8; ++index_i )
  {
    memset(s, 0, 10uLL);
    for ( index_j = 0; index_j <= 8; ++index_j )
      ++s[*(_BYTE *)(9 * index_i + index_j + arr)];
    for ( index_k = 1; index_k <= 9; ++index_k )
    {
      if ( s[index_k] != 1 )
        GG_4006F6();
    }
  }
  return *MK_FP(__FS__, 40LL) ^ v6;
}
__int64 __fastcall sub_4008FE(__int64 arr)
{
  signed int i; // [sp+18h] [bp-28h]@1
  signed int j; // [sp+1Ch] [bp-24h]@3
  signed int k; // [sp+1Ch] [bp-24h]@6
  char s[24]; // [sp+20h] [bp-20h]@3
  __int64 v6; // [sp+38h] [bp-8h]@1

  v6 = *MK_FP(__FS__, 40LL);
  for ( i = 0; i <= 8; ++i )
  {
    memset(s, 0, 0xAuLL);
    for ( j = 0; j <= 8; ++j )
      ++s[*(_BYTE *)(9 * j + i + arr)];
    for ( k = 1; k <= 9; ++k )
    {
      if ( s[k] != 1 )
        GG_4006F6();
    }
  }
  return *MK_FP(__FS__, 40LL) ^ v6;
}
__int64 __fastcall sub_4009C9(__int64 arr)
{
  signed int i; // [sp+1Ch] [bp-34h]@1
  int j; // [sp+20h] [bp-30h]@3
  signed int l; // [sp+20h] [bp-30h]@9
  int k; // [sp+24h] [bp-2Ch]@5
  signed int v6; // [sp+28h] [bp-28h]@1
  signed int v7; // [sp+2Ch] [bp-24h]@1
  char s[24]; // [sp+30h] [bp-20h]@3
  __int64 v9; // [sp+48h] [bp-8h]@1

  v9 = *MK_FP(__FS__, 40LL);
  v6 = 3;
  v7 = 3;
  for ( i = 0; i <= 8; ++i )
  {
    memset(s, 0, 0xAuLL);
    for ( j = v6 - 3; j < v6; ++j )
    {
      for ( k = v7 - 3; k < v7; ++k )
        ++s[*(_BYTE *)(9 * j + k + arr)];
    }
    for ( l = 1; l <= 9; ++l )
    {
      if ( s[l] != 1 )
        GG_4006F6();
    }
    if ( v7 == 9 )
    {
      v7 = 3;
      v6 += 3;
    }
    else
    {
      v7 += 3;
    }
  }
  return *MK_FP(__FS__, 40LL) ^ v9;
}

逻辑推理

程序需要我们输入81个字符,对应程序中9*9的固定表,需要填写表格中不为0的地方。而check函数检查表的每一行、列、以及程序中9个3*3的小表格中的数据。若都是由1-9的字符组成并且不重复,则夺旗成功。

所以这就是一道数独题目,正好对上了题目single(误,其实数独的英文是sudoku

解密

其实这里就是做九宫格游戏了,我填了一小半,然后没耐心直接放入在线解密网站跑,很快就出了结果。

夺旗成功

输入相关数据,提示成功

root@kali:~/gu_bing_reverse/my_first_mission# ./single
Input string:
401095728057800001802040305000321589500479002923586000105060203300008950269750804
Congratulations!
flag{401095728057800001802040305000321589500479002923586000105060203300008950269750804}

后记

学习了数独的有关知识,又重新动笔做了一遍题目:

第一幅图是正常思路解出的效果,此时我们发现,九宫中只有一个5,而中宫其实是比较空的。这样很难猜出下一步怎么填,所以不妨假设第一宫的数据,然后根据假设开始推导,填写。如果最后结果吻合,则为正确解答。