首页
社区
课程
招聘
[原创]看雪.纽盾 KCTF晋级赛2019 Q2 第四题 达芬奇密码
发表于: 2019-6-18 16:01 3381

[原创]看雪.纽盾 KCTF晋级赛2019 Q2 第四题 达芬奇密码

2019-6-18 16:01
3381

运行程序,随便输入点什么,信息框提示“Wrong!”,在MessageBoxW下断点,回溯就能定位到点击事件了。

int __thiscall click(CWnd *this)
{
  CWnd *v1; // esi
  int v2; // eax
  WCHAR String; // [esp+Ch] [ebp-310h]
  char v5; // [esp+Eh] [ebp-30Eh]
  char input; // [esp+20Ch] [ebp-110h]
  char v7; // [esp+20Dh] [ebp-10Fh]
  DWORD v8; // [esp+30Ch] [ebp-10h]
  CWnd *v9; // [esp+310h] [ebp-Ch]
  int v10; // [esp+314h] [ebp-8h]
  DWORD flOldProtect; // [esp+318h] [ebp-4h]

  v1 = this;
  v9 = this;
  String = 0;
  memset(&v5, 0, 0x1FEu);
  input = 0;
  memset(&v7, 0, 0xFFu);
  CWnd::GetDlgItemTextW(v1, 1000, &String, 20);
  if ( wcslen(&String) == 16 )                  // key的长度为16
  {
    v2 = 0;
    while ( !(*(&String + v2) & 0xFF00) )       // unicode转ascii
    {
      *(&input + v2) = *((_BYTE *)&String + 2 * v2);
      if ( ++v2 >= 16 )
      {
        v8 = 64;
        flOldProtect = 0;
        VirtualProtect(check, 0xD17u, 0x40u, &flOldProtect);
        if ( GetLastError() )
          return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
        qmemcpy(check, byte_11C47B8, 0x330u);   // 修改check函数
        VirtualProtect(check, 0xD17u, flOldProtect, &v8);
        if ( !GetLastError() )
        {
          v10 = 0;
          v10 = check(&input);                  // 调用
          if ( v10 == 1 )
            return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0);
        }
        v1 = v9;
        return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
      }
    }
  }
  return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
}
代码调用的是修改后的check函数,我们可以动态调试,在执行过qmemcpy后,把修改后的check函数复制到文件中,方便IDA分析。真正的check函数如下
int __thiscall click(CWnd *this)
{
  CWnd *v1; // esi
  int v2; // eax
  WCHAR String; // [esp+Ch] [ebp-310h]
  char v5; // [esp+Eh] [ebp-30Eh]
  char input; // [esp+20Ch] [ebp-110h]
  char v7; // [esp+20Dh] [ebp-10Fh]
  DWORD v8; // [esp+30Ch] [ebp-10h]
  CWnd *v9; // [esp+310h] [ebp-Ch]
  int v10; // [esp+314h] [ebp-8h]
  DWORD flOldProtect; // [esp+318h] [ebp-4h]

  v1 = this;
  v9 = this;
  String = 0;
  memset(&v5, 0, 0x1FEu);
  input = 0;
  memset(&v7, 0, 0xFFu);
  CWnd::GetDlgItemTextW(v1, 1000, &String, 20);
  if ( wcslen(&String) == 16 )                  // key的长度为16
  {
    v2 = 0;
    while ( !(*(&String + v2) & 0xFF00) )       // unicode转ascii
    {
      *(&input + v2) = *((_BYTE *)&String + 2 * v2);
      if ( ++v2 >= 16 )
      {
        v8 = 64;
        flOldProtect = 0;
        VirtualProtect(check, 0xD17u, 0x40u, &flOldProtect);
        if ( GetLastError() )
          return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
        qmemcpy(check, byte_11C47B8, 0x330u);   // 修改check函数
        VirtualProtect(check, 0xD17u, flOldProtect, &v8);
        if ( !GetLastError() )
        {
          v10 = 0;
          v10 = check(&input);                  // 调用
          if ( v10 == 1 )
            return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0);
        }
        v1 = v9;
        return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
      }
    }
  }
  return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
}
代码调用的是修改后的check函数,我们可以动态调试,在执行过qmemcpy后,把修改后的check函数复制到文件中,方便IDA分析。真正的check函数如下
signed int __cdecl check(char *input)
{
  signed int v1; // eax
  char v2; // cl
  signed int v3; // ecx
  signed int v4; // eax
  signed int v5; // eax
  signed int v6; // esi
  signed int v7; // ecx
  __int16 v8; // dx
  char *mul; // edi
  __int16 v10; // ax
  signed int v11; // eax
  signed int v12; // ecx
  unsigned __int16 v13; // bx
  signed int v14; // esi
  signed int v15; // ecx
  __int16 v16; // dx
  char *v17; // edi
  __int16 v18; // ax
  signed int v19; // eax
  signed int v20; // ecx
  unsigned __int16 v21; // bx
  unsigned int v22; // eax
  signed int v23; // ecx
  unsigned __int16 v24; // dx
  char v25; // dl
  signed int v26; // eax
  __int16 v27; // si
  int v28; // eax
  int true; // [esp+8h] [ebp-90h]
  int v31; // [esp+Ch] [ebp-8Ch]
  int v32; // [esp+10h] [ebp-88h]
  int v33; // [esp+14h] [ebp-84h]
  int v34; // [esp+18h] [ebp-80h]
  int v35; // [esp+1Ch] [ebp-7Ch]
  int v36; // [esp+20h] [ebp-78h]
  int v37; // [esp+24h] [ebp-74h]
  int calc1; // [esp+28h] [ebp-70h]
  int calc2; // [esp+2Ch] [ebp-6Ch]
  int v40; // [esp+30h] [ebp-68h]
  int v41; // [esp+34h] [ebp-64h]
  int other; // [esp+38h] [ebp-60h]
  int v43; // [esp+3Ch] [ebp-5Ch]
  int re1; // [esp+40h] [ebp-58h]
  int re2; // [esp+44h] [ebp-54h]
  int broad2; // [esp+48h] [ebp-50h]
  int v47; // [esp+4Ch] [ebp-4Ch]
  int v48; // [esp+50h] [ebp-48h]
  int v49; // [esp+54h] [ebp-44h]
  char v50; // [esp+58h] [ebp-40h]
  int result; // [esp+5Ch] [ebp-3Ch]
  int v52; // [esp+60h] [ebp-38h]
  int v53; // [esp+64h] [ebp-34h]
  int v54; // [esp+68h] [ebp-30h]
  char v55; // [esp+6Ch] [ebp-2Ch]
  int broad1; // [esp+70h] [ebp-28h]
  int v57; // [esp+74h] [ebp-24h]
  int v58; // [esp+78h] [ebp-20h]
  int v59; // [esp+7Ch] [ebp-1Ch]
  char v60; // [esp+80h] [ebp-18h]
  int v61; // [esp+84h] [ebp-14h]
  int v62; // [esp+88h] [ebp-10h]
  int v63; // [esp+8Ch] [ebp-Ch]
  int v64; // [esp+90h] [ebp-8h]
  char v65; // [esp+94h] [ebp-4h]

  v31 = 0x646E9881;
  v1 = 0;
  true = 0xE38C9616;
  v32 = 0x81DC0884;
  v33 = 0x4F484DBE;
  v34 = 0;
  v35 = 0;
  v36 = 0;
  v37 = 0;
  calc1 = 0;
  calc2 = 0;
  re1 = 0;
  re2 = 0;
  do
  {
    v2 = *((_BYTE *)&v32 + v1) ^ input[v1 + 8];
    *((_BYTE *)&calc1 + v1) = *((_BYTE *)&true + v1) ^ *((_BYTE *)&true + v1 + input - (char *)&true);
    *((_BYTE *)&re1 + v1++) = v2;
  }
  while ( v1 < 8 );                             // 异或解密两个long long数字
  true = 0;
  broad1 = 0;
  v57 = 0;
  v58 = 0;
  v59 = 0;
  v60 = 0;
  v61 = 0;
  v62 = 0;
  v63 = 0;
  v64 = 0;
  v65 = 0;
  broad2 = 0;
  v47 = 0;
  v48 = 0;
  v49 = 0;
  v50 = 0;
  result = 0;
  v52 = 0;
  v53 = 0;
  v54 = 0;
  v55 = 0;
  v31 = 0;
  v32 = 0;
  v33 = 0;
  LOBYTE(v34) = 0;
  v3 = 8;
  LOBYTE(true) = 8;
  v4 = 7;
  do
  {
    if ( *((_BYTE *)&calc1 + v4) )              // 最后一位char不为0,即第一个数字要>0x00FFFFFFFFFFFFFF
      break;
    --v3;
    --v4;
  }
  while ( v4 >= 0 );
  if ( v3 == 8 )
  {
    v5 = 7;
    do
    {
      if ( *((_BYTE *)&re1 + v5) )              // 同上
        break;
      --v3;
      --v5;
    }
    while ( v5 >= 0 );
    if ( v3 == 8 && !(calc2 & 0xF0000000) )     // calc2不得大于0x0fffffff,即第一个数字要<0x0FFFFFFFFFFFFFFF
    {
      v6 = 0;
      do
      {
        v40 = 0;
        v41 = 0;
        other = 0;
        v43 = 0;
        v7 = 0;
        v8 = *((unsigned __int8 *)&calc1 + v6);
        mul = (char *)&v40 + v6;
        do
        {                                       // 每位key乘以key,等价于求平方
          v10 = *((unsigned __int8 *)&other + v6) + v8 * *((unsigned __int8 *)&calc1 + v7);
          mul[v7] = *((_BYTE *)&other + v6) + v8 * *((_BYTE *)&calc1 + v7);
          ++v7;
          *((_BYTE *)&other + v6) = HIBYTE(v10);
        }
        while ( v7 < 8 );
        LOBYTE(v11) = 0;
        v12 = 0;
        do
        {
          v13 = (char)v11 + *((unsigned __int8 *)&broad1 + v12 + v6) + (unsigned __int8)mul[v12];
          *((_BYTE *)&broad1 + v12++ + v6) = v13;
          v11 = (signed int)v13 >> 8;
        }
        while ( v12 < 9 );
        ++v6;
      }
      while ( v6 < 8 );
      v14 = 0;
      do                                        // 跟上面一模一样
      {
        v40 = 0;
        v41 = 0;
        other = 0;
        v43 = 0;
        v15 = 0;
        v16 = *((unsigned __int8 *)&re1 + v14);
        v17 = (char *)&v40 + v14;
        do
        {
          v18 = *((unsigned __int8 *)&other + v14) + v16 * *((unsigned __int8 *)&re1 + v15);
          v17[v15] = *((_BYTE *)&other + v14) + v16 * *((_BYTE *)&re1 + v15);
          ++v15;
          *((_BYTE *)&other + v14) = HIBYTE(v18);
        }
        while ( v15 < 8 );
        LOBYTE(v19) = 0;
        v20 = 0;
        do
        {
          v21 = (char)v19 + *((unsigned __int8 *)&v61 + v20 + v14) + (unsigned __int8)v17[v20];
          *((_BYTE *)&v61 + v20++ + v14) = v21;
          v19 = (signed int)v21 >> 8;
        }
        while ( v20 < 9 );
        ++v14;
      }
      while ( v14 < 8 );
      LOBYTE(v22) = v50;
      v23 = 0;
      do
      {
        v24 = (unsigned __int8)v22 + 7 * *((unsigned __int8 *)&v61 + v23);// 第二个数字计算平方后乘以7
        *((_BYTE *)&broad2 + v23++) = v24;
        v22 = (unsigned int)v24 >> 8;
      }
      while ( v23 < 17 );
      v50 = HIBYTE(v24);
      v25 = 0;
      v26 = 0;
      do
      {
        v27 = *((unsigned __int8 *)&broad1 + v26) - *((unsigned __int8 *)&broad2 + v26) - v25;// 两个结果相减
        *((_BYTE *)&result + v26) = v27;
        if ( v27 < 0 )
          v25 = 1;
        ++v26;
      }
      while ( v26 < 17 );
      if ( !v25 )
      {
        v28 = 0;
        while ( *((_BYTE *)&result + v28) == *((_BYTE *)&true + v28) )// 对比结果是否等于8
        {
          if ( ++v28 >= 17 )
            return 1;
        }
      }
    }
  }
  return 0;
}
我们设解密出来的第一个数字为x,第二个为y,结合代码可以得到一道方程
signed int __cdecl check(char *input)
{
  signed int v1; // eax
  char v2; // cl
  signed int v3; // ecx
  signed int v4; // eax
  signed int v5; // eax
  signed int v6; // esi
  signed int v7; // ecx
  __int16 v8; // dx
  char *mul; // edi
  __int16 v10; // ax
  signed int v11; // eax
  signed int v12; // ecx
  unsigned __int16 v13; // bx
  signed int v14; // esi
  signed int v15; // ecx
  __int16 v16; // dx
  char *v17; // edi
  __int16 v18; // ax
  signed int v19; // eax
  signed int v20; // ecx
  unsigned __int16 v21; // bx
  unsigned int v22; // eax
  signed int v23; // ecx
  unsigned __int16 v24; // dx
  char v25; // dl
  signed int v26; // eax
  __int16 v27; // si
  int v28; // eax
  int true; // [esp+8h] [ebp-90h]
  int v31; // [esp+Ch] [ebp-8Ch]
  int v32; // [esp+10h] [ebp-88h]
  int v33; // [esp+14h] [ebp-84h]
  int v34; // [esp+18h] [ebp-80h]
  int v35; // [esp+1Ch] [ebp-7Ch]
  int v36; // [esp+20h] [ebp-78h]
  int v37; // [esp+24h] [ebp-74h]
  int calc1; // [esp+28h] [ebp-70h]
  int calc2; // [esp+2Ch] [ebp-6Ch]
  int v40; // [esp+30h] [ebp-68h]
  int v41; // [esp+34h] [ebp-64h]
  int other; // [esp+38h] [ebp-60h]
  int v43; // [esp+3Ch] [ebp-5Ch]
  int re1; // [esp+40h] [ebp-58h]
  int re2; // [esp+44h] [ebp-54h]
  int broad2; // [esp+48h] [ebp-50h]
  int v47; // [esp+4Ch] [ebp-4Ch]
  int v48; // [esp+50h] [ebp-48h]
  int v49; // [esp+54h] [ebp-44h]
  char v50; // [esp+58h] [ebp-40h]
  int result; // [esp+5Ch] [ebp-3Ch]
  int v52; // [esp+60h] [ebp-38h]
  int v53; // [esp+64h] [ebp-34h]
  int v54; // [esp+68h] [ebp-30h]
  char v55; // [esp+6Ch] [ebp-2Ch]
  int broad1; // [esp+70h] [ebp-28h]
  int v57; // [esp+74h] [ebp-24h]
  int v58; // [esp+78h] [ebp-20h]
  int v59; // [esp+7Ch] [ebp-1Ch]
  char v60; // [esp+80h] [ebp-18h]
  int v61; // [esp+84h] [ebp-14h]
  int v62; // [esp+88h] [ebp-10h]
  int v63; // [esp+8Ch] [ebp-Ch]
  int v64; // [esp+90h] [ebp-8h]
  char v65; // [esp+94h] [ebp-4h]

  v31 = 0x646E9881;
  v1 = 0;
  true = 0xE38C9616;
  v32 = 0x81DC0884;
  v33 = 0x4F484DBE;
  v34 = 0;
  v35 = 0;
  v36 = 0;
  v37 = 0;
  calc1 = 0;
  calc2 = 0;
  re1 = 0;
  re2 = 0;
  do
  {
    v2 = *((_BYTE *)&v32 + v1) ^ input[v1 + 8];
    *((_BYTE *)&calc1 + v1) = *((_BYTE *)&true + v1) ^ *((_BYTE *)&true + v1 + input - (char *)&true);
    *((_BYTE *)&re1 + v1++) = v2;
  }
  while ( v1 < 8 );                             // 异或解密两个long long数字
  true = 0;
  broad1 = 0;
  v57 = 0;
  v58 = 0;
  v59 = 0;
  v60 = 0;
  v61 = 0;
  v62 = 0;
  v63 = 0;
  v64 = 0;
  v65 = 0;
  broad2 = 0;
  v47 = 0;
  v48 = 0;
  v49 = 0;
  v50 = 0;
  result = 0;
  v52 = 0;
  v53 = 0;
  v54 = 0;
  v55 = 0;
  v31 = 0;
  v32 = 0;
  v33 = 0;
  LOBYTE(v34) = 0;
  v3 = 8;
  LOBYTE(true) = 8;
  v4 = 7;
  do
  {
    if ( *((_BYTE *)&calc1 + v4) )              // 最后一位char不为0,即第一个数字要>0x00FFFFFFFFFFFFFF
      break;
    --v3;
    --v4;
  }
  while ( v4 >= 0 );
  if ( v3 == 8 )
  {
    v5 = 7;
    do
    {
      if ( *((_BYTE *)&re1 + v5) )              // 同上
        break;
      --v3;
      --v5;
    }
    while ( v5 >= 0 );
    if ( v3 == 8 && !(calc2 & 0xF0000000) )     // calc2不得大于0x0fffffff,即第一个数字要<0x0FFFFFFFFFFFFFFF
    {
      v6 = 0;
      do
      {
        v40 = 0;
        v41 = 0;
        other = 0;
        v43 = 0;
        v7 = 0;
        v8 = *((unsigned __int8 *)&calc1 + v6);
        mul = (char *)&v40 + v6;
        do
        {                                       // 每位key乘以key,等价于求平方
          v10 = *((unsigned __int8 *)&other + v6) + v8 * *((unsigned __int8 *)&calc1 + v7);
          mul[v7] = *((_BYTE *)&other + v6) + v8 * *((_BYTE *)&calc1 + v7);
          ++v7;
          *((_BYTE *)&other + v6) = HIBYTE(v10);
        }
        while ( v7 < 8 );
        LOBYTE(v11) = 0;
        v12 = 0;
        do
        {
          v13 = (char)v11 + *((unsigned __int8 *)&broad1 + v12 + v6) + (unsigned __int8)mul[v12];
          *((_BYTE *)&broad1 + v12++ + v6) = v13;
          v11 = (signed int)v13 >> 8;
        }
        while ( v12 < 9 );
        ++v6;
      }
      while ( v6 < 8 );
      v14 = 0;
      do                                        // 跟上面一模一样
      {
        v40 = 0;
        v41 = 0;
        other = 0;
        v43 = 0;
        v15 = 0;
        v16 = *((unsigned __int8 *)&re1 + v14);
        v17 = (char *)&v40 + v14;
        do
        {
          v18 = *((unsigned __int8 *)&other + v14) + v16 * *((unsigned __int8 *)&re1 + v15);
          v17[v15] = *((_BYTE *)&other + v14) + v16 * *((_BYTE *)&re1 + v15);
          ++v15;
          *((_BYTE *)&other + v14) = HIBYTE(v18);
        }
        while ( v15 < 8 );
        LOBYTE(v19) = 0;
        v20 = 0;
        do
        {
          v21 = (char)v19 + *((unsigned __int8 *)&v61 + v20 + v14) + (unsigned __int8)v17[v20];
          *((_BYTE *)&v61 + v20++ + v14) = v21;
          v19 = (signed int)v21 >> 8;
        }
        while ( v20 < 9 );
        ++v14;
      }
      while ( v14 < 8 );
      LOBYTE(v22) = v50;
      v23 = 0;
      do
      {
        v24 = (unsigned __int8)v22 + 7 * *((unsigned __int8 *)&v61 + v23);// 第二个数字计算平方后乘以7
        *((_BYTE *)&broad2 + v23++) = v24;
        v22 = (unsigned int)v24 >> 8;
      }
      while ( v23 < 17 );
      v50 = HIBYTE(v24);
      v25 = 0;
      v26 = 0;
      do
      {
        v27 = *((unsigned __int8 *)&broad1 + v26) - *((unsigned __int8 *)&broad2 + v26) - v25;// 两个结果相减
        *((_BYTE *)&result + v26) = v27;
        if ( v27 < 0 )
          v25 = 1;
        ++v26;
      }
      while ( v26 < 17 );
      if ( !v25 )
      {
        v28 = 0;
        while ( *((_BYTE *)&result + v28) == *((_BYTE *)&true + v28) )// 对比结果是否等于8
        {
          if ( ++v28 >= 17 )
            return 1;
        }
      }
    }
  }
  return 0;
}
我们设解密出来的第一个数字为x,第二个为y,结合代码可以得到一道方程
x*x-7*y*y=8 (x>0x00FFFFFFFFFFFFFF)
这是非标准佩尔方程,解法参考维基百科

我们可以算出方程的初始解为(6,2),接下来的可以写代码解决,具体代码如下

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//