Back

高校战役 re writeups

呆呆师傅安利的比赛

  • cycle graph
  • 天津垓
  • fxck
  • easyparser

cycle graph

逆向代码

根据提示,这是一道纯算法题,拖到 ida 中,根据关键字符串定位到关键函数。查看核心代码

  dword_403370 = 0;
  v1 = &unk_403384;
  byte_403374 = 48;
  v2 = 0;
  dword_403378 = (int)&unk_403380;
  do
  {
    v3 = dword_402178[v2];
    ++v2;
    *(v1 - 1) = v3;
    *v1 = (char *)&unk_403380 + 12 * dword_402274[v2];
    v1[1] = (char *)&unk_403380 + 12 * dword_4021F4[v2];
    v1 += 3;
  }
  while ( (signed int)v1 < (signed int)&unk_403504 );

上面的代码是主函数开头的初始化部分

  sub_401020("You need a flag to get out of this:\n", a1);
  scan("%s", &input);
  v4 = dword_403370;                            // 0
  v5 = byte_403374;                             // 48
  v6 = 5;
  v7 = dword_403378;
  do
  {
    v11 = *(&input + v6);
    if ( *(_DWORD *)v7 + v5 == v11 )
    {
      v7 = *(_DWORD *)(v7 + 4);
    }
    else
    {
      if ( v5 - *(_DWORD *)v7 != v11 )
      {
        sub_401020("This is not flag~\n", v10);
        system("pause");
        exit(1);
      }
      v7 = *(_DWORD *)(v7 + 8);
    }
    v5 = *(&input + v6);
    ++v4;
    ++v6;
    byte_403374 = v5;
    dword_403378 = v7;
    dword_403370 = v4;
  }
  while ( v6 < 21 );

上面的代码是根据输入进行的一系列操作

  if ( input != 'f' || v13 != 'l' || v14 != 'a' || v15 != 'g' || v16 != '{' || v17 != '}' )
  {
    v8 = "illegal input~\n";
  }
  else if ( v4 > 16 || (_UNKNOWN *)v7 != &unk_4034F4 )
  {
    v8 = "This is not flag~\n";
  }
  else
  {
    v8 = "Congratulations!!\n";
  }
  sub_401020(v8, v11);
  system("pause");
  return 0;

上面的代码就是最后的验证环节

一开始尝试直接看代码,发现自己被绕进去了,完全不知道各个地址是什么东西,所以考虑通过动态调试来得到初始化后的结果

动态调试

直接拖到 x32dbg 中,在第一个输出的字符串前面打上断点,然后运行到断点的位置

分析了一下初始化部分,认为关键的内存部分应该是从 403370 开始一直到 403540 这部分

于是找到 3370 ,右键选择内存窗口找到地址

鉴于数据量不大,直接手dump下来就行了

