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}

相關文章
相關標籤/搜索