攻防世界ReverseMe-120

发布于:2025-09-08 ⋅ 阅读:(24) ⋅ 点赞:(0)

这道题比较经典,涉及三个知识点,所以记录一下。

首先给了一个文件,detect it easy看了下,是32位exe。

放入ida中,找下main函数,F5反编译看一下伪代码。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // edx
  unsigned int v4; // ecx
  __m128i si128; // xmm1
  unsigned int v6; // esi
  const __m128i *v7; // eax
  __m128i v8; // xmm0
  int v9; // eax
  char v11[100]; // [esp+0h] [ebp-CCh] BYREF
  char v12[100]; // [esp+64h] [ebp-68h] BYREF
  unsigned int v13; // [esp+C8h] [ebp-4h] BYREF

  printf("please input your flah:");
  memset(v11, 0, sizeof(v11));
  scanf("%s", v11);
  memset(v12, 0, sizeof(v12));
  sub_A21000(v12, &v13, (unsigned __int8 *)v11, strlen(v11));
  v3 = v13;
  v4 = 0;
  if ( v13 )
  {
    if ( v13 >= 16 )
    {
      si128 = _mm_load_si128((const __m128i *)&xmmword_A34F20);
      v6 = v13 - (v13 & 0xF);
      v7 = (const __m128i *)v12;
      do
      {
        v8 = _mm_loadu_si128(v7);
        v4 += 16;
        ++v7;
        v7[-1] = _mm_xor_si128(v8, si128);
      }
      while ( v4 < v6 );
    }
    for ( ; v4 < v3; ++v4 )
      v12[v4] ^= 0x25u;
  }
  v9 = strcmp(v12, "you_know_how_to_remove_junk_code");
  if ( v9 )
    v9 = v9 < 0 ? -1 : 1;
  if ( v9 )
    printf("wrong\n");
  else
    printf("correct\n");
  system("pause");
  return 0;
}

从后往前看,要得到correct结果,就需要v12和字符串you_know_how_to_remove_junk_code相等。

v12和v13先是和输入的v11进行了一个函数sub_A21000的处理,然后又做了一段包含异或0X25的处理。

先看第一个函数。

知识点1:这里其实是一个base64解密函数。

int __fastcall sub_A21000(_BYTE *a1, unsigned int *a2, unsigned __int8 *a3, unsigned int a4)     //a1是v12,a2是v13值,a3是输入数据,a4是输入数据长度。
{
  int v4; // ebx
  unsigned int v5; // eax
  int v6; // ecx
  unsigned __int8 *v7; // edi
  int v8; // edx
  bool v9; // zf
  unsigned __int8 v10; // cl
  char v11; // cl
  _BYTE *v12; // esi
  unsigned int v13; // ecx
  int v14; // ebx
  unsigned __int8 v15; // cl
  char v16; // dl
  int v20; // [esp+14h] [ebp-4h]
  unsigned int v21; // [esp+14h] [ebp-4h]
  int i; // [esp+24h] [ebp+Ch]

  v4 = 0;       // 记录 '=' 出现的次数
  v5 = 0;// 当前输入索引(扫描位置)
  v6 = 0;  // 有效 Base64 数据字符数(不包括空格、换行、=)
  v20 = 0;// 同 v6,用于避免寄存器冲突(编译器优化痕迹)
  if ( !a4 )     //长度为0就退出
    return 0;
  v7 = a3;
  do
  {
    v8 = 0;
    v9 = v5 == a4;
    if ( v5 < a4 )
    {
      do
      {
        if ( a3[v5] != 32 )// 跳过前导空格
          break;
        ++v5;
        ++v8;
      }
      while ( v5 < a4 );
      v9 = v5 == a4;
    }
    if ( v9 )
      break;
    if ( a4 - v5 >= 2 && a3[v5] == 13 && a3[v5 + 1] == 10 || (v10 = a3[v5], v10 == 10) )// 遇到换行继续
    {
      v6 = v20;
    }
    else
    {
      if ( v8 )// Base64 中间不能有空格,返回错误
        return -44;
      if ( v10 == 61 && (unsigned int)++v4 > 2 )// 等于号的个数比2个多,非法
        return -44;
      if ( v10 > 0x7Fu )// 检查字符是否超出 ASCII 127
        return -44;
      v11 = byte_A34E40[v10];
      if ( v11 == 0x7F || (unsigned __int8)v11 < 0x40u && v4 )
        return -44;
      v6 = ++v20;
    }
    ++v5;
  }
  while ( v5 < a4 );
  if ( !v6 )
    return 0;
  v12 = a1;
  v13 = ((unsigned int)(6 * v6 + 7) >> 3) - v4;  //这里用的是通用公式,取字节数减去填充 = 的数量
  if ( a1 && *a2 >= v13 )
  {
    v21 = 3;      // 每组 4 个 Base64 字符生成最多 3 字节
    v14 = 0;
    for ( i = 0; v5; --v5 )
    {
      v15 = *v7;
      if ( *v7 != '\r' && v15 != '\n' && v15 != ' ' )
      {
        v16 = byte_A34E40[v15];   //byte_A34E40是base64的字母表
        v21 -= v16 == 64;
        v14 = v16 & '?' | (v14 << 6);   //累加 6 位
        if ( ++i == 4 )   //每 4 个字符为一组
        {
          i = 0;
          if ( v21 )
            *v12++ = BYTE2(v14);   //高字节
          if ( v21 > 1 )
            *v12++ = BYTE1(v14);//中字节
          if ( v21 > 2 )
            *v12++ = v14;//低字节
        }
      }
      ++v7;
    }
    *a2 = v12 - a1;
    return 0;
  }
  *a2 = v13;
  return -42;
}

