一道简单的SMC逆向题目复现

再不学逆向👶真要被开了

SMC(self-Modifying Code)可以理解为程序在执行一段机器码之前会对其进行修改,修改后的机器码才是可执行的正常汇编。在此之前,静态分析只能看到无法识别的汇编指令。

校赛re3是个带linux下smc的题目,只能说我完全看不懂,先做个纯smc的套题来练练手。

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  int i; // [esp+4Ch] [ebp-70h]
  char Str[97]; // [esp+54h] [ebp-68h] BYREF
  __int16 v6; // [esp+B5h] [ebp-7h]
  char v7; // [esp+B7h] [ebp-5h]
  size_t v8; // [esp+B8h] [ebp-4h]

  v8 = 0;
  memset(Str, 0, sizeof(Str));
  v6 = 0;
  v7 = 0;
  scanf("%100s", Str);
  v8 = strlen(Str);
  if ( v8 == 28 )
  {
    if ( Str[27] == '}' )
    {
      for ( i = 0; i < 67; ++i )
        byte_414C3C[i] ^= 0x7Du;
      (*(void (__cdecl **)(char *, void *))byte_414C3C)(Str, &unk_414BE0);
      return 0;
    }
    else
    {
      return 0;
    }
  }
  else
  {
    printf("Try Again......\n");
    return 0;
  }
}

我们在这里可以发现flag的最后一位为'}',没啥用

第一次解密

关键在于这个奇怪的for循环处。在进行异或操作后对byte_414C3C数组进行了类似函数调用的操作,这里就是本题的第一层SMC加密

我们可以想到这里是将byte_414C3C数组异或后变成一段函数的机器码,然后再调用该函数。那么用考虑idc来实现函数的解密

static decrypt_1()
{
    auto st = 0x00414C3C;   // byte_414C3C的起始地址
    auto i = 0;
    for (i = 0; i < 67; ++i)
    {
        PatchByte(st + i,Byte(st + i) ^ 0x7D);
    }
}

运行该脚本后对新的byte_414C3C数组(现在就可以看做是函数的机器码了)按C转化成代码,再在起始处按P转化成函数(直接按P好像也行)

int __cdecl sub_414C3C(_BYTE *a1, _BYTE *a2)
{
  int i; // ecx

  if ( *a1 == 102 && a1[1] == 108 && a1[2] == 97 && a1[3] == 103 && a1[4] == 123 )
  {
    for ( i = 0; i < 90; ++i )
      a2[i] ^= 0x43u;
    ((void (__cdecl *)(_BYTE *, void *))a2)(a1 + 5, &unk_414A84);
  }
  return 0;
}

我们在这里可以发现flag的前五位为'flag{',没啥用

第二次解密

从sub_414c3c内我们再次发现了SMC的痕迹,继续这次的解密,a2的值我们可以回到主函数内看到参数byte_414BE0,用其解密即可

static decrypt_2()
{
    auto st = 0x00414BE0;   
    auto i = 0;
    for (i = 0; i < 90; ++i)
    {
        PatchByte(st + i,Byte(st + i) ^ 0x43);
    }
}

重复上述方法得到函数sub_414BE0

int __cdecl sub_414BE0(int a1, void (__cdecl *a2)(int, void *))
{
  int v2; // edx
  int v3; // esi
  int v5; // [esp+Ch] [ebp-8h] BYREF
  char v6; // [esp+10h] [ebp-4h]

  v2 = 0;
  v5 = -1817598824;
  v6 = 0;
  v3 = 0;
  while ( (*((_BYTE *)&v5 + v3 + a1 - (_DWORD)&v5) ^ 0xCC) == *((_BYTE *)&v5 + v3) )
  {
    if ( ++v3 >= 4 )
    {
      do
        *((_BYTE *)a2 + v2++) ^= 0x55u;
      while ( v2 < 347 );
      a2(a1 + 4, &unk_414A30);
      return 0;
    }
  }
  return 0;
}

注意一下调用该函数时

((void (__cdecl *)(_BYTE *, void *))a2)(a1 + 5, &unk_414A84);

也就是说,这里的a1是除去了前五位的flag。

此处的限制条件简化一下*(a1 + v3) ^ 0xCC) == (&v5 + v3),简单解密一下可以知道是The_

第三次解密

这里显然还是有smc,只不过是将for换成了while的样子。

static decrypt_3()
{
    auto st = 0x00414A84;   
    auto i = 0;
    for (i = 0; i < 347; ++i)
    {
        PatchByte(st + i,Byte(st + i) ^ 0x55);
    }
}