00C43370  00 00 00 00 30 00 00 00 80 33 C4 00 00 00 00 00  ....0....3Ä.....  
00C43380  34 00 00 00 98 33 C4 00 8C 33 C4 00 02 00 00 00  4....3Ä..3Ä.....  
00C43390  98 33 C4 00 E0 33 C4 00 2C 00 00 00 8C 33 C4 00  .3Ä.à3Ä.,....3Ä.  
00C433A0  D4 33 C4 00 2A 00 00 00 58 34 C4 00 94 34 C4 00  Ô3Ä.*...X4Ä..4Ä.  
00C433B0  06 00 00 00 D4 33 C4 00 EC 33 C4 00 2A 00 00 00  ....Ô3Ä.ì3Ä.*...  
00C433C0  98 33 C4 00 64 34 C4 00 2F 00 00 00 B8 34 C4 00  .3Ä.d4Ä./...¸4Ä.  
00C433D0  F4 34 C4 00 2A 00 00 00 1C 34 C4 00 94 34 C4 00  ô4Ä.*....4Ä..4Ä.  
00C433E0  33 00 00 00 B0 33 C4 00 EC 33 C4 00 03 00 00 00  3...°3Ä.ì3Ä.....  
00C433F0  F8 33 C4 00 1C 34 C4 00 02 00 00 00 B0 33 C4 00  ø3Ä..4Ä.....°3Ä.  
00C43400  10 34 C4 00 32 00 00 00 7C 34 C4 00 DC 34 C4 00  .4Ä.2...|4Ä.Ü4Ä.  
00C43410  32 00 00 00 28 34 C4 00 F8 33 C4 00 32 00 00 00  2...(4Ä.ø3Ä.2...  
00C43420  8C 33 C4 00 A0 34 C4 00 30 00 00 00 80 33 C4 00  .3Ä. 4Ä.0....3Ä.  
00C43430  EC 33 C4 00 03 00 00 00 28 34 C4 00 A0 34 C4 00  ì3Ä.....(4Ä. 4Ä.  
00C43440  01 00 00 00 BC 33 C4 00 AC 34 C4 00 32 00 00 00  ....¼3Ä.¬4Ä.2...  
00C43450  D4 33 C4 00 EC 33 C4 00 2B 00 00 00 D0 34 C4 00  Ô3Ä.ì3Ä.+...Ð4Ä.  
00C43460  B8 34 C4 00 02 00 00 00 10 34 C4 00 A4 33 C4 00  ¸4Ä......4Ä.¤3Ä.  
00C43470  2E 00 00 00 D0 34 C4 00 88 34 C4 00 01 00 00 00  ....Ð4Ä..4Ä.....  
00C43480  34 34 C4 00 C8 33 C4 00 02 00 00 00 34 34 C4 00  44Ä.È3Ä.....44Ä.  
00C43490  4C 34 C4 00 2D 00 00 00 98 33 C4 00 1C 34 C4 00  L4Ä.-....3Ä..4Ä.  
00C434A0  32 00 00 00 40 34 C4 00 D4 33 C4 00 04 00 00 00  2...@4Ä.Ô3Ä.....  
00C434B0  94 34 C4 00 34 34 C4 00 2D 00 00 00 E8 34 C4 00  .4Ä.44Ä.-...è4Ä.  
00C434C0  70 34 C4 00 30 00 00 00 94 34 C4 00 8C 33 C4 00  p4Ä.0....4Ä..3Ä.  
00C434D0  31 00 00 00 64 34 C4 00 40 34 C4 00 2F 00 00 00  1...d4Ä.@4Ä./...  
00C434E0  EC 33 C4 00 B0 33 C4 00 33 00 00 00 88 34 C4 00  ì3Ä.°3Ä.3....4Ä.  
00C434F0  04 34 C4 00 05 00 00 00 F4 34 C4 00 F4 34 C4 00  .4Ä.....ô4Ä.ô4Ä.  
00C43500  02 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00  ................  

然后对照着第二部分的代码一点一点看,发现其实就是如果 *(_DWORD *)v7 + v5 == v11 就让 v7 跳到下一个内存地址所指向的地址(这题指针有点绕),比如一开始如果满足输入为 0x34+0x30 接下来就先走到 C43384 ,然后从这里跳到 C43398 这个地址,继续下一步运算,如果 v5 - *(_DWORD *)v7 != v11 就走到下两个内存地址所指向的地址。

脚本求解

原理就是根据地址跳一次,那么接下来就是求解了,由于每一步都有两种可能(实际上并不是,要在可见字符有范围的)

一开始觉得可能只有一种路径,直接用循环求解了,结果不行,就写了个dfs

addr = [
    0x000034,        6,        3, 0x000002,
           6, 0xC433E0, 0x00002C,        3,
    0xC433D4, 0x00002A, 0xC43458, 0xC43494,
    0x000006, 0xC433D4, 0xC433EC, 0x00002A,
           6, 0xC43464, 0x00002F, 0xC434B8,
    0xC434F4, 0x00002A, 0xC4341C, 0xC43494,
    0x000033,       12, 0xC433EC, 0x000003,
    0xC433F8, 0xC4341C, 0x000002,       12,
    0xC43410, 0x000032, 0xC4347C, 0xC434DC,
    0x000032, 0xC43428, 0xC433F8, 0x000032,
           3, 0xC434A0, 0x000030,        0,
    0xC433EC, 0x000003, 0xC43428, 0xC434A0,
    0x000001,       15, 0xC434AC, 0x000032,
    0xC433D4, 0xC433EC, 0x00002B, 0xC434D0,
    0xC434B8, 0x000002, 0xC43410,        9,
    0x00002E, 0xC434D0, 0xC43488, 0x000001,
    0xC43434, 0xC433C8, 0x000002, 0xC43434,
    0xC4344C, 0x00002D,        6, 0xC4341C,
    0x000032, 0xC43440, 0xC433D4, 0x000004,
    0xC43494, 0xC43434, 0x00002D, 0xC434E8,
    0xC43470, 0x000030, 0xC43494,        3,
    0x000031, 0xC43464, 0xC43440, 0x00002F,
    0xC433EC,       12, 0x000033, 0xC43488,
    0xC43404, 0x000005, 0xC434F4, 0xC434F4,
]
for i in range(len(addr)):
    if addr[i] >= 0xc43380:
        addr[i] -= 0xc43380
        addr[i] //= 4



def dfs(start, add, count):
    if (add > 0x7e) or add < 0x21:
        return False
    if (count >= 21) and (start == 93):
        print (chr(add), end = '')
        return True
    elif (count >= 21) and start != 93:
        return False
    if dfs(addr[start + 1], add + addr[start], count + 1):
        print (chr(add), end = '')
        return True
    if dfs(addr[start + 2], add - addr[start], count + 1):
        print (chr(add), end = '')
        return True
    return False

dfs(0, 0x30, 5)
# 72ab0c6a79cb0b8d0

这个是倒序输出,且有个 0 是最初的 0x30

最后反转过来就行了

(因为 adworld 不会提交,也不知道对不对,反正运行结果是 Congratulations!! 了)

天津垓

反反调试

搜索 ida 字符串可以找到反调试的地方,keypatch 修改跳转或者修改函数返回值即可

破解 Authorize

运行 exe 文件,看到 Authorize: 字符串,到 ida 中通过 Shift+F12 找到字符串的位置,通过交叉引用找到关键函数。

查看函数伪代码:

int sub_1004011F6()
{
  v39 = 'H_gnisiR';
  v40 = 'eppo';
  v41 = '!r';
  v42 = 0;
  v31 = 'eht nehW';
  v32 = 'oh evif ';
  v33 = 'sorc snr';
  v34 = 'g eht ,s';
  v35 = 'os nedlo';
  v36 = 'HT reidl';
  v37 = 'si RESUO';
  v38 = '\n.nrob ';
  v25 = 't pmuj A';
  v26 = 'ks eht o';
  v27 = ' snrut y';
  v28 = 'dir a ot';
  v29 = '.kcik re';
  v30 = '\n';
  v21 = 'etneserP';
  v22 = 'IAZ yb d';
  v23 = '\nA';
  v24 = 0;
  strcpy(v20, "%s");
  strcpy(Format, "%20s");
  v1 = 17;
  v2 = 8;
  v3 = 6;
  v4 = 10;
  v5 = 15;
  v6 = 20;
  v7 = 42;
  v8 = 59;
  v9 = 47;
  v10 = 3;
  v11 = 47;
  v12 = 4;
  v13 = 16;
  v14 = 72;
  v15 = 62;
  v16 = 0;
  v17 = 7;
  v18 = 16;
  scanf(Format, Str);
  if ( strlen(Str) != 18 )
  {
    printf(v20, &v25);
    exit(1);
  }
  for ( i = 0; i <= 17; ++i )
  {
    v43 = ~(Str[i] & *((_BYTE *)&v39 + i % 14)) & (Str[i] | *((_BYTE *)&v39 + i % 14));
    if ( v43 != *(&v1 + i) )
    {
      printf(v20, &v25);
      exit(1);
    }
  }
  printf(v20, &v31);
  return printf(v20, &v21);
}

找到最后的判断条件 v43 != *(&v1 + i),而 v43 是通过一个异或来生成的,因此只需要通过 v1 数组和 v39 数组反向求解得到输入的 Str 即可。

下面是求解代码

flag = [17, 8, 6, 10, 15, 20, 42, 59, 47,  3,  47,  4,  16,  72,  62,  0,  7,  16]
xor = 'H_gnisiR'[::-1] + 'eppo'[::-1] + '!r'[::-1]
for i in range(len(flag)):
    print (chr(flag[i] ^ ord(xor[i % 14])), end='')
print ()
# Caucasus@s_ability

patch SMC

将得到的字符串输入程序,发现需要通过第二个认证

When the five horns cross, the golden soldier THOUSER is born.
Presented by ZAIA

      $$$$$$$$$$$$$$  $$$$$$$$$     $$$$$$$    $$$$$$$$$$
      $$$$$$$$$$$$$$ $$$$$$$$$$$    $$$$$$$    $$$$$$$$$$$
     $$$$$$$$$$$$$$ $$$$$$$$$$$$$   $$$$$$$   $$$$$$$$$$$$$
            $$$$$$  $$$$$$ $$$$$$   $$$$$$$  $$$$$$0 $$$$$$
          $$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$0
       $$$$$$$$   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
     $$$$$$$$$$$$$$$$$$$             $$$$$                   $$$
   $$$$$$$$$$$$$$$$$$$$       $$$$$$  $$$  $$$$$$       $$$$$$$$$
 $$$$$$$$$$$$$$$$$$$$$         $$$$$$ $$0 $$$$$$         $$$$$$$$

Input the flag to hijack the ability of Hiden Intelligence:

Strings window 中并没有找到 Input the flag 的字符串,怀疑程序使用了 SMC。

法一(patch 方法同 HGAME FAKE)

SMC 可以手动 patch 或者使用动态调试 dump,这里先选择手动 patch

在 ida 的汇编页面中浏览 Data 部分,发现在 0x10040164D 位置有一长串未被 ida 解析的二进制数据,推测这部分是被加密过的代码,按 x 交叉引用找到加密的代码

__int64 sub_100401A6C()
{
  sub_100401506(byte_10040164D, 1045i64, Str);
  sub_10040162B();
  return (*(__int64 (**)(void))byte_10040164D)();
}

发现先用一个函数调用了这段二进制数据,随后调用以这个地址为开头的函数。

进入 sub_100401506 函数中

BOOL __fastcall sub_100401506(void *a1, int a2, __int64 a3)
{
  BOOL result; // eax
  DWORD flOldProtect; // [rsp+28h] [rbp-8h]
  int i; // [rsp+2Ch] [rbp-4h]
  void *lpAddress; // [rsp+40h] [rbp+10h]
  int v7; // [rsp+48h] [rbp+18h]
  __int64 v8; // [rsp+50h] [rbp+20h]

  lpAddress = a1; // bytes
  v7 = a2;        // 1045
  v8 = a3;        // Str
  if ( strlen(Str) != 18 )
    exit(1);
  if ( !VirtualProtect(lpAddress, v7, 0x40u, &flOldProtect) )
    exit(1);
  for ( i = 0; i < v7; ++i )
    *((_BYTE *)lpAddress + i) ^= *(_BYTE *)(i % 18 + v8); // 关键代码
  result = VirtualProtect(lpAddress, v7, flOldProtect, &flOldProtect);
  if ( !result )
    exit(1);
  return result;
}

把关键代码美化一下:

for ( i = 0; i < v7; ++i )
    lpAddress[i] ^= v8[i % 18]; // 关键代码

发现是通过一个异或做的加密,密钥就是第一步中输入的字符串。

因此写一个python脚本来解密并生成patch文件

smc = [0x16,  0x29, 0x0F4,  0x8F,   0x91,  0x72,  0x75,  0x73,    8 , 0x0FE, 0x0F3,  0x45, 0x0E2,  0x69,  0x6C,  0x69, 0x0B3, 0x0FC, 0x0D3,  0x61,
 0x75,  0x63,  0x13,0x0D1,  0x6B,   0x73,  0x87, 0x0F6, 0x0CB,  0x61,  0x62,  0x69, 0x0A8,    6 ,  0x54,  0x79,  0x84, 0x0E4, 0x0ED,  0x63,  0x61,
# 中间部分省略
0x0FC, 0x0A2,  0x89,  0x2B,  0x74,  0x73,  0x40,  0x9B,  0x1C,  0x60,  0x62,  0x69,  0x84,  0x57,  0x75,  0x79,  0x43,  0x90,  0x48,  0x81, 0x0C4,
0x0F0,    1 ,    0 ,    0 ,  0x5D, 0x0C3]

file = open('patch.py', 'w')
str = 'Caucasus@s_ability'
title = '''file_path = "天津垓.exe"
fr = open(file_path, "rb")
fw = open('modify_'+file_path, "wb")
data = fr.read()
fw.write(data)
fw.seek(0xc4d)

'''
file.write(title)

for i in range(1045):
    smc[i] ^= (ord(str[i % 18]))

for i in range(0, 1045, 50):
    file.write('fw.write(')
    file.write(f'{bytes(smc[i:i + 50])}')
    file.write(')\n')

file.close()

在命令行中依次运行 python solve.pypython patch.py

会得到一个新的 exe 文件 modify_天津垓.exe

在 ida 中打开,进入之前解密的地址,发现此时已经可以被解析了

法二( idapython 自动 patch)

自己手动 patch 的好处在于可以避免直接对文件进行操作,如果出现错误不容易复原(毕竟 ida 7.0 没有撤销)。而利用 idapython 的接口进行 patch 则写起来更加方便。

def patch(start, end):
    str = 'Caucasus@s_ability'
    addr = start
    while(addr < end + 1):
        PatchByte(addr, Byte(addr) ^ ord(str[(addr - start) % 18]))
        addr += 1
        print (addr - start)

如果将 str 取出作为变量的一部分,这段代码就可以通用到一些其他题目了。

随后在 ida 的 File > script file 中选择写好的脚本,在 File > script command 中直接输入 patch( <start_addr>, <end_addr>) 即可(具体的 <addr> 可以直接从 ida 的汇编页面中读取)

点击 run 后即可 patch 成功。

hijack

查看上一步中成功解密的代码

int sub_10040164D()
{
  v9 = 2007666;
  v10 = 2125764;
  v11 = 1909251;
  v12 = 2027349;
  v13 = 2421009;
  v14 = 1653372;
  v15 = 2047032;
  v16 = 2184813;
  v17 = 2302911;
  v18 = 2263545;
  v19 = 1909251;
  v20 = 2165130;
  v21 = 1968300;
  v22 = 2243862;
  v23 = 2066715;
  v24 = 2322594;
  v25 = 1987983;
  v26 = 2243862;
  v27 = 1869885;
  v28 = 2066715;
  v29 = 2263545;
  v30 = 1869885;
  v31 = 964467;
  v32 = 944784;
  v33 = 944784;
  v34 = 944784;
  v35 = 728271;
  v36 = 1869885;
  v37 = 2263545;
  v38 = 2283228;
  v39 = 2243862;
  v40 = 2184813;
  v41 = 2165130;
  v42 = 2027349;
  v43 = 1987983;
  v44 = 2243862;
  v45 = 1869885;
  v46 = 2283228;
  v47 = 2047032;
  v48 = 1909251;
  v49 = 2165130;
  v50 = 1869885;
  v51 = 2401326;
  v52 = 1987983;
  v53 = 2243862;
  v54 = 2184813;
  v55 = 885735;
  v56 = 2184813;
  v57 = 2165130;
  v58 = 1987983;
  v59 = 2460375;
  strcpy(Format, "Input the flag to hijack the ability of Hiden Intelligence:");
  strcpy(v7, "Progrise Key confirmed. Ready to break.\n");
  strcpy(v6, "Jacking Break! Zaia Enterprise.");
  strcpy(v5, "%59s");
  v3 = 29477;
  v4 = 0;
  strcpy(v2, "Not verified!");
  v62 = 2147483659;
  printf(Format);
  scanf(v5, Str);
  printf(v7);
  if ( strlen(Str) != 51 )
  {
    printf(v2);
    exit(0);
  }
  v61 = 19683;
  for ( i = 0; i <= 0x32; ++i )
  {
    v60 = v61 * (unsigned int)(unsigned __int8)Str[i] % v62; // 关键代码
    if ( v60 != *(&v9 + i) ) // 验证
    {
      printf(v2);
      exit(0);
    }
  }
  printf(v6);
  getchar();
  return getchar();
}

将关键代码美化一下

if ( *(&v9 + i) != v61 * Str[i] % v62 )

只要确保式子两端一直相等就行

程序的输入部分还是存到了 Str 中,因此只需要通过已经定义好的数求出 Str 就可以

这里直接对 Str 的每一位进行枚举(也可以用 z3 solver 或者求模逆等方法求解)

#include <stdio.h>

int v[51];

int main(){
    v[ 0] = 2007666;
    v[ 1] = 2125764;
// 中间省略
    v[49] = 1987983;
    v[50] = 2460375;
    int v61 = 19683;
    int res = 0;
    for(int i = 0; i <= 0x32; ++i){
        for(res = 0; res <= 0x80; res++){
            if (v[i] == v61 * res % 2147483659){
                printf("%c", res);
                break;
            }
        }
    }
    printf("\n");
    return 0;
}
// flag{Thousandriver_is_1000%_stronger_than_zero-one}

在程序中输入这个字符串后,就能看到如下输出

Input the flag to hijack the ability of Hiden Intelligence:flag{Thousandriver_is_1000%_stronger_than_zero-one}
Progrise Key confirmed. Ready to break.
Jacking Break! Zaia Enterprise.

说明我们破解成功了!

fxck!

在输出前经过了两个函数,猜测第一个是加密,第二个是验证

看一下第一个函数,里面大概有两段代码是有用的

第一段

  for ( i = 0; i < v10; ++i )
  {
    v14 = input_v11[i];
    for ( j = 0; j < v12; ++j )
    {
      v14 += (unsigned __int8)v20[j] << 8;
      v20[j] = v14 % 58;
      v14 /= 58u;
    }
    while ( v14 )
    {
      v4 = v12++;
      v20[v4] = v14 % 58;
      v14 /= 58u;
    }
  }

第二段

  for ( k = 0; k <= 57; ++k )
    byte_602500[k] ^= byte_602490[k % 7] ^ (unsigned __int8)k;
  for ( l = 0; l < v12; ++l )
    des_v9[v16 + l] = byte_602500[(unsigned __int8)v20[v12 - 1 - l]];// 转成base58
  des_v9[v12 + v16] = 0;

一看到一堆58的取模和除法,猜测是base58编码

然后动调拿一下table

接下来看第二个函数,发现输入竟然最后比较的时候才出现,整个函数都是用来生成正确密文的,于是直接动调拿密文

写个脚本求解就行了

key = [0x14, 0x98, 0xE6, 0x55, 0xC8, 0xBF, 0xAA]

init = [
    0x55, 0xDB, 0xA7, 0x12, 0x89, 0xFC, 0xEB, 0x5B, 0xDA, 0xA4, 
    0x13, 0x8E, 0xFD, 0xF7, 0x4B, 0xC5, 0xA5, 0x10, 0x8F, 0xFA, 
    0xE9, 0x59, 0xD7, 0xAB, 0x7C, 0xE3, 0x96, 0x85, 0x3D, 0xB3, 
    0xCF, 0x72, 0xD1, 0xFF, 0xEA, 0x54, 0xD8, 0xA6, 0x15, 0x88, 
    0xFF, 0xEA, 0x54, 0xD8, 0xA7, 0x16, 0x89, 0xE0, 0xEB, 0x57, 
    0xD9, 0xA1, 0x14, 0x8B, 0xFE, 0xE5, 0x55, 0xDB
]

true_table = ''
for i in range(len(init)):
    init[i] ^= key[i % 7] ^ i
    true_table += '%c' % init[i]

from base58 import *
flag = '4VyhuTqRfYFnQ85Bcw5XcDr3ScNBjf5CzwUdWKVM7SSVqBrkvYGt7SSUJe'
fake_table = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
true_flag = ''
for i in range(len(flag)):
    true_flag += fake_table[true_table.index(flag[i])]

print (b58decode(true_flag))
# b'\x06flag{63510cf7-2b80-45e1-a186-21234897e5cd}'

得到flag

拿到flag之后用程序验证,结果发现没有通过,以为自己只在第一层,base58分析错了,于是从头认真读代码 最后上网看了wp,才发现flag就是过不了验证

easyparser

见过的最大的VM了,边动调边读指令

先是一堆初始化,直接动调过去,找输入

发现用了个循环,先将输入放到一个地址里

判断最后一个是否为 }

然后,将输入的一部分取出,存到了某个地址+0xE1到+0x100,正好32位

然后判断一下头是不是 flag{

接下来又进入一个循环,发现将某个地址的数字取出,存到了7号寄存器中,然后将对应位的输入取出,存到6号寄存器中

对6号寄存器分别进行了 ^0x63<<2 两个指令

然后和7号寄存器进行比较

猜测对所有明文都进行了同样的加密,所以直接从地址中取出密文,解密

cipher = [144,332,28,240,132,60,24,64,64,240,208,88,44,8,52,240,276,240,128,44,40,52,8,240,144,68,48,80,92,44,264,240]

for c in cipher:
    print (chr((c >> 2) ^ 0x63), end = '')
print ()
# G0d_Bless_Wuhan_&_China_Growth!_
Built with Hugo
Theme Stack designed by Jimmy