确定好该函数后,右击rename函数为base64decode方便查看,继续看代码。

v3 = v13;
  v4 = 0;
  if ( v13 )
  {
    if ( v13 >= 16 )
    {
      si128 = _mm_load_si128((const __m128i *)&xmmword_A34F20);
      v6 = v13 - (v13 & 0xF);
      v7 = (const __m128i *)v12;
      do
      {
        v8 = _mm_loadu_si128(v7);
        v4 += 16;
        ++v7;
        v7[-1] = _mm_xor_si128(v8, si128);
      }
      while ( v4 < v6 );
    }
    for ( ; v4 < v3; ++v4 )
      v12[v4] ^= 0x25u;
  }
  v9 = strcmp(v12, "you_know_how_to_remove_junk_code");

知识点2:这里是使用了SSE指令,即带 _mm_前缀,可以通过在一个控制器上同时处理多个数据流,从而提高运算速度。

_mm_load_si128函数表示从内存中加载一个128bits值到暂存器,也就是16字节,**注意:**p必须是一个16字节对齐的一个变量的地址。返回可以存放在代表寄存器的变量中的值。
_mm_loadu_si128函数和_mm_load_si128一样的,但是不要求地址p是16字节对齐。

v6 = v13 - (v13 & 0xF);

这个是先把v13进行对齐,否则无法使用SSE指令处理。

v4 += 16;

这里是取n个16位的v12和xmmword_A34F20进行异或,剩下的不足16位的就和0x25异或。

双击查看xmmword_A34F20,发现其就是16位的0x25。

.rdata:00A34F20 xmmword_A34F20  xmmword 25252525252525252525252525252525h

所以,就是输入值进行base64解密后,和0x25异或,最后和you_know_how_to_remove_junk_code字符串比较是否一致。

import base64
s=list("you_know_how_to_remove_junk_code")
encrypted_bytes = bytes(ord(c) ^ 0x25 for c in s)
decoded_bytes = base64.b64encode(encrypted_bytes).decode('ascii')
print(decoded_bytes)

这里还有一个我犯的错误点是把字符转ord异或后,应该转为byte字节,再进行base64加密,但是我转的是chr,这会导致一部分数据无法转为chr,从而导致错误。

知识点3:加密、解密、计算哈希、编码等都应该使用byte字节形式相互转化。


网站公告

今日签到

点亮在社区的每一天
去签到