然后拿到sub_414A84函数

int __cdecl sub_414A84(int a1, void (__cdecl *a2)(_DWORD))
{
  char v2; // dl
  int v3; // eax
  char i; // dl
  char j; // dl
  char *v6; // esi
  int v7; // edi
  unsigned __int8 v8; // bl
  unsigned __int8 v9; // dl
  int v10; // ecx
  int v11; // eax
  unsigned __int8 v12; // dh
  int v13; // eax
  unsigned __int8 v14; // dl
  _BYTE *v15; // esi
  int v16; // eax
  int v17; // ecx
  char v18; // dl
  __int16 v20[34]; // [esp+0h] [ebp-64h]
  char v21; // [esp+44h] [ebp-20h] BYREF
  char v22[15]; // [esp+45h] [ebp-1Fh] BYREF
  char v23[16]; // [esp+54h] [ebp-10h] BYREF

  v2 = 65;
  v3 = 0;
  do
    *((_BYTE *)v20 + v3++) = v2++;
  while ( v2 <= 90 );
  for ( i = 97; i <= 122; ++i )
    *((_BYTE *)v20 + v3++) = i;
  for ( j = 48; j <= 57; ++j )
    *((_BYTE *)v20 + v3++) = j;
  *(__int16 *)((char *)v20 + v3) = 12075;
  *((_BYTE *)&v20[1] + v3) = 0;
  v6 = &v21;
  v21 = 0;
  memset(v22, 0, 12);
  strcpy(v23, "cmVhbEN0Rl8=");
  v7 = 0;
  do
  {
    v8 = *(_BYTE *)(a1 + v7 + 1);
    v9 = *(_BYTE *)(a1 + v7 + 2);
    v10 = *(_BYTE *)(a1 + v7) & 3;
    v11 = *(unsigned __int8 *)(a1 + v7) >> 2;
    v7 += 3;
    *v6 = *((_BYTE *)v20 + v11);
    v6[1] = *((_BYTE *)v20 + ((v8 >> 4) | (16 * v10)));
    v6[2] = *((_BYTE *)v20 + ((v9 >> 6) | (4 * (v8 & 0xF))));
    v6[3] = *((_BYTE *)v20 + (v9 & 0x3F));
    v6 += 4;
  }
  while ( v7 < 6 );
  if ( v7 < 8 )
  {
    v12 = *(_BYTE *)(v7 + a1);
    *v6 = *((_BYTE *)v20 + (v12 >> 2));
    v13 = v12 & 3;
    if ( v7 == 7 )
    {
      v6[1] = v20[8 * v13];
      v6[2] = 61;
    }
    else
    {
      v14 = *(_BYTE *)(v7 + a1 + 1);
      v6[1] = *((_BYTE *)v20 + ((16 * v13) | (v14 >> 4)));
      v6[2] = v20[2 * (v14 & 0xF)];
    }
    v15 = v6 + 3;
    *v15 = 61;
    v6 = v15 + 1;
  }
  v16 = 0;
  *v6 = 0;
  v17 = 0;
  v18 = v21;
  while ( v18 == v23[v17] )
  {
    v18 = v22[v17++];
    if ( !v18 )
    {
      do
        *((_BYTE *)a2 + v16++) ^= 0x4Du;
      while ( v16 < 83 );
      a2(a1 + 8);
      return 0;
    }
  }
  return 0;
}

显然是个Base64,解密出来得到realCtF_

第四次解密

然后继续套smc

static decrypt_4()
{
    auto st = 0x00414A30;   
    auto i = 0;
    for (i = 0; i < 83; ++i)
    {
        PatchByte(st + i,Byte(st + i) ^ 0x4D);
    }
}

拿到sub_414A30

int __cdecl sub_414A30(char *a1)
{
  int v1; // ebx
  int v2; // edx
  char *v3; // esi
  char v5[12]; // [esp+Ch] [ebp-Ch] BYREF

  v1 = 0;
  strcpy(v5, "kvtu`C4h\"o");
  v2 = -1;
  do
    ++v2;
  while ( v5[v2] );
  if ( v2 > 0 )
  {
    v3 = a1;
    do
    {
      if ( *v3 != v3[v5 - a1] - 1 )
        break;
      ++v1;
      ++v3;
    }
    while ( v1 < v2 );
  }
  return 0;
}

简单解密一下得到just_B3g!n

综上,得到flag:

flag{The_realCtF_just_B3g!n}

posted @ 2022-05-12 22:09  iPlayForSG  阅读(534)  评论(0)    收藏  举报