TSCTF-J逆向WP(待更新)

记得TSCTF-J刚开始的时候我连IDA都没下,easyxor对着代码瞅了半天,幸好ctrl+F5可以直接显示出数组的内容,让我似懂非懂的把签到题过了,赛后重新来看这些题,或许会有一些新的理解吧。

easy-xor

新生赛光速入门RE,刚刚下载IDApro之后打开的第一道题。
F5查看代码:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _BYTE *v3; // esi
  int i; // edx
  int v5; // eax
  char *v7; // eax
  char v8; // [esp-4h] [ebp-Ch]
  char v9; // [esp+0h] [ebp-8h]
  char v10; // [esp+0h] [ebp-8h]

  sub_401020("Welcome to TSCTF-J 2021!!!!!
", v9);
  sub_401020("Please Input your flag:", v8);
  v3 = malloc(0x50u);
  sub_401050("%s", (char)v3);
  for ( i = 0; i < 8; ++i )
  {
    if ( aTsctfJ[i] != v3[i] )
    {
      sub_401020("Wrong flag! Flag must contain "TSCTF-J"
", v10);
      return 0;
    }
  }
  v5 = 0;
  while ( (v5 ^ (char)v3[v5] ^ 0xD) == byte_4021D8[v5] )
  {
    if ( ((v5 + 1) ^ (char)v3[v5 + 1] ^ 0xD) != byte_4021D9[v5] )
    {
      ++v5;
      break;
    }
    if ( ((v5 + 2) ^ (char)v3[v5 + 2] ^ 0xD) != byte_4021DA[v5] )
    {
      v5 += 2;
      break;
    }
    if ( ((v5 + 3) ^ (char)v3[v5 + 3] ^ 0xD) != byte_4021DB[v5] )
    {
      v5 += 3;
      break;
    }
    v5 += 4;
    if ( v5 >= 44 )
      break;
  }
  if ( v5 != 44 || (v7 = "You are so genius!!! Your input is the right flag.
", v3[44]) )
    v7 = "Wrong flag! Please Try Again.
";
  sub_401020(v7, v10);
  system("pause");
  return 0;
}

一开始啥也不知道,甚至不知道byte开头的是几个数组,然后就慢慢看,最后是通过ctrl+F5导出C代码才发现的。
赛后复现,有了一些新理解。
首先
(D8,D9,DA)三个字符串里只有一个量,而且地址是连续的。那么相当于(D8[i+3]=D9[i+2]=DA[i+1]=DB[i])
因此原来的代码
其实相当于(D8[v5],D8[v5+1],D8[v5],D8[v5+3])。想到这一层,题目就好理解了。其实就相当于是每一位都必须满足该异或式,否则会提前break。
当然,通过通过快捷键A可以直接把这四个数组合并成一个字符串,同时源代码也改变了:


验证了之前的推测,根据(aoplus boplus c=d <=> a=boplus coplus d)写脚本:

#include<bits/stdc++.h>
using namespace std;
char key[60] =
{
    'Y','_','L','Z','O','%','A','q','R','a','K','e','1',
    'M','f',']','/','C','K','M','Z','L',']','7','_','K',
    'O','&','c','O','x7F','a','r','x7F','x1F','q','l',
    'I','x','s','x04','x05','x06','[','','','',
    '','','','','','','','',''
};

char flag[60];
int main()
{

    for(int i=43;i>=0;i--)flag[i]=i^0xD^key[i];
    for(int i=0;i<44;i++)cout<<flag[i];
}

得到flag

debugMe

拖入IDA64:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int i; // [rsp+Ch] [rbp-584h]
  int i_0; // [rsp+10h] [rbp-580h]
  int *ptr; // [rsp+18h] [rbp-578h]
  int key[4]; // [rsp+30h] [rbp-560h] BYREF
  uint8 enflag[56]; // [rsp+40h] [rbp-550h] BYREF
  char input[256]; // [rsp+80h] [rbp-510h] BYREF
  uint8 buffer[1024]; // [rsp+180h] [rbp-410h] BYREF
  unsigned __int64 v11; // [rsp+588h] [rbp-8h]

  v11 = __readfsqword(0x28u);
  puts("Welcome to TSCTF-J, wish you can catch the flag");
  printf("Please input your flag:");
  __isoc99_scanf("%s", input);
  if ( strlen(input) == 40 )
  {
    *(_QWORD *)buffer = 0LL;
    *(_QWORD *)&buffer[8] = 0LL;
    memset(&buffer[16], 0, 0x3F0uLL);
    key[0] = 1734437990;
    key[1] = 1801545339;
    key[2] = 1818648421;
    key[3] = 2099341153;
    *(_QWORD *)enflag = 0x692B9F24C93ECBB0LL;
    *(_QWORD *)&enflag[8] = 0x4C84B46449C15A4ELL;
    *(_QWORD *)&enflag[16] = 0x7BDFB74A90AED151LL;
    *(_QWORD *)&enflag[24] = 0x2954405AD89B1055LL;
    *(_QWORD *)&enflag[32] = 0xFA7ECCD6EBB7FD3ELL;
    *(_QWORD *)&enflag[40] = 0x84E7B0309D4C973FLL;
    *(_QWORD *)&enflag[48] = 0xBACEB9973E970C85LL;
    i = 7;
    ptr = (int *)enflag;
    do
    {
      decrypt(ptr, key);
      ptr += 2;
      --i;
    }
    while ( i );
    buffer[(int)base64_decode(enflag, 0x38uLL, buffer)] = 0;
    for ( i_0 = 0; (unsigned __int64)i_0 < 0x28; ++i_0 )
    {
      if ( input[i_0] != buffer[i_0] )
      {
        printf("Sorry, plz try again");
        return 0;
      }
    }
    printf("Jesus, You are so handsome!!");
    result = 0;
  }
  else
  {
    printf("Sorry, plz try again");
    result = 0;
  }
  return result;
}

查了一下,_QWORD是8字节,范围是([0,2^{64}])对应的是unsigned long long类型。
uint8是1个字节,范围([0,255]),对应unsigned char类型。
uint 32是4个字节,范围([0,2^{32}-1]),对应unsgined int类型。

注意到这里:

每次将一个int64位整形赋值给一个uint8位整形,那么相当于占据了8个enflag数组下标,数了一下赋值的16进制数正好是长度为16,也就是enflag每个下标对应一个16进制数,于是得到了enflag数组。

接下来分析程序:

    i = 7;
    ptr = (int *)enflag;
    do
    {
      decrypt(ptr, key);
      ptr += 2;
      --i;
    }
    while ( i );
    buffer[(int)base64_decode(enflag, 0x38uLL, buffer)] = 0;

首先一个int型的指针ptr=&enflag[0],也就是说ptr等于enflag中前4个16进制数,接着进入decrypt函数:

void __cdecl decrypt(int *v, int *k)
{
  int v0; // [rsp+1Ch] [rbp-24h]
  int v1; // [rsp+20h] [rbp-20h]
  int sum; // [rsp+24h] [rbp-1Ch]
  int i; // [rsp+28h] [rbp-18h]

  v0 = *v;
  v1 = v[1];
  sum = -957401312;
  for ( i = 0; i <= 31; ++i )
  {
    v1 -= (v0 + sum) ^ ((v0 >> 5) + k[3]) ^ (16 * v0 + k[2]);
    v0 -= (v1 + sum) ^ ((v1 >> 5) + k[1]) ^ (16 * v1 + *k);
    sum += 1640531527;
  }
  *v = v0;
  v[1] = v1;
}

注意到有v0和v1两个int型变量,结合之前的分析,v0应该是enflag的前四个16进制数,v1应该是enflag的后四个16进制数。

然后进行decrypt运算,之后ptr+=2,也就是跳过了8个16进制数,其实相当于enflag[i+=8]。
最后进行base64_decode得到flag。
当然,其实也可以完全不这样分析直接照抄代码得到flag,或者通过input长度为40这个条件直接动态调试得到base64_decode之后的buffer字符串的值。
但是由于Linux下的GDB实在是太难用了,我完全不会用gdb跟踪并查看buffer的值,并且用IDA上的远程调试连接虚拟机一直不成功(我很难李姐),所以选择了写脚本直接得到flag。

#include<iostream>
#include<cstdio>
using namespace std;
#define _QWORD unsigned long long
#define uint8 unsigned char
#define uint32 unsigned int
//通过宏定义保证变量类型是一样的
uint8 reverse_map[128] =
{
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  36u,
  255u,
  255u,
  255u,
  37u,
  26u,
  27u,
  28u,
  29u,
  30u,
  31u,
  32u,
  33u,
  34u,
  35u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  0u,
  1u,
  2u,
  3u,
  4u,
  5u,
  6u,
  7u,
  8u,
  9u,
  10u,
  11u,
  12u,
  13u,
  14u,
  15u,
  16u,
  17u,
  18u,
  19u,
  20u,
  21u,
  22u,
  23u,
  24u,
  25u,
  255u,
  255u,
  255u,
  255u,
  255u,
  255u,
  38u,
  39u,
  40u,
  41u,
  42u,
  43u,
  44u,
  45u,
  46u,
  47u,
  48u,
  49u,
  50u,
  51u,
  52u,
  53u,
  54u,
  55u,
  56u,
  57u,
  58u,
  59u,
  60u,
  61u,
  62u,
  63u,
  255u,
  255u,
  255u,
  255u,
  255u
};

void decrypt(int *v, int *k)
{
  int v0;
  int v1;
  int sum;
  int i;
  v0 = *v;
  v1 = v[1];
  sum = -957401312;
  for ( i = 0; i <= 31; ++i )
  {
    v1 -= (v0 + sum) ^ ((v0 >> 5) + k[3]) ^ (16 * v0 + k[2]);
    v0 -= (v1 + sum) ^ ((v1 >> 5) + k[1]) ^ (16 * v1 + *k);
    sum += 1640531527;
  }
  *v = v0;
  v[1] = v1;
}

uint32 base64_decode(const uint8 *code, uint32 code_len, uint8 *plain)
{
  uint32 v3;
  uint32 v4;
  uint32 v5;
  uint32 i;
  uint32 j;
  uint32 k;
  uint8 quad[4];
  j = 0LL;
  for ( i = 0LL; i < code_len; i += 4LL )
  {
    for ( k = 0LL; k <= 3; ++k )
      quad[k] = reverse_map[code[k + i]];
    v3 = j++;
    plain[v3] = (quad[1] >> 4) | (4 * quad[0]);
    if ( quad[2] > 0x3Fu )
      break;
    if ( quad[3] > 0x3Fu )
    {
      v4 = j++;
      plain[v4] = (quad[2] >> 2) | (16 * quad[1]);
      return j;
    }
    plain[j] = (quad[2] >> 2) | (16 * quad[1]);
    v5 = j + 1;
    j += 2LL;
    plain[v5] = quad[3] | (quad[2] << 6);
  }
  return j;
}
int result;
int i;
int i_0;
int *ptr;
int key[4];
uint8 enflag[56];
uint8 buffer[1024];
int main()
{
    key[0] = 1734437990;
    key[1] = 1801545339;
    key[2] = 1818648421;
    key[3] = 2099341153;
    *(_QWORD *)enflag = 0x692B9F24C93ECBB0LL;
    *(_QWORD *)&enflag[8] = 0x4C84B46449C15A4ELL;
    *(_QWORD *)&enflag[16] = 0x7BDFB74A90AED151LL;
    *(_QWORD *)&enflag[24] = 0x2954405AD89B1055LL;
    *(_QWORD *)&enflag[32] = 0xFA7ECCD6EBB7FD3ELL;
    *(_QWORD *)&enflag[40] = 0x84E7B0309D4C973FLL;
    *(_QWORD *)&enflag[48] = 0xBACEB9973E970C85LL;
    i = 7;
    ptr = (int *)enflag;
    do
    {
      decrypt(ptr, key);
      ptr += 2;
      --i;
    }
    while ( i );
    buffer[(int)base64_decode(enflag, 0x38uLL, buffer)] = 0;
    for(int i=0;i<40;i++)cout<<(char)buffer[i];
}

flag:

UpxRand

打开exe文件发现是一张图片,然后图片展开越来越慢,令人捉急。

先查壳

发现有壳,用upx脱壳,然后就是套路题,扔进IDA64,发现有个sleep函数 考虑修改汇编把它直接nop掉。
apply这个更改之后重新打开.exe文件

扫码得到flag。

maze

先拖到IDA,得到源代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // edi
  int v4; // esi
  unsigned int v5; // esi
  unsigned int v6; // edi
  int v7; // eax
  _BYTE *v8; // ebx
  unsigned int v9; // ecx
  unsigned int v10; // edx
  char v11; // al
  unsigned int v12; // eax
  int v14; // [esp-Ch] [ebp-78h]
  int v15; // [esp-8h] [ebp-74h]
  char v16; // [esp+0h] [ebp-6Ch]
  __int128 v17[6]; // [esp+4h] [ebp-68h]
  int v18; // [esp+64h] [ebp-8h]

  *((_QWORD *)&v17[0] + 1) = 0x100010101000200i64;
  v17[1] = xmmword_4032C0;
  v15 = v4;
  v5 = 0;
  v17[2] = xmmword_4032E0;
  v14 = v3;
  v6 = 0;
  v17[3] = xmmword_403300;
  v17[4] = xmmword_4032F0;
  v17[5] = xmmword_4032B0;
  v18 = 16843008;
  v7 = sub_401410();
  std::ostream::operator<<(v7, sub_401660, v14, v15);
  sub_401410();
  v8 = (_BYTE *)sub_401AB3(300);
  sub_401690();
  while ( 1 )
  {
    if ( !v5 && v6 == 9 )
      goto LABEL_20;
    if ( !*v8 )
      break;
    switch ( *v8 )
    {
      case 'A':
        v9 = v5;
        v10 = v6 - 1;
        goto LABEL_11;
      case 'D':
        v9 = v5;
        v10 = v6 + 1;
        goto LABEL_11;
      case 'S':
        v9 = v5 + 1;
        goto LABEL_10;
      case 'W':
        v9 = v5 - 1;
LABEL_10:
        v10 = v6;
LABEL_11:
        if ( v9 > 9 || v10 > 9 || (v11 = *((_BYTE *)v17 + 10 * v9 + v10 + 12), v11 == 1) || v11 == 3 )
        {
          sub_401020("Wrong Flag.", v16);
          return 0;
        }
        ++v8;
        v12 = v6 + 10 * v5;
        v5 = v9;
        *((_BYTE *)v17 + v12 + 12) = 3;
        v6 = v10;
        break;
      default:
        sub_401020("Unknown Key.", v16);
        return 0;
    }
  }
  if ( !v5 && v6 == 9 )
LABEL_20:
    sub_401410();
  return 0;
}

然后开始分析,通过 得出这个是一个(10*10)的图,并且v17存储着地图,且不能走回头路
然后有一个类似BFS的跑图过程

得到v5,v6/v9,10应该对应的是位置下标x,y,WSAD控制方向,终点是(0,9),起点是(0,0)。
修改变量名之后,接下来只要还原出地图就可以得到flag。
进入变量以后,还发现这样一句 因此还需要用md5的32位加密。
重点在这里

结合之前的代码,发现map赋值顺序是D0,C0,E0,00,F0,B0(只有D0未显示,猜测是第一个,且出现了2应该是在第一行)

接下来进入16进制窗口:

猜测就是这些16进制数组成了01迷宫,只需要按照赋值的顺序手动得到一个10*10的地图即可。

B0 01 00 01 01 01 01 00 00  01 01 01 00 00 00 00 00
C0 01 01 01 01 00 00 01 01  00 00 00 00 01 01 01 00
D0 00 01 01 01 00 00 00 00  00 02 00 01 01 01 00 01
E0 01 01 01 01 01 00 00 01  01 00 01 00 00 00 01 01
F0 01 00 01 00 01 01 01 00  00 00 00 00 01 00 01 01
00 00 01 01 00 00 00 01 00  01 00 00 01 01 01 01 01
   00 00 00 00
   D0 C0  E0 00 F0 B0//打表顺序
D0 00 01 01 01 00 00 00 00  00 02 00 01 01 01 00 01
C0 01 01 01 01 00 00 01 01  00 00 00 00 01 01 01 00
E0 01 01 01 01 01 00 00 01  01 00 01 00 00 00 01 01
00 00 01 01 00 00 00 01 00  01 00 00 01 01 01 01 01
F0 01 00 01 00 01 01 01 00  00 00 00 00 01 00 01 01
B0 01 00 01 01 01 01 00 00  01 01 01 00 00 00 00 00
//根据上表得到01迷宫
0111000002
0111011111
0011000011
1011111001
1010001101
1000101001
1111101011
1000001011
1011110011
1000000000//这里有个疑问,目前我能获取的地图信息只能到这里,后面全部补0,应该和第一行输入时长度较短有关系,得到的迷宫有两种方法都可以解出来
SSDSSSDDWDDSSSAAAASSDDDDDDWWWWDWWAWAAAWWDDDDD 063a45cc01b39073388033260064b679//第一种方法
SSDSSSDDWDDSSSAAAASSDDDDDWDWWWDWWAWAAAWWDDDDD f62f3965313148ebccaec0bcdb132957//第二种方法

tic-tac-toe

先打开.exe文件,赢了一局之后弹出便秘窗口,本来以为像Upx-Rand那题一样把sleep函数给nop了就行了,结果弹出窗口需要赢10000局,那没事了,扔进Exeinfo PE,发现是.NET文件,搜了一下C#需要用dnspy反编译,当场下载一个dnspy,然后在一堆令人头皮发麻的目录下找到了它:

本来还想着直接硬刚代码,发现不大可能,想到之前的窗口,需要赢10000局,于是直接ctrl+F搜索10000关键字,恰好只有一个,flag肯定就在附近:

进入APGC745VI2函数
先手动base64解码两个字符串,发现都不是flag,于是考虑查看EFRH8ME1VK函数,发现调用了三个参数,前两个已经显示,于是进入
发现是个数组且也经过了加密,进入EFRH8ME1VK函数:

public static string EFRH8ME1VK(string Data, string Key, string Vector)
{
	byte[] buffer = Convert.FromBase64String(Data);
	byte[] array = new byte[32];
	Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(array.Length)), array, array.Length);
	byte[] array2 = new byte[16];
	Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(array2.Length)), array2, array2.Length);
	byte[] bytes = null;
	Rijndael rijndael = Rijndael.Create();
	try
	{
		using (MemoryStream memoryStream = new MemoryStream(buffer))
		{
			using (CryptoStream cryptoStream = new CryptoStream(memoryStream, rijndael.CreateDecryptor(array, array2), 0))
			{
				using (MemoryStream memoryStream2 = new MemoryStream())
				{
					byte[] array3 = new byte[1024];
				        int count;
					while ((count = cryptoStream.Read(array3, 0, array3.Length)) > 0)
					{
						memoryStream2.Write(array3, 0, count);
					}
					bytes = memoryStream2.ToArray();
				}
			}
		}
	}
	catch
        {
		bytes = null;
	}
	return Encoding.UTF8.GetString(bytes);
}

