2019 上海市大学生网络安全大赛 RE部分WP

此次比赛就作了这一道逆向题,看到队友的WP,下面的对v10的加密方式为RC4,从我提取的v4数组就可以察觉出这是CR4了,本身傻乎乎的用OD调试,跟踪数据半天才作出来,仍是见得的少了... ...下面有几篇不错的RC4的文章:html

C语言实现https://zhoujianshi.github.io/articles/2016/RC4%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/index.htmlc++

Python实现https://specters.top/2019/03/01/Python%E5%AE%9E%E7%8E%B0RC4/git

联系逆向解释https://kabeor.cn/RC4%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95%E5%8F%8A%E9%80%86%E5%90%91%E6%96%B9%E6%B3%95%E5%88%9D%E6%8E%A2/github

 

文件下载:https://www.lanzous.com/i76vcne数组

 

1.准备

获取信息app

  • 32位文件

 

2.IDA打开

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _DWORD *v4; // [esp+1Ch] [ebp-A0h]
  int v5; // [esp+20h] [ebp-9Ch]
  int v6; // [esp+24h] [ebp-98h]
  int v7; // [esp+28h] [ebp-94h]
  int v8; // [esp+2Ch] [ebp-90h]
  char v9; // [esp+30h] [ebp-8Ch]
  int v10; // [esp+50h] [ebp-6Ch]
  int v11; // [esp+70h] [ebp-4Ch]

  sub_402620();
  v5 = 1701148529;
  v6 = 101;
  v7 = 0;
  v8 = 0;
  v4 = malloc(0x408u);
  puts("Plz solve the puzzle:");
  sub_40ED00("%32s", &v9);
  if ( (unsigned __int8)sub_401C70(&v9)
    && (sub_401B60((int)&v10, &v9),
        sub_401850(v4, (int)&v5, strlen((const char *)&v5)),
        sub_4018D0(v4, &v10, 8),
        (unsigned __int8)sub_401950(&v10)) )
  {
    sub_401BA0(&v9, (int)&v11);
    sub_40ED20("Congrats!\n%s\n", &v11);
  }
  else
  {
    puts("Failed!");
  }
  return 0;
}

 

经过分析,要获得flag,则须要if中的条件成立ide

  if ( (unsigned __int8)sub_401C70(&v9)
    && (sub_401B60((int)&v10, &v9),
        sub_401850(v4, (int)&v5, strlen((const char *)&v5)),
        sub_4018D0(v4, &v10, 8),
        (unsigned __int8)sub_401950(&v10)) )

 

3.代码分析

 打开sub_401C70(&v9)函数函数

int __cdecl sub_401C70(char *a1)
{
  char *v2; // edx

  if ( strlen(a1) != 16 )
    return 0;
  v2 = a1;
  while ( (unsigned __int8)(*v2 - 58) > 38u && (unsigned __int8)(*v2 - 48) <= 54u )// 输入的字符串每一个字符都是'a'~'f'或者'0'~'9'之间
  {
    if ( ++v2 == a1 + 16 )                      // 遍历到字符串末尾结束,输入字符串长度为16
      return 1;
  }
  return 0;
}

 

3.1 暴力解v10

由于&&符号后面是( func1, func2, func3)形式的判断条件,所以我先直接看func3便可(func3决定条件是否成立),打开sub_401950(&v10)函数加密

