2017年全国大学生信息安全竞赛--填数游戏

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

1.准备 2017年全国大学生信息安全竞赛--填数游戏
文章图片

获取信息

  • 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个0Sudu::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>>,std::allocator>((std::istream::sentry *)&std::cin, &v11); if ( (unsigned __int8)set_sudu((Sudu *)&v10, (const std::string *)&v11) ^ 1 )// 需要函数返回1{std::operator<>((std::ostream::sentry *)&std::cout, "fail"); std::ostream::operator<<(std::endl>); lpuexcpt = 0; }else{if ( Sudu::check((Sudu *)&v10) )// 需要函数返回1{fctx.call_site = 1; std::operator<>((std::ostream::sentry *)&std::cout, "success"); }else{fctx.call_site = 1; std::operator<>((std::ostream::sentry *)&std::cout, "fail"); }std::ostream::operator<<(std::endl>); 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@(int a1@, Sudu *this, int (*a3)[9]){int result; // eaxsigned 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矩阵
2017年全国大学生信息安全竞赛--填数游戏
文章图片
2017年全国大学生信息安全竞赛--填数游戏
文章图片
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_4std::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!=((int)&v7, (int)&v6) )// 遍历整个字符串{v8 = *(_BYTE *)__gnu_cxx::__normal_iterator::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::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@(int a1@, 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::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,哈哈哈~~~))
2017年全国大学生信息安全竞赛--填数游戏
文章图片


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

4.get flag!
【2017年全国大学生信息安全竞赛--填数游戏】flag{340089102508406930016207058060875349709064820854392006093650071170023604602740590}

    推荐阅读