ISCC2019 Reverse dig dig dig writeup

本题主要考察一些常用的加密方法。

分析

__int64 __fastcall main(int argc, char **argv, char **a3)
{
  size_t v3; // rax
  char *dest; // ST18_8
  size_t length; // rax
  char *v6; // rax
  char *v7; // rax
  const char *v8; // ST18_8

  if ( argc != 2 )
  {
    puts("./dec_dec_dec flag_string_is_here ");
    exit(0);
  }
  v3 = strlen(argv[1]);
  dest = (char *)malloc(v3 + 1);
  length = strlen(argv[1]);
  strncpy(dest, argv[1], length);
  v6 = base64_encode(dest);
  v7 = rot13(v6);
  v8 = mapping_table(v7);
  if ( !strcmp(v8, s2) )
    puts("correct  :)");
  else
    puts("incorrect :(");
  return 0LL;
}

程序总共有三次加密,加密完成和字符串@1DE!440S9W9,2T%Y07=%<W!Z.3!:1T%S2S-),7-$/3T (s2)进行比较,只要比较成功即可,前两次加密很容易就能分析出来,一个是base64加密,一个是rot13加密。最后一个加密函数是我没见过的,其反汇编如下:

mapping_table

char *__fastcall mapping_table(const char *a1)
{
  unsigned int v1; // ST1C_4
  _BYTE *v2; // rax
  _BYTE *v3; // rax
  char v4; // dl
  _BYTE *v5; // rax
  char v6; // cl
  _BYTE *v7; // rax
  char v8; // cl
  _BYTE *v9; // rax
  char v10; // dl
  _BYTE *v11; // rax
  char v12; // dl
  char *v13; // rax
  char v14; // dl
  _BYTE *v15; // rax
  char v16; // cl
  _BYTE *v17; // rax
  char v18; // cl
  _BYTE *v19; // rax
  char v20; // dl
  char *s; // [rsp+8h] [rbp-88h]
  unsigned int i; // [rsp+10h] [rbp-80h]
  signed int v24; // [rsp+14h] [rbp-7Ch]
  unsigned int v25; // [rsp+18h] [rbp-78h]
  signed __int64 v26; // [rsp+20h] [rbp-70h]
  signed __int64 v27; // [rsp+20h] [rbp-70h]
  signed __int64 v28; // [rsp+20h] [rbp-70h]
  signed __int64 v29; // [rsp+20h] [rbp-70h]
  char *v30; // [rsp+20h] [rbp-70h]
  signed __int64 v31; // [rsp+20h] [rbp-70h]
  signed __int64 v32; // [rsp+20h] [rbp-70h]
  signed __int64 v33; // [rsp+20h] [rbp-70h]
  char *v34; // [rsp+28h] [rbp-68h]

  s = (char *)a1;
  v1 = strlen(a1);
  v34 = (char *)malloc(4 * v1 / 3 + 1);
  v26 = (signed __int64)v34;
  for ( i = v1; i > 0x2D; i -= 45 )
  {
    v2 = (_BYTE *)v26++;
    *v2 = 77;
    v24 = 0;
    while ( v24 <= 44 )
    {
      v3 = (_BYTE *)v26;
      v27 = v26 + 1;
      if ( *s >> 2 )
        v4 = ((unsigned __int8)*s >> 2) + 32;
      else
        v4 = 32;
      *v3 = v4;
      v5 = (_BYTE *)v27;
      v28 = v27 + 1;
      if ( 16 * *s & 0x30 )
        v6 = (16 * *s & 0x30) + 32;
      else
        v6 = 32;
      *v5 = v6 | ((unsigned __int8)s[1] >> 4);
      v7 = (_BYTE *)v28;
      v29 = v28 + 1;
      if ( 4 * s[1] & 0x3C )
        v8 = (4 * s[1] & 0x3C) + 32;
      else
        v8 = 32;
      *v7 = v8 | ((unsigned __int8)s[2] >> 6);
      v9 = (_BYTE *)v29;
      v26 = v29 + 1;
      if ( s[2] & 0x3F )
        v10 = (s[2] & 0x3F) + 32;
      else
        v10 = 32;
      *v9 = v10;
      v24 += 3;
      s += 3;
    }
  }
  v11 = (_BYTE *)v26;
  v30 = (char *)(v26 + 1);
  if ( i )
    v12 = (i & 0x3F) + 32;
  else
    v12 = 32;
  *v11 = v12;
  v25 = 0;
  while ( v25 < i )
  {
    v13 = v30;
    v31 = (signed __int64)(v30 + 1);
    if ( *s >> 2 )
      v14 = ((unsigned __int8)*s >> 2) + 32;
    else
      v14 = 32;
    *v13 = v14;
    v15 = (_BYTE *)v31;
    v32 = v31 + 1;
    if ( 16 * *s & 0x30 )
      v16 = (16 * *s & 0x30) + 32;
    else
      v16 = 32;
    *v15 = v16 | ((unsigned __int8)s[1] >> 4);
    v17 = (_BYTE *)v32;
    v33 = v32 + 1;
    if ( 4 * s[1] & 0x3C )
      v18 = (4 * s[1] & 0x3C) + 32;
    else
      v18 = 32;
    *v17 = v18 | ((unsigned __int8)s[2] >> 6);
    v19 = (_BYTE *)v33;
    v30 = (char *)(v33 + 1);
    if ( s[2] & 0x3F )
      v20 = (s[2] & 0x3F) + 32;
    else
      v20 = 32;
    *v19 = v20;
    v25 += 3;
    s += 3;
  }
  *v30 = 0;
  return v34;
}