bool __cdecl sub_401950(_BYTE *a1)
{
  _BYTE *v1; // ecx
  bool result; // al

  v1 = a1;
  while ( 2 )
  {
    switch ( *v1 )
    {
      case 0:
        dword_40F028 &= dword_40F038;
        dword_40F02C *= dword_40F028;
        goto LABEL_4;
      case 1:
        if ( !dword_40F02C )
          goto LABEL_6;
        dword_40F028 /= dword_40F02C;
        dword_40F024 += dword_40F034;
        goto LABEL_4;
      case 2:
        dword_40F030 ^= dword_40F034;
        dword_40F03C += dword_40F020;
        goto LABEL_4;
      case 3:
        dword_40F03C -= dword_40F030;
        dword_40F030 &= dword_40F024;
        goto LABEL_4;
      case 4:
        dword_40F034 *= dword_40F020;
        dword_40F02C -= dword_40F038;
        goto LABEL_4;
      case 5:
        dword_40F020 ^= dword_40F02C;
        dword_40F038 -= dword_40F03C;
        goto LABEL_4;
      case 6:
        if ( !dword_40F03C )
          goto LABEL_6;
        dword_40F034 |= dword_40F024 / dword_40F03C;
        dword_40F024 /= dword_40F03C;
        goto LABEL_4;
      case 7:
        dword_40F038 += dword_40F028;
        dword_40F034 |= dword_40F024;
        goto LABEL_4;
      case 8:
        dword_40F020 *= dword_40F02C;
        dword_40F030 -= dword_40F03C;
        goto LABEL_4;
      case 9:
        dword_40F028 += dword_40F034;
        dword_40F02C ^= dword_40F030;
LABEL_4:
        if ( ++v1 != a1 + 8 )
          continue;
        result = (dword_40F038 == 231)
               + (dword_40F034 == 14456)
               + (dword_40F030 == 14961)
               + (dword_40F02C == -13264)
               + (dword_40F028 == 16)
               + (dword_40F024 == 104)
               + (dword_40F020 == -951) == 7;
        if ( dword_40F03C != -239 )
          goto LABEL_6;
        break;
      default:
LABEL_6:
        result = 0;
        break;
    }
    return result;
  }
}

经过分析,这就是将v10的每一位进行switch选择,对一组数据进行更改,最后要知足要求,才能返回1。spa

所以,获得v10的值成为了这道题的关键,又由于v10的每一个数都是0~9之间,且长度为8,我直接暴力求解。

#include <bits/stdc++.h>

#pragma warning(disable:4996)
#define _DWORD unsigned int
#define _BYTE char

using namespace std;

bool __cdecl sub_401950(_BYTE* a1)
{
    _BYTE* v1; // ecx
    bool result; // al

    int val_0 = 0x0A;
    int val_1 = 0x8A;
    int val_2 = 0x1A1;
    int val_3 = 0x12A;
    int val_4 = 0x269;
    int val_5 = 0x209;
    int val_6 = 0x68;
    int val_7 = 0x39F;
    int val_8 = 0x2C8;

    v1 = a1;
    while (2)
    {
        switch (*v1-48)
        {
        case 0:
            val_3 &= val_7;
            val_4 *= val_3;
            goto LABEL_4;
        case 1:
            if (!val_4)
                goto LABEL_6;
            val_3 /= val_4;
            val_2 += val_6;
            goto LABEL_4;
        case 2:
            val_5 ^= val_6;
            val_8 += val_1;
            goto LABEL_4;
        case 3:
            val_8 -= val_5;
            val_5 &= val_2;
            goto LABEL_4;
        case 4:
            val_6 *= val_1;
            val_4 -= val_7;
            goto LABEL_4;
        case 5:
            val_1 ^= val_4;
            val_7 -= val_8;
            goto LABEL_4;
        case 6:
            if (!val_8)
                goto LABEL_6;
            val_6 |= val_2 / val_8;
            val_2 /= val_8;
            goto LABEL_4;
        case 7:
            val_7 += val_3;
            val_6 |= val_2;
            goto LABEL_4;
        case 8:
            val_1 *= val_4;
            val_5 -= val_8;
            goto LABEL_4;
        case 9:
            val_3 += val_6;
            val_4 ^= val_5;
        LABEL_4:
            if (++v1 != a1 + 8)
                continue;
            result = (val_7 == 231)
                + (val_6 == 14456)
                + (val_5 == 14961)
                + (val_4 == -13264)
                + (val_3 == 16)
                + (val_2 == 104)
                + (val_1 == -951) == 7;
            if (val_8 != -239)
                goto LABEL_6;
            break;
        default:
        LABEL_6:
            result = 0;
            break;
        }
        return result;
    }
}

int main()
{
    char* s = (char*)malloc(10);

    for (long i = 10000000; i < 100000000; ++i) {
        sprintf(s, "%08ld", i);
        //cout << s << endl;
        if (sub_401950(s)) {
            cout << i << endl;
            system("PAUSE");
        }
    }
    system("PAUSE");
    return 0;
}

获得v10的值

 

3.2 v9的十六进制加密

获得v10值后,不能得到有关输入字符串的相关信息,打开前面的函数sub_401B60(&v10, &v9)

