首页
社区
课程
招聘
[原创]2019 Q2 第四题 达芬奇密码 分析
2019-6-23 17:59 7991

[原创]2019 Q2 第四题 达芬奇密码 分析

2019-6-23 17:59
7991

0x1 初步分析

MFC程序,根据字符串"Wrong!"找到验证函数0x401ea0

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

  v1 = this;
  v9 = this;
  input = 0;
  memset(&v5, 0, 0x1FEu);
  input_1 = 0;
  memset(&v7, 0, 0xFFu);
  CWnd::GetDlgItemTextW(v1, 1000, &input, 20);
  if ( wcslen(&input) == 16 )
  {
    i = 0;
    while ( !(*(&input + i) & 0xFF00) )
    {
      *(&input_1 + i) = *((_BYTE *)&input + 2 * i);
      if ( ++i >= 16 )
      {
        ignore = 64;
        flOldProtect = 0;
        VirtualProtect(ctf_check, 0xD17u, 0x40u, &flOldProtect);
        if ( GetLastError() )
          return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
        qmemcpy(ctf_check, &shellcode, 0x330u);
        VirtualProtect(ctf_check, 0xD17u, flOldProtect, &ignore);
        if ( !GetLastError() )
        {
          ret = 0;
          ret = ctf_check(&input_1);
          if ( ret == 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);
}

key长度为16

if ( wcslen(&input) == 16 )

对话框初始化时解密shellcode:

signed int __thiscall sub_401D80(CDialog *this)
{
  CDialog *v1; // esi
  unsigned int i; // eax

  v1 = this;
  CDialog::OnInitDialog(this);
  SendMessageW(*((HWND *)v1 + 8), 0x80u, 1u, *((_DWORD *)v1 + 29));
  SendMessageW(*((HWND *)v1 + 8), 0x80u, 0, *((_DWORD *)v1 + 29));
  i = 0;
  do
  {
    shellcode[i] ^= 0xABu;
    ++i;
  }
  while ( i < 0x330 );
  return 1;
}

复制解密完的shellcode到一特定地址,传入input,开始验证,返回为1则成功:

VirtualProtect(ctf_check, 0xD17u, 0x40u, &flOldProtect);
if ( GetLastError() )
  return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
qmemcpy(ctf_check, shellcode, 0x330u);
VirtualProtect(ctf_check, 0xD17u, flOldProtect, &ignore);
if ( !GetLastError() )
{
  ret = 0;
  ret = ctf_check(&input_1);
  if ( ret == 1 )
    return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0);
}

0x2 验证算法分析

为了便于调试,我nop掉了复制shellcode的步骤,然后写了一段简单的Idapython把解密后的shellcode放到正确的位置上:

origin_code = GetManyBytes(0x5647b8,0x330)

addr = 0x4010e0
for i,c in enumerate(origin_code):
    PatchByte(addr+i,ord(c)^0xab)

F5出来的伪代码:

signed int __cdecl ctf_check(char *p)
{
  signed int i; // eax
  unsigned __int8 v2; // cl
  signed int v3; // ecx
  signed int v4; // eax
  signed int v5; // eax
  signed int i_1; // esi
  signed int i_2; // ecx
  __int16 c; // dx
  unsigned __int8 *v9; // edi
  __int16 v10; // ax
  signed int v11; // eax
  signed int j; // ecx
  unsigned __int16 v13; // bx
  signed int i_3; // esi
  signed int j_2; // ecx
  __int16 c_1; // dx
  unsigned __int8 *v17; // edi
  __int16 v18; // ax
  signed int v19; // eax
  signed int j_1; // ecx
  unsigned __int16 v21; // bx
  unsigned __int8 v22[4]; // eax
  signed int i_4; // ecx
  unsigned __int16 v24; // dx
  char bflag; // dl
  signed int i_5; // eax
  __int16 t; // si
  int v28; // eax
  char xor_data1[8]; // [esp+8h] [ebp-90h]
  char xor_data2[8]; // [esp+10h] [ebp-88h]
  int v32; // [esp+18h] [ebp-80h]
  int v33; // [esp+1Ch] [ebp-7Ch]
  int v34; // [esp+20h] [ebp-78h]
  int v35; // [esp+24h] [ebp-74h]
  unsigned __int8 data1[8]; // [esp+28h] [ebp-70h]
  unsigned __int8 buf[16]; // [esp+30h] [ebp-68h]
  unsigned __int8 data2[8]; // [esp+40h] [ebp-58h]
  unsigned __int8 outbuf3[17]; // [esp+48h] [ebp-50h]
  unsigned __int8 outbuf4[17]; // [esp+5Ch] [ebp-3Ch]
  unsigned __int8 outbuf1[17]; // [esp+70h] [ebp-28h]
  unsigned __int8 outbuf2[17]; // [esp+84h] [ebp-14h]

  xor_data1[4] = 0x81u;
  xor_data2[3] = 0x81u;
  i = 0;
  xor_data1[0] = 0x16;
  xor_data1[1] = 0x96u;
  xor_data1[2] = 0x8Cu;
  xor_data1[3] = 0xE3u;
  xor_data1[5] = 0x98u;
  xor_data1[6] = 0x6E;
  xor_data1[7] = 0x64;
  xor_data2[0] = 0x84u;
  xor_data2[1] = 8;
  xor_data2[2] = 0xDCu;
  xor_data2[4] = 0xBEu;
  xor_data2[5] = 0x4D;
  xor_data2[6] = 0x48;
  xor_data2[7] = 0x4F;
  v32 = 0;
  v33 = 0;
  v34 = 0;
  v35 = 0;
  *(_DWORD *)data1 = 0;
  *(_DWORD *)&data1[4] = 0;
  *(_DWORD *)data2 = 0;
  *(_DWORD *)&data2[4] = 0;
  do
  {
    v2 = xor_data2[i] ^ p[i + 8];
    data1[i] = xor_data1[i] ^ xor_data1[i + p - xor_data1];
    data2[i++] = v2;
  }
  while ( i < 8 );
  *(_DWORD *)xor_data1 = 0;
  *(_DWORD *)outbuf1 = 0;
  *(_DWORD *)&outbuf1[4] = 0;
  *(_DWORD *)&outbuf1[8] = 0;
  *(_DWORD *)&outbuf1[12] = 0;
  outbuf1[16] = 0;
  *(_DWORD *)outbuf2 = 0;
  *(_DWORD *)&outbuf2[4] = 0;
  *(_DWORD *)&outbuf2[8] = 0;
  *(_DWORD *)&outbuf2[12] = 0;
  outbuf2[16] = 0;
  *(_DWORD *)outbuf3 = 0;
  *(_DWORD *)&outbuf3[4] = 0;
  *(_DWORD *)&outbuf3[8] = 0;
  *(_DWORD *)&outbuf3[12] = 0;
  outbuf3[16] = 0;
  *(_DWORD *)outbuf4 = 0;
  *(_DWORD *)&outbuf4[4] = 0;
  *(_DWORD *)&outbuf4[8] = 0;
  *(_DWORD *)&outbuf4[12] = 0;
  outbuf4[16] = 0;
  *(_DWORD *)&xor_data1[4] = 0;
  *(_DWORD *)xor_data2 = 0;
  *(_DWORD *)&xor_data2[4] = 0;
  LOBYTE(v32) = 0;
  v3 = 8;
  xor_data1[0] = 8;
  v4 = 7;
  do
  {
    if ( data1[v4] )
      break;
    --v3;
    --v4;
  }
  while ( v4 >= 0 );
  if ( v3 == 8 )
  {
    v5 = 7;
    do
    {
      if ( data2[v5] )
        break;
      --v3;
      --v5;
    }
    while ( v5 >= 0 );
    if ( v3 == 8 && !(data1[7] & 0xF0) )
    {
      i_1 = 0;
      do
      {
        *(_DWORD *)buf = 0;
        *(_DWORD *)&buf[4] = 0;
        *(_DWORD *)&buf[8] = 0;
        *(_DWORD *)&buf[12] = 0;
        i_2 = 0;
        c = data1[i_1];
        v9 = &buf[i_1];
        do
        {
          v10 = buf[i_1 + 8] + c * data1[i_2];
          v9[i_2] = buf[i_1 + 8] + c * data1[i_2];
          ++i_2;
          buf[i_1 + 8] = HIBYTE(v10);
        }
        while ( i_2 < 8 );
        LOBYTE(v11) = 0;
        j = 0;
        do
        {
          v13 = (char)v11 + outbuf1[j + i_1] + v9[j];
          outbuf1[j++ + i_1] = v13;
          v11 = (signed int)v13 >> 8;
        }
        while ( j < 9 );
        ++i_1;
      }
      while ( i_1 < 8 );
      i_3 = 0;
      do
      {
        *(_DWORD *)buf = 0;
        *(_DWORD *)&buf[4] = 0;
        *(_DWORD *)&buf[8] = 0;
        *(_DWORD *)&buf[12] = 0;
        j_2 = 0;
        c_1 = data2[i_3];
        v17 = &buf[i_3];
        do
        {
          v18 = buf[i_3 + 8] + c_1 * data2[j_2];
          v17[j_2] = buf[i_3 + 8] + c_1 * data2[j_2];
          ++j_2;
          buf[i_3 + 8] = HIBYTE(v18);
        }
        while ( j_2 < 8 );
        LOBYTE(v19) = 0;
        j_1 = 0;
        do
        {
          v21 = (char)v19 + outbuf2[j_1 + i_3] + v17[j_1];
          outbuf2[j_1++ + i_3] = v21;
          v19 = (signed int)v21 >> 8;
        }
        while ( j_1 < 9 );
        ++i_3;
      }
      while ( i_3 < 8 );
      v22[0] = outbuf3[16];
      i_4 = 0;
      do
      {
        v24 = v22[0] + 7 * outbuf2[i_4];
        outbuf3[i_4++] = v24;
        *(_DWORD *)v22 = (unsigned int)v24 >> 8;
      }
      while ( i_4 < 17 );
      outbuf3[16] = HIBYTE(v24);
      bflag = 0;
      i_5 = 0;
      do
      {
        t = outbuf1[i_5] - outbuf3[i_5] - bflag;
        outbuf4[i_5] = t;
        if ( t < 0 )
          bflag = 1;
        ++i_5;
      }
      while ( i_5 < 17 );
      if ( !bflag )
      {
        v28 = 0;
        while ( outbuf4[v28] == xor_data1[v28] )
        {
          if ( ++v28 >= 17 )
            return 1;
        }
      }
    }
  }
  return 0;
}

看起来乱七八糟的,于是我把它等价转成了Python:

xor_data1 = [0x16,0x96,0x8c,0xe3,0x81,0x98,0x6e,0x64]
xor_data2 = [0x84,0x8, 0xdc,0x81,0xbe,0x4d,0x48,0x4f]


input_s = '7654321e87654321'

data1 = [xor_data1[i]^ord(input_s[i]) for i in range(8)]
data2 = [xor_data2[i]^ord(input_s[i+8]) for i in range(8)]

outbuf1 = [0]*17
outbuf2 = [0]*17
outbuf3 = [0]*17
outbuf4 = [0]*17
def loop1():
    for i in range(8):
        buf = [0]*16
        ch = data1[i]

        for j in range(8):
            t = buf[i+8] + ch*data1[j]
            buf[i+j] = t%256
            buf[i+8] = t>>8

        t1 = 0
        for j in range(9):
            t2 = t1 + outbuf1[j+i] + buf[i+j]
            outbuf1[i+j] = t2%256
            t1 = t2>>8

def loop2():
    for i in range(8):
        buf = [0]*16
        ch = data2[i]

        for j in range(8):
            t = buf[i+8] + ch*data2[j]
            buf[i+j] = t%256
            buf[i+8] = t>>8

        t1 = 0
        for j in range(9):
            t2 = t1 + outbuf2[j+i] + buf[i+j]
            outbuf2[i+j] = t2%256
            t1 = t2>>8

def loop3():
    t1 = 0
    for i in range(17):
        t2 = t1 + 7*outbuf2[i]
        outbuf3[i] = t2%256
        t1 = t2>>8
    outbuf3[16] = t2>>8

def loop4():
    bflag = 0
    for i in range(17):
        t = outbuf1[i] - outbuf3[i] - bflag
        outbuf4[i] = t%256
        if t<0:
            bflag = 1

# loop1()
# loop2()
# loop3()
# loop4()
# print(list(map(hex,outbuf1)))
# print(list(map(hex,outbuf2)))
# print(list(map(hex,outbuf3)))
# print(list(map(hex,outbuf4)))

data1 = ['5a', 'a5', 'e1', 'b9', 'b3', 'f3', '57', '05']
data2 = ['de', '38', 'bd', 'b2', '88', '09', '05', '02']
o1 = [chr(int(data1[i],16)^xor_data1[i]) for i in range(8)]
o2 = [chr(int(data2[i],16)^xor_data2[i]) for i in range(8)]

print(''.join(o1+o2))

总结一下:

16字节的输入先和固定值异或,然后分为两部分长度为8的256进制大数x,y
loop1计算x**2
loop2计算y**2
loop3计算7*y**2
loop4计算x**2-7*y**2
最后的结果与8比较,相等就返回1。
另外还有对x,y大小的限制:
(1<<56)<=x<(1<<60)
y>=(1>>56)

直接用wolframalpha解得
x = 385044246406735194, y = 145533045678356702
最后用上面的代码转换为输入,最后的key为L3mZ2k9aZ0a36DMM

 

loop4的减法实现似乎有点问题,借位为1就一直为1了,但是并不影响最后的答案,因为一旦借位,就返回0了。


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2019-6-23 19:05 被mratlatsn编辑 ,原因:
收藏
点赞3
打赏
分享
最新回复 (1)
雪    币: 997
活跃值: (1518)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
WMBa0 2023-3-9 20:15
2
0
游客
登录 | 注册 方可回帖
返回