首先猜测该函数是一个一一映射的函数,再根据v34 = (char *)malloc(4 * v1 / 3 + 1);猜测是一个3字节4字节的映射规则,然后再利用mapping_table函数进行分析。

提取出来的代码:

#define _BYTE char
#define __int64 long long
#define __int8 char
char *mapping_table(const char *a1)
{
    unsigned int v1;    // ST1C_4
    _BYTE *v2;          // rax
    _BYTE *v3;          // rax
    char v4;            // dl
    _BYTE *v5;          // rax
    char v6;            // cl
    _BYTE *v7;          // rax
    char v8;            // cl
    _BYTE *v9;          // rax
    char v10;           // dl
    _BYTE *v11;         // rax
    char v12;           // dl
    char *v13;          // rax
    char v14;           // dl
    _BYTE *v15;         // rax
    char v16;           // cl
    _BYTE *v17;         // rax
    char v18;           // cl
    _BYTE *v19;         // rax
    char v20;           // dl
    char *s;            // [rsp+8h] [rbp-88h]
    unsigned int i;     // [rsp+10h] [rbp-80h]
    signed int v24;     // [rsp+14h] [rbp-7Ch]
    unsigned int v25;   // [rsp+18h] [rbp-78h]
    signed __int64 v26; // [rsp+20h] [rbp-70h]
    signed __int64 v27; // [rsp+20h] [rbp-70h]
    signed __int64 v28; // [rsp+20h] [rbp-70h]
    signed __int64 v29; // [rsp+20h] [rbp-70h]
    char *v30;          // [rsp+20h] [rbp-70h]
    signed __int64 v31; // [rsp+20h] [rbp-70h]
    signed __int64 v32; // [rsp+20h] [rbp-70h]
    signed __int64 v33; // [rsp+20h] [rbp-70h]
    char *v34;          // [rsp+28h] [rbp-68h]

    s = (char *)a1;
    v1 = strlen(a1);
    v34 = (char *)malloc(4 * v1 / 3 + 1);
    v26 = (signed __int64)v34;
    for (i = v1; i > 0x2D; i -= 45)
    {
        v2 = (_BYTE *)v26++;
        *v2 = 77;
        v24 = 0;
        while (v24 <= 44)
        {
            v3 = (_BYTE *)v26;
            v27 = v26 + 1;
            if (*s >> 2)
                v4 = ((unsigned __int8)*s >> 2) + 32;
            else
                v4 = 32;
            *v3 = v4;
            v5 = (_BYTE *)v27;
            v28 = v27 + 1;
            if (16 * *s & 0x30)
                v6 = (16 * *s & 0x30) + 32;
            else
                v6 = 32;
            *v5 = v6 | ((unsigned __int8)s[1] >> 4);
            v7 = (_BYTE *)v28;
            v29 = v28 + 1;
            if (4 * s[1] & 0x3C)
                v8 = (4 * s[1] & 0x3C) + 32;
            else
                v8 = 32;
            *v7 = v8 | ((unsigned __int8)s[2] >> 6);
            v9 = (_BYTE *)v29;
            v26 = v29 + 1;
            if (s[2] & 0x3F)
                v10 = (s[2] & 0x3F) + 32;
            else
                v10 = 32;
            *v9 = v10;
            v24 += 3;
            s += 3;
        }
    }
    v11 = (_BYTE *)v26;
    v30 = (char *)(v26 + 1);
    if (i)
        v12 = (i & 0x3F) + 32;
    else
        v12 = 32;
    *v11 = v12;
    v25 = 0;
    while (v25 < i)
    {
        v13 = v30;
        v31 = (signed __int64)(v30 + 1);
        if (*s >> 2)
            v14 = ((unsigned __int8)*s >> 2) + 32;
        else
            v14 = 32;
        *v13 = v14;
        v15 = (_BYTE *)v31;
        v32 = v31 + 1;
        if (16 * *s & 0x30)
            v16 = (16 * *s & 0x30) + 32;
        else
            v16 = 32;
        *v15 = v16 | ((unsigned __int8)s[1] >> 4);
        v17 = (_BYTE *)v32;
        v33 = v32 + 1;
        if (4 * s[1] & 0x3C)
            v18 = (4 * s[1] & 0x3C) + 32;
        else
            v18 = 32;
        *v17 = v18 | ((unsigned __int8)s[2] >> 6);
        v19 = (_BYTE *)v33;
        v30 = (char *)(v33 + 1);
        if (s[2] & 0x3F)
            v20 = (s[2] & 0x3F) + 32;
        else
            v20 = 32;
        *v19 = v20;
        v25 += 3;
        s += 3;
    }
    *v30 = 0;
    return v34;
}#define _BYTE char
#define __int64 long long
#define __int8 char
char *mapping_table(const char *a1)
{
    unsigned int v1;    // ST1C_4
    _BYTE *v2;          // rax
    _BYTE *v3;          // rax
    char v4;            // dl
    _BYTE *v5;          // rax
    char v6;            // cl
    _BYTE *v7;          // rax
    char v8;            // cl
    _BYTE *v9;          // rax
    char v10;           // dl
    _BYTE *v11;         // rax
    char v12;           // dl
    char *v13;          // rax
    char v14;           // dl
    _BYTE *v15;         // rax
    char v16;           // cl
    _BYTE *v17;         // rax
    char v18;           // cl
    _BYTE *v19;         // rax
    char v20;           // dl
    char *s;            // [rsp+8h] [rbp-88h]
    unsigned int i;     // [rsp+10h] [rbp-80h]
    signed int v24;     // [rsp+14h] [rbp-7Ch]
    unsigned int v25;   // [rsp+18h] [rbp-78h]
    signed __int64 v26; // [rsp+20h] [rbp-70h]
    signed __int64 v27; // [rsp+20h] [rbp-70h]
    signed __int64 v28; // [rsp+20h] [rbp-70h]
    signed __int64 v29; // [rsp+20h] [rbp-70h]
    char *v30;          // [rsp+20h] [rbp-70h]
    signed __int64 v31; // [rsp+20h] [rbp-70h]
    signed __int64 v32; // [rsp+20h] [rbp-70h]
    signed __int64 v33; // [rsp+20h] [rbp-70h]
    char *v34;          // [rsp+28h] [rbp-68h]

    s = (char *)a1;
    v1 = strlen(a1);
    v34 = (char *)malloc(4 * v1 / 3 + 1);
    v26 = (signed __int64)v34;
    for (i = v1; i > 0x2D; i -= 45)
    {
        v2 = (_BYTE *)v26++;
        *v2 = 77;
        v24 = 0;
        while (v24 <= 44)
        {
            v3 = (_BYTE *)v26;
            v27 = v26 + 1;
            if (*s >> 2)
                v4 = ((unsigned __int8)*s >> 2) + 32;
            else
                v4 = 32;
            *v3 = v4;
            v5 = (_BYTE *)v27;
            v28 = v27 + 1;
            if (16 * *s & 0x30)
                v6 = (16 * *s & 0x30) + 32;
            else
                v6 = 32;
            *v5 = v6 | ((unsigned __int8)s[1] >> 4);
            v7 = (_BYTE *)v28;
            v29 = v28 + 1;
            if (4 * s[1] & 0x3C)
                v8 = (4 * s[1] & 0x3C) + 32;
            else
                v8 = 32;
            *v7 = v8 | ((unsigned __int8)s[2] >> 6);
            v9 = (_BYTE *)v29;
            v26 = v29 + 1;
            if (s[2] & 0x3F)
                v10 = (s[2] & 0x3F) + 32;
            else
                v10 = 32;
            *v9 = v10;
            v24 += 3;
            s += 3;
        }
    }
    v11 = (_BYTE *)v26;
    v30 = (char *)(v26 + 1);
    if (i)
        v12 = (i & 0x3F) + 32;
    else
        v12 = 32;
    *v11 = v12;
    v25 = 0;
    while (v25 < i)
    {
        v13 = v30;
        v31 = (signed __int64)(v30 + 1);
        if (*s >> 2)
            v14 = ((unsigned __int8)*s >> 2) + 32;
        else
            v14 = 32;
        *v13 = v14;
        v15 = (_BYTE *)v31;
        v32 = v31 + 1;
        if (16 * *s & 0x30)
            v16 = (16 * *s & 0x30) + 32;
        else
            v16 = 32;
        *v15 = v16 | ((unsigned __int8)s[1] >> 4);
        v17 = (_BYTE *)v32;
        v33 = v32 + 1;
        if (4 * s[1] & 0x3C)
            v18 = (4 * s[1] & 0x3C) + 32;
        else
            v18 = 32;
        *v17 = v18 | ((unsigned __int8)s[2] >> 6);
        v19 = (_BYTE *)v33;
        v30 = (char *)(v33 + 1);
        if (s[2] & 0x3F)
            v20 = (s[2] & 0x3F) + 32;
        else
            v20 = 32;
        *v19 = v20;
        v25 += 3;
        s += 3;
    }
    *v30 = 0;
    return v34;
}