int __cdecl sub_401B60(int a1, _BYTE *a2)
{
  _BYTE *v2; // ebx
  int i; // esi
  char v4; // ST08_1
  _BYTE *v5; // ST00_4
  int result; // eax

  v2 = a2;
  for ( i = a1; *v2; result = sub_40ED40(v5, "%02X", v4) )
  {
    v4 = i;
    v5 = v2;
    v2 += 2;
    ++i;
  }
  return result;
}

可以猜想到,v9加密为十六进制,存储到v10中,(若是v9="0123456789abcdef",则v10=0x0123456789abcdef。经过后面OD调试,也可以发现。)

 

3.3 v4数组生成

打开sub_401850(v4, &v5, strlen((const char *)&v5))函数

int __cdecl sub_401850(_DWORD *a1, int a2, int a3)
{
  int v3; // eax
  int v4; // edi
  unsigned int *v5; // ebx
  int v6; // ecx
  int result; // eax
  unsigned int v8; // esi
  char v9; // dl
  unsigned int *v10; // edx

  v3 = 0;
  v4 = (int)(a1 + 2);
  *a1 = 0;
  a1[1] = 0;
  do
  {
    *(_DWORD *)(v4 + 4 * v3) = v3;
    ++v3;
  }
  while ( v3 != 256 );
  v5 = a1 + 2;
  v6 = 0;
  LOBYTE(result) = 0;
  do
  {
    v8 = *v5;
    v9 = *(_BYTE *)(a2 + v6++) + *v5;
    result = (unsigned __int8)(v9 + result);
    v10 = (unsigned int *)(v4 + 4 * result);
    *v5 = *v10;
    *v10 = v8;
    if ( v6 >= a3 )
      v6 = 0;
    ++v5;
  }
  while ( v5 != a1 + 258 );
  return result;
}

这里在生成V4数组,我从OD中提取出来

        0x00, 0x00, 0x71, 0x12,
        0x62, 0x31, 0x4D, 0x97,
        0x14, 0x0D, 0xED, 0xA3,
        0xD6, 0xFC, 0xF1, 0x3B,
        0x3C, 0x33, 0xB5, 0x22,
        0xA2, 0x1A, 0x17, 0x1D,
        0x98, 0x91, 0x06, 0x2A,
        0x8B, 0x23, 0xE6, 0x55,
        0x46, 0x3A, 0x65, 0x28,
        0x30, 0x39, 0xD4, 0x0C,
        0x01, 0x2D, 0x25, 0x10,
        0x09, 0x8F, 0x6A, 0x3F,
        0x44, 0xD8, 0x6D, 0xC5,
        0xA6, 0x72, 0x07, 0x83,
        0x40, 0xC6, 0x8E, 0x1F,
        0x77, 0x61, 0x96, 0x4A,
        0x08, 0xFE, 0x53, 0x5A,
        0xA1, 0xDF, 0xB6, 0x67,
        0x66, 0x5C, 0x57, 0xB8,
        0xD3, 0x11, 0x52, 0x21,
        0xCC, 0x56, 0x2E, 0xC2,
        0x88, 0xAA, 0xF9, 0x20,
        0x7A, 0x6F, 0x4E, 0x76,
        0xE8, 0xC1, 0xD5, 0xBD,
        0xCE, 0x9E, 0x38, 0x95,
        0x50, 0xF2, 0x9F, 0xB2,
        0x9A, 0x0B, 0x47, 0x16,
        0x60, 0xBF, 0xFD, 0x92,
        0x35, 0x89, 0xDA, 0xFF,
        0x9B, 0xBA, 0x13, 0xAB,
        0xF4, 0x79, 0x87, 0xAC,
        0x8C, 0x73, 0x84, 0xB3,
        0x0E, 0xC8, 0x26, 0xA5,
        0xE7, 0x15, 0xE9, 0xC3,
        0x69, 0x70, 0xE0, 0x68,
        0x42, 0x81, 0xCD, 0xEB,
        0xDE, 0x7D, 0xEF, 0xD0,
        0x24, 0x00, 0xF0, 0x41,
        0xA0, 0xEE, 0x05, 0x94,
        0x85, 0xBB, 0x43, 0x02,
        0xF7, 0xC0, 0xD1, 0x1B,
        0x7F, 0x5B, 0xEC, 0xF6,
        0x2B, 0x1E, 0xE2, 0x27,
        0xFB, 0x78, 0x54, 0x58,
        0xE4, 0x32, 0xDB, 0xB7,
        0xC7, 0x90, 0x7C, 0xF8,
        0x5D, 0x5F, 0x63, 0xBE,
        0x2C, 0x0A, 0xDD, 0x9C,
        0x75, 0x19, 0xC4, 0xA8,
        0x86, 0x36, 0xBC, 0x8D,
        0xD7, 0x7B, 0xB4, 0x5E,
        0x3E, 0xA7, 0xB1, 0xE1,
        0x59, 0x82, 0xB9, 0xAE,
        0xD9, 0x7E, 0xAF, 0xCF,
        0x9D, 0xF5, 0xFA, 0x48,
        0x4F, 0xA9, 0x6C, 0x64,
        0x6E, 0x49, 0x4B, 0x6B,
        0x29, 0x45, 0xE5, 0x04,
        0xA4, 0x4C, 0x34, 0x80,
        0xD2, 0x3D, 0xE3, 0x99,
        0x37, 0xDC, 0x93, 0xC9,
        0xCA, 0xCB, 0xEA, 0xB0,
        0x0F, 0x03, 0x8A, 0xF3,
        0x51, 0x1C, 0xAD, 0x74,
        0x18, 0x2F
