猜 ID 应该是比赛的时候有提示ID吧 其他还好

题目简介

I have borrowed this software license(Leaked_License) and library from Big Brother of a friend of a friend! It would be nice if I could have forged a license for the ID of this years competition computer.

题目给了一个dll 一个许可证

能发现许可证是32字节 而且是十六进制 我们能猜到这个是16字节的密文

IDA打开发现只有一个导出函数就是 verify 其他也没有名字一眼看到了 也可以查看字符串能看到secret.key查看调用也能到这里

bool __cdecl verify(
        void *a1,
        int a2,
        int a3,
        int a4,
        int a5,
        unsigned int a6,
        void *a7,
        int a8,
        int a9,
        int a10,
        int a11,
        unsigned int a12)
{
  int v12; // eax
  bool v13; // zf
  int v14; // eax
  char *v15; // ecx
  int v16; // eax
  char *v17; // ecx
  int v18; // ecx
  bool v19; // bl
  char *v20; // ecx
  int v21; // eax
  int v22; // eax
  void *v23; // eax
  void *v25; // [esp-30h] [ebp-1F0h]
  int v26; // [esp-2Ch] [ebp-1ECh]
  int v27; // [esp-28h] [ebp-1E8h]
  int v28; // [esp-24h] [ebp-1E4h]
  void *v29; // [esp-18h] [ebp-1D8h]
  void *v30; // [esp-18h] [ebp-1D8h]
  int v31; // [esp-14h] [ebp-1D4h]
  int v32; // [esp-14h] [ebp-1D4h]
  int v33; // [esp-10h] [ebp-1D0h]
  int v34; // [esp-10h] [ebp-1D0h]
  int v35; // [esp-Ch] [ebp-1CCh]
  int v36; // [esp-Ch] [ebp-1CCh]
  int v37; // [esp-Ch] [ebp-1CCh]
  int v38; // [esp-8h] [ebp-1C8h]
  int v39; // [esp-8h] [ebp-1C8h]
  int v40; // [esp-4h] [ebp-1C4h]
  int v41; // [esp-4h] [ebp-1C4h]
  int v42; // [esp-4h] [ebp-1C4h]
  _DWORD v44[4]; // [esp+8h] [ebp-1B8h] BYREF
  _BYTE v45[96]; // [esp+18h] [ebp-1A8h] BYREF
  void **v46; // [esp+78h] [ebp-148h] BYREF
  _DWORD v47[19]; // [esp+128h] [ebp-98h] BYREF
  void *v48[5]; // [esp+174h] [ebp-4Ch] BYREF
  unsigned int v49; // [esp+188h] [ebp-38h]
  void *Block[5]; // [esp+18Ch] [ebp-34h] BYREF
  unsigned int v51; // [esp+1A0h] [ebp-20h]
  void *v52; // [esp+1A4h] [ebp-1Ch]
  unsigned int v53; // [esp+1B8h] [ebp-8h]

  v12 = sub_10008DC0();
  sub_10009020(v12);
  sub_10003930(v40);
  v13 = sub_10004FB0(v35, v38, v41) == 0;
  v14 = *(_DWORD *)(v44[0] + 4);
  if ( v13 )
  {
    v15 = (char *)v44 + v14;
    v16 = *(_DWORD *)((char *)&v44[3] + v14) | 2;
    if ( !*((_DWORD *)v15 + 14) )
      v16 = *((_DWORD *)v15 + 3) | 6;
  }
  else
  {
    v17 = (char *)v44 + v14;
    v16 = 0;
    if ( !*((_DWORD *)v17 + 14) )
      v16 = 4;
  }
  sub_10001B00(v16, 0);
  v18 = v44[0];
  if ( (*((_BYTE *)&v44[3] + *(_DWORD *)(v44[0] + 4)) & 6) != 0 )
  {
    v19 = 0;
  }
  else
  {
    sub_10003130(v39, v42);
    sub_10002EB0(v45);
    sub_10003260(v48);
    sub_10002390(v48);
    if ( !sub_10005070(v45) )
    {
      v20 = (char *)v44 + *(_DWORD *)(v44[0] + 4);
      v21 = *((_DWORD *)v20 + 3) | 2;
      if ( !*((_DWORD *)v20 + 14) )
        v21 = *((_DWORD *)v20 + 3) | 6;
      sub_10001B00(v21, 0);
    }
    LOBYTE(v29) = 0;
    sub_10004970((int)v48, 0, 0xFFFFFFFF);
    v22 = sub_1000A7E0(v29, v31, v33, v36, 0, 15);
    v23 = (void *)sub_10001DA0(Block, v22);
    sub_100027A0(&dword_100325B0, v23);
    if ( v51 >= 0x10 )
      j__free(Block[0]);
    v51 = 15;
    Block[4] = nullptr;
    LOBYTE(Block[0]) = 0;
    if ( v53 >= 0x10 )
      j__free(v52);
    LOBYTE(v30) = 0;
    sub_10004970((int)&a7, 0, 0xFFFFFFFF);
    LOBYTE(v25) = 0;
    sub_10004970((int)&a1, 0, 0xFFFFFFFF);
    v19 = (unsigned __int8)sub_10002000(v25, v26, v27, v28, 0, 15, v30, v32, v34, v37, 0, 15) != 0;
    if ( v49 >= 0x10 )
      j__free(v48[0]);
    v49 = 15;
    v48[4] = nullptr;
    LOBYTE(v48[0]) = 0;
    sub_100031E0(v47);
    v47[0] = &std::ios_base::`vftable';
    std::ios_base::_Ios_base_dtor((struct std::ios_base *)v47);
    v18 = v44[0];
  }
  *(_DWORD *)((char *)v44 + *(_DWORD *)(v18 + 4)) = &std::ifstream::`vftable';
  *(_DWORD *)((char *)&v44[-1] + *(_DWORD *)(v44[0] + 4)) = *(_DWORD *)(v44[0] + 4) - 112;
  sub_10003AE0(v45);
  *(_DWORD *)((char *)v44 + *(_DWORD *)(v44[0] + 4)) = &std::istream::`vftable';
  *(_DWORD *)((char *)&v44[-1] + *(_DWORD *)(v44[0] + 4)) = *(_DWORD *)(v44[0] + 4) - 24;
  v46 = &std::ios_base::`vftable';
  std::ios_base::_Ios_base_dtor((struct std::ios_base *)&v46);
  if ( a6 >= 0x10 )
    j__free(a1);
  a6 = 15;
  a5 = 0;
  LOBYTE(a1) = 0;
  if ( a12 >= 0x10 )
    j__free(a7);
  return v19;
}

open函数 也就是打开许可证的函数

_DWORD *__thiscall sub_10004FB0(_DWORD *this, int a2, int a3, int a4)
{
  int v5; // eax
  int v6; // eax
  int v7; // eax
  void (__thiscall ***v8)(_DWORD, int); // eax
  int v10; // [esp+8h] [ebp-10h] BYREF
  int v11; // [esp+14h] [ebp-4h]

  if ( *(this + 20) )
    return nullptr;
  v5 = sub_1001EFF6("secret.key", 1, 64);
  if ( !v5 )
    return nullptr;
  sub_10005120(v5, 1);
  v6 = sub_100069B0(&v10);
  v11 = 0;
  v7 = sub_10009310(v6);
  sub_10005340(v7);
  v11 = -1;
  if ( v10 )
  {
    v8 = (void (__thiscall ***)(_DWORD, int))(*(int (__thiscall **)(int))(*(_DWORD *)v10 + 8))(v10);
    if ( v8 )
      (**v8)(v8, 1);
  }
  return this;
}

close函数 也就是读取许可证后关闭

int __thiscall sub_10005070(int this)
{
  int v2; // edi

  if ( *(_DWORD *)(this + 80) )
  {
    v2 = this;
    if ( !(unsigned __int8)sub_100051C0() )
      v2 = 0;
    if ( fclose(*(FILE **)(this + 80)) )
      v2 = 0;
  }
  else
  {
    v2 = 0;
  }
  *(_BYTE *)(this + 76) = 0;
  *(_DWORD *)(this + 12) = this + 4;
  *(_DWORD *)(this + 16) = this + 8;
  *(_DWORD *)(this + 28) = this + 20;
  *(_DWORD *)(this + 32) = this + 24;
  *(_DWORD *)(this + 44) = this + 36;
  *(_DWORD *)(this + 48) = this + 40;
  *(_BYTE *)(this + 69) = 0;
  *(_DWORD *)(this + 8) = 0;
  **(_DWORD **)(this + 32) = 0;
  **(_DWORD **)(this + 48) = 0;
  **(_DWORD **)(this + 12) = 0;
  **(_DWORD **)(this + 28) = 0;
  **(_DWORD **)(this + 44) = 0;
  *(_DWORD *)(this + 80) = 0;
  *(_DWORD *)(this + 72) = dword_10033CE4;
  *(_DWORD *)(this + 64) = 0;
  return v2;
}

翻翻找找能找到校验函数

bool __cdecl sub_10002000(
        void ***Src,
        int a2,
        int a3,
        int a4,
        int a5,
        unsigned int a6,
        void ***a7,
        int a8,
        int a9,
        int a10,
        int a11,
        unsigned int a12)
{
  int *v12; // eax
  int v13; // esi
  void **v14; // ecx
  void ***p_Src; // edx
  void ***v16; // ebx
  int v17; // eax
  void ***v18; // ecx
  void **v19; // ecx
  void **v20; // edx
  void **v21; // ecx
  void ***v22; // edi
  int v23; // eax
  void ***v24; // ecx
  void ***v25; // ecx
  void **v26; // ecx
  void **v27; // edx
  int i; // edi
  void ***v29; // ecx
  void ***v30; // eax
  char v31; // al
  void **v32; // ecx
  int v33; // eax
  void **v34; // ecx
  int v35; // eax
  int *v36; // eax
  int *v37; // ecx
  void **v38; // esi
  unsigned int v39; // ebx
  unsigned int v40; // edx
  unsigned int v41; // edx
  bool v42; // cf
  unsigned __int8 v43; // al
  unsigned __int8 v44; // al
  unsigned __int8 v45; // al
  int v46; // eax
  bool v47; // zf
  int v48; // eax
  bool v49; // bl
  bool v50; // bl
  void **v52[4]; // [esp-18h] [ebp-90h] BYREF
  size_t v53; // [esp-8h] [ebp-80h]
  unsigned int v54; // [esp-4h] [ebp-7Ch]
  int v55; // [esp+Ch] [ebp-6Ch]
  int v56; // [esp+10h] [ebp-68h]
  void *v57; // [esp+14h] [ebp-64h]
  unsigned int v58; // [esp+28h] [ebp-50h]
  void *Block[5]; // [esp+2Ch] [ebp-4Ch] BYREF
  unsigned int v60; // [esp+40h] [ebp-38h]
  void *v61[4]; // [esp+44h] [ebp-34h] BYREF
  int v62; // [esp+54h] [ebp-24h]
  unsigned int v63; // [esp+58h] [ebp-20h]
  void *v64[4]; // [esp+5Ch] [ebp-1Ch] BYREF
  int v65; // [esp+6Ch] [ebp-Ch]
  unsigned int v66; // [esp+70h] [ebp-8h]

  v12 = sub_10001DA0((int *)Block, (int)&a7);
  sub_100027A0((void **)&a7, (void **)v12);
  if ( v60 >= 0x10 )
    j__free(Block[0]);
  v13 = a5;
  if ( a5 < 32 )
  {
    v66 = 15;
    v65 = 0;
    LOBYTE(v64[0]) = 0;
    sub_10004970(v64, &Src, 0, 0xFFFFFFFF);
    v56 = 32 % v13;
    v55 = 32 / v13;
    if ( 32 / v13 > 0 )
    {
      do
      {
        v14 = v64;
        p_Src = (void ***)&Src;
        if ( v66 >= 0x10 )
          v14 = (void **)v64[0];
        v16 = (void ***)&Src;
        v17 = (int)v14 + v65;
        if ( a6 >= 0x10 )
          p_Src = Src;
        v18 = (void ***)((char *)p_Src + a5);
        if ( a6 >= 0x10 )
          v16 = Src;
        if ( v16 == v18 )
        {
          v19 = v64;
          if ( v66 >= 0x10 )
            v19 = (void **)v64[0];
          if ( v17 )
            sub_10002850(v64, v17 - (_DWORD)v19, 0);
          else
            sub_10002850(v64, 0, 0);
        }
        else
        {
          v20 = v64;
          if ( v66 >= 0x10 )
            v20 = (void **)v64[0];
          if ( v17 )
            v17 -= (int)v20;
          sub_10009C00(v17, 0, v16, (char *)v18 - (char *)v16);
        }
        --v55;
      }
      while ( v55 );
    }
    v21 = v64;
    v22 = (void ***)&Src;
    if ( v66 >= 0x10 )
      v21 = (void **)v64[0];
    v23 = (int)v21 + v65;
    v24 = (void ***)&Src;
    if ( a6 >= 0x10 )
      v24 = Src;
    v25 = (void ***)((char *)v24 + v56);
    if ( a6 >= 0x10 )
      v22 = Src;
    if ( v22 == v25 )
    {
      v26 = v64;
      if ( v66 >= 0x10 )
        v26 = (void **)v64[0];
      if ( v23 )
        sub_10002850(v64, v23 - (_DWORD)v26, 0);
      else
        sub_10002850(v64, 0, 0);
    }
    else
    {
      v27 = v64;
      if ( v66 >= 0x10 )
        v27 = (void **)v64[0];
      if ( v23 )
        v23 -= (int)v27;
      sub_10009C00(v23, 0, v22, (char *)v25 - (char *)v22);
    }
    sub_10004970(&Src, v64, 0, 0xFFFFFFFF);
    if ( v66 >= 0x10 )
      j__free(v64[0]);
  }
  v63 = 15;
  v62 = 0;
  LOBYTE(v61[0]) = 0;
  sub_10006520((int)v61, &unk_1002D8FF, 0);
  for ( i = 0; i < 32; ++i )
  {
    v29 = (void ***)&Src;
    v30 = (void ***)&a7;
    if ( a6 >= 0x10 )
      v29 = Src;
    if ( a12 >= 0x10 )
      v30 = a7;
    v31 = *((_BYTE *)v29 + i) ^ *((_BYTE *)v30 + i);
    v32 = v61;
    LOBYTE(v56) = v31;
    if ( v63 >= 0x10 )
      v32 = (void **)v61[0];
    v33 = (int)v32 + v62;
    v34 = v61;
    if ( v63 >= 0x10 )
      v34 = (void **)v61[0];
    if ( v33 )
      v33 -= (int)v34;
    sub_100066F0(v33, 1u, v56);
  }
  v54 = 15;
  v53 = 0;
  LOBYTE(v52[0]) = 0;
  sub_10004970(v52, v61, 0, 0xFFFFFFFF);
  v35 = sub_1000A7E0(v52[0], (int)v52[1], (int)v52[2], (int)v52[3], v53, v54);
  v36 = sub_10001DA0((int *)Block, v35);
  v37 = v36;
  v38 = &dword_100325B0;
  if ( (unsigned int)dword_100325C4 >= 0x10 )
    v38 = (void **)dword_100325B0;
  v39 = v36[4];
  if ( (unsigned int)v36[5] >= 0x10 )
    v37 = (int *)*v36;
  v40 = dword_100325C0;
  if ( v39 < dword_100325C0 )
    v40 = v36[4];
  if ( v40 )
  {
    v42 = v40 < 4;
    v41 = v40 - 4;
    if ( v42 )
    {
LABEL_64:
      if ( v41 == -4 )
        goto LABEL_73;
    }
    else
    {
      while ( (void *)*v37 == *v38 )
      {
        ++v37;
        ++v38;
        v42 = v41 < 4;
        v41 -= 4;
        if ( v42 )
          goto LABEL_64;
      }
    }
    v42 = *(_BYTE *)v37 < *(_BYTE *)v38;
    if ( *(_BYTE *)v37 != *(_BYTE *)v38
      || v41 != -3
      && ((v43 = *((_BYTE *)v37 + 1), v42 = v43 < *((_BYTE *)v38 + 1), v43 != *((_BYTE *)v38 + 1))
       || v41 != -2
       && ((v44 = *((_BYTE *)v37 + 2), v42 = v44 < *((_BYTE *)v38 + 2), v44 != *((_BYTE *)v38 + 2))
        || v41 != -1 && (v45 = *((_BYTE *)v37 + 3), v42 = v45 < *((_BYTE *)v38 + 3), v45 != *((_BYTE *)v38 + 3)))) )
    {
      v46 = v42 ? -1 : 1;
      goto LABEL_74;
    }
LABEL_73:
    v46 = 0;
LABEL_74:
    v47 = v46 == 0;
    if ( v46 )
      goto LABEL_79;
  }
  if ( v39 >= dword_100325C0 )
    v48 = v39 != dword_100325C0;
  else
    v48 = -1;
  v47 = v48 == 0;
LABEL_79:
  v49 = v47;
  if ( v60 >= 0x10 )
    j__free(Block[0]);
  v60 = 15;
  Block[4] = nullptr;
  LOBYTE(Block[0]) = 0;
  if ( v58 >= 0x10 )
    j__free(v57);
  v50 = v49;
  if ( v63 >= 0x10 )
    j__free(v61[0]);
  v63 = 15;
  v62 = 0;
  LOBYTE(v61[0]) = 0;
  if ( a6 >= 0x10 )
    j__free(Src);
  a6 = 15;
  a5 = 0;
  LOBYTE(Src) = 0;
  if ( a12 >= 0x10 )
    j__free(a7);
  return v50;
}

能看到里面有很多0x10 看着特别像是一个取字符串真实地址 里面还有一个异或操作

for ( i = 0; i < 32; ++i )
  {
    v29 = (void ***)&Src;
    v30 = (void ***)&a7;
    if ( a6 >= 0x10 )
      v29 = Src;
    if ( a12 >= 0x10 )
      v30 = a7;
    v31 = *((_BYTE *)v29 + i) ^ *((_BYTE *)v30 + i);
    v32 = v61;
    LOBYTE(v56) = v31;

从verify调用的sub_100002000的位置能看到 我们能知道这个是C++ 所以它是传入两个 std::string 因为 32 位 MSVC 的 std::string 会被 IDA 拆成多个参数

32 位 MSVC 的 std::string 结构

struct string {
    char buf_or_ptr[16];  //  16 字节短字符串直接存在这里
    int size;             // +0x10字符串长度
    int capacity;         // +0x14容量
};
sub_10002000(
    v25, v26, v27, v28, 0, 15,
    v30, v32, v34, v37, 0, 15
)

也就是

Src, a2, a3, a4, a5, a6        第一个 string
a7, a8, a9, a10, a11, a12      第二个 string

所以我们能知道他是对于两个字符串来进行相对异或操作 然后我们能想到许可证是一个32字节的 那肯定就是第二个strings 因为第一个strings进行了很多的操作

  v13 = a5;
  if ( a5 < 32 )
  {
    v66 = 15;
    v65 = 0;
    LOBYTE(v64[0]) = 0;
    sub_10004970(v64, &Src, 0, 0xFFFFFFFF);
    v56 = 32 % v13;
    v55 = 32 / v13;
    if ( 32 / v13 > 0 )
    {
      do
      {
        v14 = v64;
        p_Src = (void ***)&Src;
        if ( v66 >= 0x10 )
          v14 = (void **)v64[0];
        v16 = (void ***)&Src;
        v17 = (int)v14 + v65;
        if ( a6 >= 0x10 )
          p_Src = Src;
        v18 = (void ***)((char *)p_Src + a5);
        if ( a6 >= 0x10 )
          v16 = Src;
        if ( v16 == v18 )
        {
          v19 = v64;
          if ( v66 >= 0x10 )
            v19 = (void **)v64[0];
          if ( v17 )
            sub_10002850(v64, v17 - (_DWORD)v19, 0);
          else
            sub_10002850(v64, 0, 0);
        }
        else
        {
          v20 = v64;
          if ( v66 >= 0x10 )
            v20 = (void **)v64[0];
          if ( v17 )
            v17 -= (int)v20;
          sub_10009C00(v17, 0, v16, (char *)v18 - (char *)v16);
        }
        --v55;
      }
      while ( v55 );
    }

能看到这是一个完美的字符串拼接操作 由上方的结构体能看出来 因为题目简介要的就是id伪装 然后文件名字就是他的id嘛 admin@nsa.gov.us 正正好好16字节 然后要补充到32字节 当然运行的时候a5肯定不是一个0 而是真实的长度 静态和动态是不一样的

a5 = id.length()

所以我们能看出来是一个将字符串追加到屁股后头到32字节 所以

exp

old_id = b"admin@nsa.gov.us"
old_license = bytes.fromhex(
    "7e43ecf0b4e27dacfb5e613437b17acb46e8deab2c70510dc71844b492a691ec"
)

target_id = b"quals2016@asis-ctf.ir"

def pad32(x):
    return (x * (32 // len(x) + 1))[:32]

old_id_pad = pad32(old_id)
target_id_pad = pad32(target_id)

secret = bytes(a ^ b for a, b in zip(old_license, old_id_pad))
new_license = bytes(a ^ b for a, b in zip(secret, target_id_pad))

print("secret =", secret.hex())
print("license =", new_license.hex())
print("flag = ASIS{" + new_license.hex() + "}")

当然 你要问我这个target_id 怎么来的 那我只能说 我看wp出来的 这谁能猜出来啊 哈哈哈哈