具体分析代码如下:

mapping_table.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char *mapping_table(const char *a1);
int main(int argc, char const *argv[])
{
    puts(mapping_table(argv[1]));

    return 0;
}

运行结果:

ex@Ex:~/test$ gcc -O3 mapping_table.c -o mapping_table
ex@Ex:~/test$ ./mapping_table abc
#86)C
ex@Ex:~/test$ ./mapping_table abcabc
&86)C86)C
ex@Ex:~/test$ ./mapping_table abcabcabc
)86)C86)C86)C
ex@Ex:~/test$ ./mapping_table abcabcabc123
,86)C86)C86)C,3(S
ex@Ex:~/test$ ./mapping_table abcabcabc123123
/86)C86)C86)C,3(S,3(S

虽然不知道第一个字符有什么用,但是不影响我们还原字符串,然后利用该函数生成对应的映射表,进行反向查询就可以还原原来的字符。

解密

creat_mapping_table.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char *mapping_table(const char *a1);
int main(int argc, char const *argv[])
{
    char *table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    int table_length = 64;
    char buf[4];
    int store;
    char *temp;

    puts("[\n");
    for (int i = 0; i < table_length; i++)
    {
        buf[0] = table[i];
        for (int ii = 0; ii < table_length; ii++)
        {
            buf[1] = table[ii];
            for (int iii = 0; iii < table_length; iii++)
            {
                buf[2] = table[iii];
                buf[3] = '\0';
                temp = mapping_table(buf);
                store = *(int *)buf;
                printf("{\"%s\":\t\"%s\"},\n", temp + 1, (char *)&store);
                free(temp);
            }
        }
    }
    puts("]\n");

    return 0;
}

运行结果:

ex@Ex:~/test$ gcc -O3 creat_mapping_table.c -o creat_mapping_table
ex@Ex:~/test$ ./creat_mapping_table > table

生成了映射文件table,在把我们的字符串@1DE!440S9W9,2T%Y07=%<W!Z.3!:1T%S2S-),7-$/3T 带到映射表中去查找(注意第一个字符是要被舍去的),虽然最后四个字符/3T 在映射表中找不到,但是不影响我们拿flag,查找完之后得到字符串FIAQD3gvLKAyAwEspz90ZGAsK3I1sD,进行rot13解密得到字符串SVNDQ3tiYXNlNjRfcm90MTNfX3V1fQ。然后进行base64解密即可拿到flag。

原本想直接生成json文件放到Python里面处理,但是由于文件有太多的非常字符要处理,所以就只能手动查找,鉴于要查找的字符串不长,所以花的时间也不多。

ex@Ex:~/test$ echo -n SVNDQ3tiYXNlNjRfcm90MTNfX3V1fQ | base64 -d
ISCC{base64_rot13__uu}base64: invalid input

总结

主要是对一些常用的加密算法要了解,一些不常用的加密算法也是比较容易进行还原的,因为逆向的很多加密算法遵循一一对应的原则,所以我们即使我们不知道加密的具体原理,但是只要我们能生成对照表,就能通过对照表进行还原。