v4数组

 

3.4 v10的加密与解密

打开 sub_4018D0(v4, &v10, 8)函数

_DWORD *__cdecl sub_4018D0(_DWORD *a1, _BYTE *a2, int a3)
{
  int v3; // edx
  int v4; // ecx
  int v5; // esi
  _BYTE *v6; // ebx
  int v7; // edi
  unsigned int *v8; // eax
  int v9; // edx
  _DWORD *v10; // ebp
  _DWORD *v11; // ST00_4
  unsigned int v12; // ebp
  _DWORD *result; // eax

  v3 = *a1;
  v4 = a1[1];
  v5 = (int)(a1 + 2);
  if ( a3 > 0 )
  {
    v6 = a2;
    v7 = *a1;
    do
    {
      v7 = (unsigned __int8)(v7 + 1);
      v8 = (unsigned int *)(v5 + 4 * v7);
      v9 = *v8;
      v4 = (unsigned __int8)(*v8 + v4);
      v10 = (_DWORD *)(v5 + 4 * v4);
      v11 = v10;
      v12 = *v10;
      *v8 = v12;
      *v11 = v9;
      *v6++ ^= *(_DWORD *)(v5 + 4 * (unsigned __int8)(v9 + v12));
    }
    while ( v6 != &a2[a3] );
    v3 = v7;
  }
  result = a1;
  *a1 = v3;
  a1[1] = v4;
  return result;
}

这里利用v4数组,对v10的每一位进行异或运算。

这个函数执行后,就是最后一个函数,刚刚咱们暴力解出了61495072,v10 = '\x06\x01\x04\x09\x05\x00\x07\x02',所以咱们可以对v10的每一位进行异或,获得v10加密前的值。

原本是准备利用提取出的v4,代入函数计算出v10的值,可是没成功,哈哈哈!后面发现v4的值其实是固定的,不会受到输入的影响而且我只须要知道*v6异或的值便可。所以,我在OD中调试,记录异或的值。

 

0x7c, 0x0AB, 0x2D, 0x91, 0x2F, 0x98, 0x0ED, 0xA9

咱们可以写出脚本

num = [0x7c, 0x0AB, 0x2D, 0x91, 0x2F, 0x98, 0x0ED, 0xA9]
v10 = '\x06\x01\x04\x09\x05\x00\x07\x02'

flag = []
n = 0

for i in v10:
    flag.append(hex(ord(i) ^ num[n]))
    n = n + 1
print(flag)

 ['0x7a', '0xaa', '0x29', '0x98', '0x2a', '0x98', '0xea', '0xab']

组合起来,获得v9通过十六进制加密后的v10=0x7aaaa29982a98eaab

所以咱们可以获得v9="7aaa29982a98eaab",输入到程序中获得flag。

 

4.get flag!

flag{5cb92582-66a8-e5b7-d3bf-3b99df8ac7f0}

相关文章
相关标签/搜索