2017年全國大學生信息安全競賽--填數遊戲

測試文件:http://static2.ichunqiu.com/icq/resources/fileupload/CTF/echunqiu/qgdxs/numgame_8808BCE6D17A3EF92461A50079264767.zip數組

 

1.準備

獲取信息app

  • 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);函數ide

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

打開函數this

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)spa

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)code

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)orm

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}

相關文章
相關標籤/搜索