发现是经过一些列操作之后返回了bytes字符串,那就好办了,直接把之前发现的代码整合起来,手动添加Main函数输出该返回值即可。
本来还打算配置C#环境,后来某佬说可以用在线IDE,于是直接跳过配置环境这种搞人心态的步骤,根据报错删去部分代码,并把函数前面的private改成public static封装就可以了。

using System;
using System.ComponentModel;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
namespace reApplication
{
	class re
	{
		private static byte[] _aesKetByte = new byte[]
		{
			18,
			52,
			86,
			120,
			144,
			171,
			205,
			239,
			18,
			52,
			86,
			120,
			144,
			171,
			205,
			239
		};
		private static string _aesKeyStr = Encoding.UTF8.GetString(re._aesKetByte);
		public static string EFRH8ME1VK(string Data, string Key, string Vector)
		{
			byte[] buffer = Convert.FromBase64String(Data);
			byte[] array = new byte[32];
			Array.Copy(Encoding.UTF8.GetBytes(Key.PadRight(array.Length)), array, array.Length);
			byte[] array2 = new byte[16];
			Array.Copy(Encoding.UTF8.GetBytes(Vector.PadRight(array2.Length)), array2, array2.Length);
			byte[] bytes = null;
			Rijndael rijndael = Rijndael.Create();
			try
			{
				using (MemoryStream memoryStream = new MemoryStream(buffer))
				{
					using (CryptoStream cryptoStream = new CryptoStream(memoryStream, rijndael.CreateDecryptor(array, array2), 0))
					{
						using (MemoryStream memoryStream2 = new MemoryStream())
						{
							byte[] array3 = new byte[1024];
							int count;
							while ((count = cryptoStream.Read(array3, 0, array3.Length)) > 0)
							{
								memoryStream2.Write(array3, 0, count);
							}
							bytes = memoryStream2.ToArray();
						}
					}
				}
			}
			catch
			{
				bytes = null;
			}
			return Encoding.UTF8.GetString(bytes);
		}
		public static string APGC745VI2()
		{
			string @string = Encoding.UTF8.GetString(Convert.FromBase64String("YjhvTGNZM1BnTkp6WnBiTGlvMFRlYm82bnUyTzBkVGVwTzBWNDVmTUg5c1dDeFRkUW5VVjBpY1Jabk5tbVRYUg=="));
			return re.EFRH8ME1VK(@string, Encoding.UTF8.GetString(Convert.FromBase64String("VFNDVEYtSnt3ZWxjb21lfQ==")), re._aesKeyStr);
		}
		static void Main(string[] args)
		{
			Console.WriteLine(APGC745VI2());
		}
	}
}

