zoukankan      html  css  js  c++  java
  • 2017年全国大学生信息安全竞赛--填数游戏

    测试文件:http://static2.ichunqiu.com/icq/resources/fileupload/CTF/echunqiu/qgdxs/numgame_8808BCE6D17A3EF92461A50079264767.zip

    1.准备

    获取信息

    • 32位文件

    2.IDA打开

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      std::string *v4; // [esp-18h] [ebp-1B8h]
      int (*v5)[9]; // [esp-14h] [ebp-1B4h]
      struct _Unwind_Exception *lpuexcpt; // [esp+0h] [ebp-1A0h]
      struct SjLj_Function_Context fctx; // [esp+4h] [ebp-19Ch]
      void *v8; // [esp+28h] [ebp-178h]
      std::string **v9; // [esp+2Ch] [ebp-174h]
      char v10; // [esp+40h] [ebp-160h]
      int v11; // [esp+184h] [ebp-1Ch]
      char v12; // [esp+188h] [ebp-18h]
      int *v13; // [esp+190h] [ebp-10h]
    
      v13 = &argc;
      fctx.personality = (_Unwind_Personality_Fn)__gxx_personality_sj0;
      fctx.lsda = dword_47CAB8;
      fctx.jbuf[0] = &v12;
      v8 = &loc_4015A5;
      v9 = &v4;
      _Unwind_SjLj_Register(&fctx);
      __main();
      Sudu::Sudu(&v10);                             // v10中填充324个0
      Sudu::set_data((int)&v10, (Sudu *)&_data_start__, v5);// 初始化数据,进入函数知道_data_start__应该是两字为一组,即4字节为一组。
      fctx.call_site = -1;
      std::string::string(&v11);
      fctx.call_site = 1;
      std::operator>><char,std::char_traits<char>,std::allocator<char>>((std::istream::sentry *)&std::cin, &v11);
      if ( (unsigned __int8)set_sudu((Sudu *)&v10, (const std::string *)&v11) ^ 1 )// 需要函数返回1
      {
        std::operator<<<std::char_traits<char>>((std::ostream::sentry *)&std::cout, "fail");
        std::ostream::operator<<(std::endl<char,std::char_traits<char>>);
        lpuexcpt = 0;
      }
      else
      {
        if ( Sudu::check((Sudu *)&v10) )            // 需要函数返回1
        {
          fctx.call_site = 1;
          std::operator<<<std::char_traits<char>>((std::ostream::sentry *)&std::cout, "success");
        }
        else
        {
          fctx.call_site = 1;
          std::operator<<<std::char_traits<char>>((std::ostream::sentry *)&std::cout, "fail");
        }
        std::ostream::operator<<(std::endl<char,std::char_traits<char>>);
        lpuexcpt = 0;
      }
      std::string::~string(v4);
      _Unwind_SjLj_Unregister(&fctx);
      return (int)lpuexcpt;
    }

    3.代码分析

    Sudu::set_data((int)&v10, (Sudu *)&_data_start__, v5);函数

    int __userpurge Sudu::set_data@<eax>(int a1@<ecx>, Sudu *this, int (*a3)[9])
    {
      int result; // eax
      signed int j; // [esp+Ch] [ebp-Ch]
      signed int i; // [esp+10h] [ebp-8h]
    
      for ( i = 0; i <= 8; ++i )                    // 9行9列数据
      {
        for ( j = 0; j <= 8; ++j )
        {
          result = j + 9 * i;                       // i+1行j+1列的坐标
          *(_DWORD *)(a1 + 4 * result) = *((_DWORD *)this + 9 * i + j);// 可以计算得到al
        }
      }
      return result;
    }

    使用脚本计算v10矩阵

    origin = [
    0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 5, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 7, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
    4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
    0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
    1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0,
    2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 5, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0,
    5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0,
    9, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0,
    7, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 1, 0, 0, 0,
    2, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0,
    9, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0,
    4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0,
    3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0,
    7, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 1, 0, 0, 0,
    5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0,
    9, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0,
    4, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 1, 0, 0, 0,
    2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0,
    6, 0, 0, 0, 7, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0,
    5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0,
    9, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0,
    7, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 1, 0, 0, 0,
    2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0,
    9, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0,
    4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0,
    8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    ]
    
    key = []
    
    for i in range(len(origin)//4):
            key.append(origin[i*4])
    for i in range(9):
        for j in range(9):
            print(key[j+i*9], end=' ')
        print('')
    script
    0 0 7 5 0 0 0 6 0 
    0 2 0 0 1 0 0 0 7 
    9 0 0 0 3 0 4 0 0 
    2 0 1 0 0 0 0 0 0 
    0 3 0 1 0 0 0 0 5 
    0 0 0 0 0 0 7 1 0 
    4 0 0 0 0 8 2 0 0 
    0 0 5 9 0 0 0 8 0 
    0 8 0 0 0 1 0 0 3 

    第一处判断条件

    if ( (unsigned __int8)set_sudu((Sudu *)&v10, (const std::string *)&v11) ^ 1 )// 需要函数返回1

    打开函数

    signed int __cdecl set_sudu(Sudu *a1, const std::string *a2)
    {
      std::string *v2; // ST00_4
      std::string *v4; // [esp+0h] [ebp-38h]
      int v5; // [esp+Ch] [ebp-2Ch]
      int v6; // [esp+1Ch] [ebp-1Ch]
      int v7; // [esp+20h] [ebp-18h]
      char v8; // [esp+27h] [ebp-11h]
      const std::string *v9; // [esp+28h] [ebp-10h]
      int v10; // [esp+2Ch] [ebp-Ch]
    
      v10 = 0;
      v9 = a2;
      v7 = std::string::begin(v4);                  // 输入字符串的起始字符
      v6 = std::string::end(v2);                    // 输入字符串的结束符
      while ( __gnu_cxx::operator!=<char const*,std::string>((int)&v7, (int)&v6) )// 遍历整个字符串
      {
        v8 = *(_BYTE *)__gnu_cxx::__normal_iterator<char const*,std::string>::operator*(&v7);// v8=*v7,即遍历指针v7指向的值
        if ( (unsigned __int8)Sudu::set_number((int)a1, (Sudu *)(v10 / 9), v10 % 9, v8 - 48, v5) ^ 1 )// 第一个参数a1为v10矩阵
                                                    // 第二个参数表示v10的行坐标
                                                    // 第三个参数表示v10的列坐标
                                                    // 第四个参数表示将输入的字符数字转为整型数字
                                                    // 第五个参数表示?
          return 0;
        ++v10;
        __gnu_cxx::__normal_iterator<char const*,std::string>::operator++(&v7);
      }
      return 1;
    }

    查看(unsigned __int8)Sudu::set_number((int)a1, (Sudu *)(v10 / 9), v10 % 9, v8 - 48, v5)

    signed int __userpurge Sudu::set_number@<eax>(int a1@<ecx>, Sudu *this, int a3, int a4, int a5)
    {
      if ( !a4 )
        return 1;
      if ( (signed int)this < 0
        || (signed int)this > 8
        || a3 < 0
        || a3 > 8
        || *(_DWORD *)(a1 + 4 * (a3 + 9 * (_DWORD)this))// 判断a1[*this][a3]是否为0
        || a4 <= 0
        || a4 > 9 )                                 // 需要这里的条件不成立
      {
        return 0;
      }
      *(_DWORD *)(a1 + 4 * (9 * (_DWORD)this + a3)) = a4;// a1[*this][a3]=a4,将输入的非0数字写入到矩阵中
      return 1;
    }

    返回主函数

        if ( Sudu::check((Sudu *)&v10) )            // 需要函数返回1
        {
          fctx.call_site = 1;
          std::operator<<<std::char_traits<char>>((std::ostream::sentry *)&std::cout, "success");
        }

    查看Sudu::check((Sudu *)&v10)

    bool __fastcall Sudu::check(Sudu *a1)
    {
      Sudu *v2; // [esp+0h] [ebp-4h]
    
      v2 = a1;
      return (unsigned __int8)Sudu::check_block((int)a1)
          && (unsigned __int8)Sudu::check_col((int)v2)
          && (unsigned __int8)Sudu::check_row((int)v2);
    }

    打开(unsigned __int8)Sudu::check_block((int)a1)

    signed int __fastcall Sudu::check_block(int a1)
    {
      char v2[10]; // [esp+12h] [ebp-26h]
      int v3; // [esp+1Ch] [ebp-1Ch]
      int v4; // [esp+20h] [ebp-18h]
      int l; // [esp+24h] [ebp-14h]
      int k; // [esp+28h] [ebp-10h]
      int j; // [esp+2Ch] [ebp-Ch]
      int i; // [esp+30h] [ebp-8h]
    
      for ( i = 0; i <= 8; ++i )
      {
        for ( j = 1; j <= 9; ++j )
          v2[j] = 1;
        for ( k = 0; k <= 8; ++k )
        {
          v4 = 3 * (i / 3) + k / 3;
          v3 = 3 * (i % 3) + k % 3;                 // 观察一组v4,v3的取值(v4表示矩阵横坐标,v3表示纵坐标)
                                                    // 0 0 (0,0)
                                                    // 0 1 (0,1)
                                                    // 0 2 (0,2)
                                                    // 0 3 (1,0)
                                                    // 0 4 (1,1)
                                                    // 0 5 (1,2)
                                                    // 0 6 (2,0)
                                                    // 0 7 (2,1)
                                                    // 0 8 (2,2)
                                                    // 这是9x9矩阵中,左上的3x3矩阵
          v2[*(_DWORD *)(a1 + 4 * (v3 + 9 * v4))] = 0;// 将矩阵9个值,分别将v2数组的9个值赋值为0,如果3x3矩阵有重复数字,则必有v2[l]=1
        }
        for ( l = 1; l <= 9; ++l )
        {
          if ( v2[l] )                              // 要返回1,因此需要v2[1]=0。则每个3x3的矩阵中分布着1~9的数字,且没有重复。
                                                    // 这是数独!!!
            return 0;
        }
      }
      return 1;
    }

    总结:实际上这就是一个解v10矩阵的数独游戏,直接网上在线解9x9矩阵的数独就行。(仔细看一下函数前面带的Sudu::就是数独(虽然正确是Shudu,哈哈哈~~~))

    将填入的数字写出来,已有的数字用0表示,在代码检测中将跳过0.

    4.get flag!

    flag{340089102508406930016207058060875349709064820854392006093650071170023604602740590}

  • 相关阅读:
    基于TFTP协议的远程升级设计
    BZOJ 刷题记录 PART 6
    解决org.hibernate.LazyInitializationException: could not initialize proxy
    在不同版本号hdfs集群之间转移数据
    从零開始制作H5应用(4)——V4.0,增加文字并给文字加特效
    不再安全的 OSSpinLock
    @synchronized 再考察
    ReactiveCocoa
    怎样界定问题
    问题是什么
  • 原文地址:https://www.cnblogs.com/Mayfly-nymph/p/11723672.html
Copyright © 2011-2022 走看看