运行后得到flag:

minecraft


打开.exe文件,发现是一个minecraft场景,玩了一会之后,我觉得很好玩于是跑去下了个minecraft,然后尝试了几次跑图发现不可做,直接扔进Exeinfo PE,发现是PyInstaller v.3.6,直接百度,发现应该用pyinstxtractor.py反编译。
于是照着这份教程这份教程将反编译出来的struct.pyc和main.pyc用16进制打开,并用struct的前八字节覆盖mian的前八字字节,然后在线反编译得到源码,发现关键代码:

def judge_flag(label_text):
    flag = input('xe8xafxb7xe8xbex93xe5x85xa5flagxe8xbfx9bxe8xa1x8cxe9xaax8cxe8xafx81xefxbcx9a')
    enflag = [
        81,
        77,
        13,
        81,
        25,
        180,
        41,
        237,
        21,
        233,
        125,
        65,
        229,
        209,
        161,
        192,
        161,
        125,
        25,
        85,
        57,
        185,
        229,
        125,
        181,
        141,
        125,
        196,
        205,
        185,
        209,
        125,
        165,
        81,
        245]
    
    def encrypt(data):
        return (data & 192) >> 6 | (data & 48) << 2 | (data & 12) << 2 | (data & 3) << 2

    if None == (lambda .0 = None: [ encrypt(ord(s)) for s in .0 ])(flag):
        label_text += ' flagxe6x98xaf' + flag
    else:
        label_text += ' xe8xbex93xe5x85xa5flagxe9x94x99xe8xafxaf'
    return label_text

虽然看不懂那个奇怪的lambda表达式,但是注意到enflag数组和encrypt函数,根据逆向的性质,encrypt(flag)=enflag,decrypt(enflag)=flag,发现该encrypt函数不好写逆向代码,于是直接暴力(我不会告诉你第一题easy-xor我上来直接写了dfs爆搜了每一位跑了一小时没跑出来)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int enflag[]={
        81,
        77,
        13,
        81,
        25,
        180,
        41,
        237,
        21,
        233,
        125,
        65,
        229,
        209,
        161,
        192,
        161,
        125,
        25,
        85,
        57,
        185,
        229,
        125,
        181,
        141,
        125,
        196,
        205,
        185,
        209,
        125,
        165,
        81,
        245
        };

int encrypt(int data)
{
    return (data & 192) >> 6 | (data & 48) << 2 | (data & 12) << 2 | (data & 3) << 2;
}
int main()
{
    for(int i=0;i<35;i++)
    {
        for(int j=32;j<=127;j++)
        {
            if(encrypt(j)==enflag[i])printf("%c",j);
        }
    }

}

全靠瞎**猜得到flag ,官方wp中反编译出来的py源码就很好理解,应该是在线编译器或者反编译操作不对出锅?

原文地址:https://www.cnblogs.com/THRANDUil/p/15496302.html