分类 Reverse 下的文章

关于WP

Author: 0xcafebabe
第三方题目(非XDSEC成员):
1.xor(大嘘) (Author: 中国人民警察大学的:Chovy)
2.Cython-Strike: Bomb Defusion (Author: 西安电子科技大学的:不玩逆向爱玩CS的同学)

入门指北

下载PDF,了解里面关于XOR的讲解内容后,可以发现直接给出了解题代码。

#include <iostream>
int main()
{
    char password_enc[] = { 
        123, 121, 115, 117, 98, 112, 109, 100, 37, 96, 37, 100, 101, 37, 73, 39,
        101, 73, 119, 73, 122, 121, 120, 113, 73, 122, 121, 120, 113, 73, 97, 119, 
        111, 73, 98, 121, 73, 115, 110, 102, 122, 121, 100, 115, 107, 22 };
    // 因为a^b=c时, b^c=a, 所以我们可以这样还原数据:
    char password[47];
    for (int i = 0; i < 46; i++) {
        password[i] = password_enc[i] ^ 22;
    }
    password[46] = 0; // 使用0字符来截断掉%s的无尽输出..
        printf("%s\n", password); // 哈哈,这就是本题的f l a g,自己运行一下交上去吧!
    return 0;
}

可以拿到flag
moectf{r3v3rs3_1s_a_long_long_way_to_explore}

Xor

拿到附件后发现是Windows下的可执行文件(.exe),我们载入IDA后可以观察到反编译的代码

int __fastcall main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // rax
  __int64 i; // rax
  char Buffer[16]; // [rsp+20h] [rbp-48h] BYREF
  __int128 v7; // [rsp+30h] [rbp-38h]
  __int64 v8; // [rsp+40h] [rbp-28h]
  int v9; // [rsp+48h] [rbp-20h]
  char v10; // [rsp+4Ch] [rbp-1Ch]

  sub_140001010("Input Your Flag moectf{xxx} (len = 45) \n");
  v8 = 0LL;
  *(_OWORD *)Buffer = 0LL;
  v9 = 0;
  v7 = 0LL;
  v10 = 0;
  v3 = _acrt_iob_func(0);
  fgets(Buffer, 45, v3);
  for ( i = 0LL; i < 44; ++i )
  {
    if ( ((unsigned __int8)Buffer[i] ^ 0x24) != byte_1400022B8[i] )
    {
      sub_140001010("FLAG is wrong!\n");
      system("pause");
      exit(0);
    }
  }
  sub_140001010("FLAG is RIGHT!\n");
  system("pause");
  return 0;
}

我们注意到 char Buffer[16]; // [rsp+20h] [rbp-48h] BYREF这个变量的大小是16,但是下面 fgets(Buffer, 45, v3);的大小是45,可以推测这个变量的大小反编译错误了。我们按y改成45,再刷新看代码。

int __fastcall main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // rax
  __int64 i; // rax
  char Buffer[48]; // [rsp+20h] [rbp-48h] BYREF

  sub_140001010("Input Your Flag moectf{xxx} (len = 45) \n");
  memset(Buffer, 0, 45);
  v3 = _acrt_iob_func(0);
  fgets(Buffer, 45, v3);
  for ( i = 0LL; i < 44; ++i )
  {
    if ( ((unsigned __int8)Buffer[i] ^ 0x24) != byte_1400022B8[i] )
    {
      sub_140001010("FLAG is wrong!\n");
      system("pause");
      exit(0);
    }
  }
  sub_140001010("FLAG is RIGHT!\n");
  system("pause");
  return 0;
}

可以发现,下面那些由于反编译错误的变量消失了,这时我们可以看到程序主逻辑是异或0x24并且逐个与byte_1400022B8进行比较,我们双击byte_1400022B8。

.rdata:00000001400022B8 ; _BYTE byte_1400022B8[56]
.rdata:00000001400022B8 byte_1400022B8  db 49h, 4Bh, 41h, 47h, 50h, 42h, 5Fh, 41h, 1Ch, 16h, 46h
.rdata:00000001400022B8                                         ; DATA XREF: main+58↑o
.rdata:00000001400022C3                 db 10h, 13h, 1Ch, 40h, 9, 42h, 16h, 46h, 1Ch, 9, 2 dup(10h)
.rdata:00000001400022CF                 db 42h, 1Dh, 9, 46h, 15h, 2 dup(14h), 9, 17h, 16h, 14h
.rdata:00000001400022DA                 db 41h, 2 dup(40h), 16h, 14h, 47h, 12h, 40h, 14h, 59h
.rdata:00000001400022E4                 db 0Ch dup(0)

发现这种形式,不方便抄出来,所以我们按U,他就会变成Undefined,再右键选中所有的,Convert拿出来成Python的List。
所以我们很容易写出EXP(解题脚本)。

enc = [0x49, 0x4B, 0x41, 0x47, 0x50, 0x42, 0x5F, 0x41, 0x1C, 0x16, 0x46, 0x10, 0x13, 0x1C, 0x40, 0x09, 0x42, 0x16, 0x46, 0x1C, 0x09, 0x10, 0x10, 0x42, 0x1D, 0x09, 0x46, 0x15, 0x14, 0x14, 0x09, 0x17, 0x16, 0x14, 0x41, 0x40, 0x40, 0x16, 0x14, 0x47, 0x12, 0x40, 0x14, 0x59]
for i in enc:
    print(chr(i ^ 0x24), end='')
    # moectf{e82b478d-f2b8-44f9-b100-320edd20c6d0}

所以Flag:moectf{e82b478d-f2b8-44f9-b100-320edd20c6d0}

upx

github下载UPX,然后用命令行启动,upx -d,就可以解压程序,再拖入IDA,即可查看正确的flag。
moectf{ec5390dd-f8cf-4b02-bc29-3bb0c5604c29}

dynamic

程序要求我们进行动态调试,我们先载入IDA进行分析。

int __fastcall main_0(int argc, const char **argv, const char **envp)
{
  char *v3; // rdi
  __int64 i; // rcx
  char v6; // [rsp+20h] [rbp+0h] BYREF
  _BYTE v7[80]; // [rsp+28h] [rbp+8h] BYREF
  _DWORD v8[56]; // [rsp+78h] [rbp+58h] BYREF

  v3 = &v6;
  for ( i = 34LL; i; --i )
  {
    *(_DWORD *)v3 = -858993460;
    v3 += 4;
  }
  j___CheckForDebuggerJustMyCode(&unk_140022067, argv, envp);
  v7[0] = -94;
  v7[1] = 5;
  v7[2] = 105;
  v7[3] = -117;
  v7[4] = -38;
  v7[5] = 23;
  v7[6] = 5;
  v7[7] = -31;
  v7[8] = -36;
  v7[9] = -52;
  v7[10] = -52;
  v7[11] = -63;
  v7[12] = 100;
  v7[13] = 116;
  v7[14] = -6;
  v7[15] = 80;
  v7[16] = -43;
  v7[17] = -95;
  v7[18] = -102;
  v7[19] = -84;
  v7[20] = -36;
  v7[21] = -34;
  v7[22] = 100;
  v7[23] = -65;
  v7[24] = -108;
  v7[25] = 45;
  v7[26] = 35;
  v7[27] = -13;
  v7[28] = 1;
  v7[29] = -43;
  v7[30] = 98;
  v7[31] = -56;
  v7[32] = -22;
  v7[33] = -83;
  v7[34] = -46;
  v7[35] = -42;
  v7[36] = 42;
  v7[37] = 80;
  v7[38] = 94;
  v7[39] = 107;
  v7[40] = 115;
  v7[41] = 12;
  v7[42] = -3;
  v7[43] = -116;
  v7[44] = 61;
  v7[45] = 56;
  v7[46] = 61;
  v7[47] = -47;
  v8[0] = -889275714;
  v8[1] = -559038242;
  v8[2] = 866566;
  v8[3] = 1131796;
  sub_14001129E(v7, 4294967284LL, v8);
  sub_1400113D4("What happened to my Flag?\n");
  sub_14001129E(v7, 12LL, v8);
  sub_1400113D4("Your Flag has REencrypted.");
  return 0;
}

可以发现,What happened to my Flag?上下都有一个sub_14001129E的函数,我们对4294967284LL进行分析,可以知道它其实是-12(0xFFFFFFF4LL),再点进去,可以观察到如下代码

__int64 __fastcall sub_140011820(int *a1, __int64 a2, __int64 a3)
{
  __int64 result; // rax
  unsigned int v4; // [rsp+24h] [rbp+4h]
  unsigned int v5; // [rsp+24h] [rbp+4h]
  unsigned int v6; // [rsp+44h] [rbp+24h]
  unsigned int v7; // [rsp+44h] [rbp+24h]
  unsigned int v8; // [rsp+44h] [rbp+24h]
  unsigned int v9; // [rsp+64h] [rbp+44h]
  unsigned int v10; // [rsp+64h] [rbp+44h]
  unsigned int j; // [rsp+84h] [rbp+64h]
  int i; // [rsp+84h] [rbp+64h]
  int v13; // [rsp+A4h] [rbp+84h]
  int v14; // [rsp+A4h] [rbp+84h]
  int v15; // [rsp+C4h] [rbp+A4h]
  unsigned int v16; // [rsp+C4h] [rbp+A4h]
  int v17; // [rsp+194h] [rbp+174h]
  int v18; // [rsp+194h] [rbp+174h]
  int v19; // [rsp+194h] [rbp+174h]
  int v20; // [rsp+194h] [rbp+174h]
  int v22; // [rsp+1C8h] [rbp+1A8h]
  int v23; // [rsp+1C8h] [rbp+1A8h]

  v22 = a2;
  result = j___CheckForDebuggerJustMyCode(&unk_140022067, a2, a3);
  if ( v22 <= 1 )
  {
    if ( v22 < -1 )
    {
      v23 = -v22;
      v14 = 52 / v23 + 6;
      v10 = 1131796 * v14;
      v5 = *a1;
      do
      {
        v16 = (v10 >> 2) & 3;
        for ( i = v23 - 1; i; --i )
        {
          v7 = a1[i - 1];
          v19 = a1[i]
              - (((v7 ^ *(_DWORD *)(a3 + 4LL * (v16 ^ i & 3))) + (v5 ^ v10)) ^ (((16 * v7) ^ (v5 >> 3))
                                                                              + ((4 * v5) ^ (v7 >> 5))));
          a1[i] = v19;
          v5 = v19;
        }
        v8 = a1[v23 - 1];
        v20 = *a1
            - (((v8 ^ *(_DWORD *)(a3 + 4LL * v16)) + (v5 ^ v10)) ^ (((16 * v8) ^ (v5 >> 3)) + ((4 * v5) ^ (v8 >> 5))));
        *a1 = v20;
        v5 = v20;
        v10 -= 1131796;
        result = (unsigned int)--v14;
      }
      while ( v14 );
    }
  }
  else
  {
    v13 = 52 / v22 + 6;
    v9 = 0;
    v6 = a1[v22 - 1];
    do
    {
      v9 += 1131796;
      v15 = (v9 >> 2) & 3;
      for ( j = 0; j < v22 - 1; ++j )
      {
        v4 = a1[j + 1];
        v17 = (((v6 ^ *(_DWORD *)(a3 + 4LL * (v15 ^ j & 3))) + (v4 ^ v9)) ^ (((16 * v6) ^ (v4 >> 3))
                                                                           + ((4 * v4) ^ (v6 >> 5))))
            + a1[j];
        a1[j] = v17;
        v6 = v17;
      }
      v18 = (((v6 ^ *(_DWORD *)(a3 + 4LL * (v15 ^ j & 3))) + (*a1 ^ v9)) ^ (((16 * v6) ^ ((unsigned int)*a1 >> 3))
                                                                          + ((4 * *a1) ^ (v6 >> 5))))
          + a1[v22 - 1];
      a1[v22 - 1] = v18;
      v6 = v18;
      result = (unsigned int)--v13;
    }
    while ( v13 );
  }
  return result;
}

这其实是一个标准的XXTEA,当n>1的时候(也就是+12)的时候,是加密,-12就是解密。
所以这个程序其实在做一个事情:把内置的flag进行解密后又马上加密回去,所以我们只需要动态调试,找到解密后的flag即可。
最后我们附上这个题目的c语言源码

#include <iostream>
#include <stdint.h>
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
#define DELTA 0x114514
void ppp(uint32_t* v, int n, uint32_t const key[4])
{
        uint32_t y, z, sum;
        unsigned p, rounds, e;
        if (n > 1)            /* Coding Part */
        {
                rounds = 6 + 52 / n;
                sum = 0;
                z = v[n - 1];
                do
                {
                        sum += DELTA;
                        e = (sum >> 2) & 3;
                        for (p = 0; p < n - 1; p++)
                        {
                                y = v[p + 1];
                                z = v[p] += MX;
                        }
                        y = v[0];
                        z = v[n - 1] += MX;
                } while (--rounds);
        }
        else if (n < -1)      /* Decoding Part */
        {
                n = -n;
                rounds = 6 + 52 / n;
                sum = rounds * DELTA;
                y = v[0];
                do
                {
                        e = (sum >> 2) & 3;
                        for (p = n - 1; p > 0; p--)
                        {
                                z = v[p - 1];
                                y = v[p] -= MX;
                        }
                        z = v[n - 1];
                        y = v[0] -= MX;
                        sum -= DELTA;
                } while (--rounds);
        }
}
int main()
{
        char v[] = { 0xa2,0x05,0x69,0x8b,0xda,0x17,0x05,0xe1,0xdc,0xcc,0xcc,0xc1,0x64,0x74,0xfa,0x50,0xd5,0xa1,0x9a,0xac,0xdc,0xde,0x64,0xbf,0x94,0x2d,0x23,0xf3,0x01,0xd5,0x62,0xc8,0xea,0xad,0xd2,0xd6,0x2a,0x50,0x5e,0x6b,0x73,0x0c,0xfd,0x8c,0x3d,0x38,0x3d,0xd1 };
        uint32_t k[] = { 0xcafebabe , 0xdeadc0de, 0xd3906, 0x114514 };
        ppp((uint32_t*)v, -12, k);
        printf("What happened to my Flag?");
        ppp((uint32_t*)v, 12, k);
        printf("Your Flag has encrypted.");
        return 0;
}

moectf{18d4c944-947c-4808-9536-c7d34d6b3827}

Reverse 问卷: Bye, MoeCTF2024

填写问卷即可获得flag。
moectf{Thank_You_FOR_Playing_Reverse_Challenges}

upx-revenge

解法1:这个程序直接upx -d是显示无法进行解压的,原因是UPX解压的时候会检测程序段名称是否为UPX0UPX1这类的,但是我改成了vmp0,导致无法直接解压,我们使用010Editor打开后更改这个字符串也就可以解密了。
解法2:直接拖入X64dbg进行动态调试,调试起来后直接搜索全局字符串,就可以找到flag。
解法3:CheatEngine进行字符串搜索。
moectf{554ea35c-a1bb-4d8f-a323-bd697564bf27}

XTEA

我们拿到文件后拖入IDA,可以获得以下代码

int __fastcall main_0(int argc, const char **argv, const char **envp)
{
  char *v3; // rdi
  __int64 i; // rcx
  __int64 v5; // rax
  __int64 v6; // rax
  __int64 v7; // rax
  __int64 v8; // rax
  __int64 v10; // rax
  char v11; // [rsp+20h] [rbp+0h] BYREF
  unsigned int v12; // [rsp+24h] [rbp+4h]
  char Str[48]; // [rsp+48h] [rbp+28h] BYREF
  _DWORD v14[12]; // [rsp+78h] [rbp+58h] BYREF
  _BYTE Src[32]; // [rsp+A8h] [rbp+88h] BYREF
  _BYTE v16[28]; // [rsp+C8h] [rbp+A8h] BYREF
  int j; // [rsp+E4h] [rbp+C4h]

  v3 = &v11;
  for ( i = 58LL; i; --i )
  {
    *(_DWORD *)v3 = -858993460;
    v3 += 4;
  }
  j___CheckForDebuggerJustMyCode(&unk_140028066, argv, envp);
  v12 = 32;
  memset(Str, 0, 0xDuLL);
  v5 = sub_1400110AA(std::cout, "please input key:");
  std::ostream::operator<<(v5, sub_140011046);
  sub_14001153C(std::cin, Str);
  v14[0] = 2;
  v14[1] = 0;
  v14[2] = 2;
  v14[3] = 4;
  v6 = sub_1400110AA(std::cout, "let me check your key");
  std::ostream::operator<<(v6, sub_140011046);
  v7 = sub_1400110AA(std::cout, "emmm");
  std::ostream::operator<<(v7, sub_140011046);
  if ( j_strlen(Str) == 12 )
  {
    memset(v16, 0, 8uLL);
    j_memcpy(Src, Str, 8uLL);
    sub_14001119F(v12, Src, v14);
    j_memcpy(Str, Src, 8uLL);
    j_memcpy(v16, &Str[4], 8uLL);
    sub_14001119F(v12, v16, v14);
    j_memcpy(&Str[4], v16, 8uLL);
    for ( j = 0; j < 12; ++j )
    {
      if ( Str[j] != byte_140022000[j] )
        goto LABEL_5;
    }
    v10 = sub_1400110AA(std::cout, "Correct key! Your flag is moectf{your key}");
    std::ostream::operator<<(v10, sub_140011046);
    return 0;
  }
  else
  {
LABEL_5:
    v8 = sub_1400110AA(std::cout, "XD,wrong!");
    std::ostream::operator<<(v8, sub_140011046);
    return 0;
  }
}

可以观察到,sub_14001119F为加密函数,因为在memcpy之后进行的它,并且它的结果又覆盖回去了,最后与正确的结果进行对比。
以下是XTEA的加密

__int64 __fastcall sub_1400148C0(unsigned int a1, unsigned int *a2, __int64 a3)
{
  __int64 result; // rax
  unsigned int i; // [rsp+24h] [rbp+4h]
  unsigned int v5; // [rsp+44h] [rbp+24h]
  unsigned int v6; // [rsp+64h] [rbp+44h]
  unsigned int v7; // [rsp+84h] [rbp+64h]

  j___CheckForDebuggerJustMyCode(&unk_140028066, a2, a3);
  v5 = *a2;
  v6 = a2[1];
  v7 = 0;
  for ( i = 0; i < a1; ++i )
  {
    v5 += (*(_DWORD *)(a3 + 4LL * (v7 & 3)) + v7) ^ (v6 + ((v6 >> 5) ^ (16 * v6)));
    v7 -= 855655493;
    v6 += (*(_DWORD *)(a3 + 4LL * ((v7 >> 11) & 3)) + v7) ^ (v5 + ((v5 >> 5) ^ (16 * v5)));
  }
  *a2 = v5;
  result = 4LL;
  a2[1] = v6;
  return result;
}

我们可以观察到IDA存在一些反编译错误,比如a3是一个DWORD型的指针(在main函数中,这个数组被赋值为了2 0 2 4)。我们进行修改,并且我们发现第一个参数是rounds(循环次数),第二个参数是Source,修改后的结果如下:

__int64 __fastcall sub_1400148C0(unsigned int rounds, unsigned int *source, _DWORD *key)
{
  __int64 result; // rax
  unsigned int i; // [rsp+24h] [rbp+4h]
  unsigned int v5; // [rsp+44h] [rbp+24h]
  unsigned int v6; // [rsp+64h] [rbp+44h]
  unsigned int v7; // [rsp+84h] [rbp+64h]

  j___CheckForDebuggerJustMyCode(&unk_140028066, source, key);
  v5 = *source;
  v6 = source[1];
  v7 = 0;
  for ( i = 0; i < rounds; ++i )
  {
    v5 += (key[v7 & 3] + v7) ^ (v6 + ((v6 >> 5) ^ (16 * v6)));
    v7 -= 855655493;
    v6 += (key[(v7 >> 11) & 3] + v7) ^ (v5 + ((v5 >> 5) ^ (16 * v5)));
  }
  *source = v5;
  result = 4LL;
  source[1] = v6;
  return result;
}

于是,我们可以观察到这是一个魔改了Delta的XTEA。
我们再返回main函数进行观察

  if ( j_strlen(Str) == 12 )
  {
    memset(v16, 0, 8uLL);
    j_memcpy(Src, Str, 8uLL);
    xtea_encrypt(len, (__int64)Src, (__int64)key);
    j_memcpy(Str, Src, 8uLL);
    j_memcpy(v16, &Str[4], 8uLL);
    xtea_encrypt(len, (__int64)v16, (__int64)key);
    j_memcpy(&Str[4], v16, 8uLL);
    for ( j = 0; j < 12; ++j )
    {
      if ( Str[j] != byte_140022000[j] )
        goto LABEL_5;
    }
    v10 = sub_1400110AA(std::cout, "Correct key! Your flag is moectf{your key}");
    std::ostream::operator<<(v10, sub_140011046);
    return 0;
  }

可以注意到,输入的数据是12字节,也就是flag长度是12字节,首先Str(输入的字符)前8个字节先拷贝到了Src里面,然后Src进行加密,加密了前8个字节(因为Xtea只加密8个字节),然后,memcpy把加密的8个字节再覆盖回Str里面,接下来,Memcpy对str+4拷贝(拷贝后8个字节)到v16中,然后对v16进行加密,所以上面的流程可以等效为:

xtea_encrypt(len, (__int64)Src, (__int64)key);
xtea_encrypt(len, (__int64)(Src + 4), (__int64)key);

最后和标准结果进行对比。
我们写出最后的EXP *(由于Regadgets库暂时不公开,所以我只放出了部分使用到的函数的源码。)

enc = [0xA3, 0x69, 0x96, 0x26, 0xBD, 0x78, 0x0B, 0x3D, 0x9D, 0xA5, 0x28, 0x62]
key = [2, 0, 2, 4]

# 拆分成3个DWORD
dw1, dw2, dw3 = byte2dword(enc)

dw2, dw3 = xtea_decrypt((dw2, dw3), key=key, delta=-0x33004445, rounds=32)
dw1, dw2 = xtea_decrypt((dw1, dw2), key=key, delta=-0x33004445, rounds=32)

print(dword2byte([dw1, dw2, dw3]))
# b'moectf2024!!'

'''
from regadgets import *
Internal Functions Of Regadgets
'''
from typing import List, Tuple
from struct import unpack
from ctypes import c_uint32
def byte2dword(x: List[int]):
    if len(x) % 4 != 0:
        if type(x) == bytes:
            x += b'\x00' * (4 - (len(x) % 4))
        else:
            x += [0] * (4 - (len(x) % 4))
    return [v[0] for v in (unpack('<I', bytes(x[i:i+4])) for i in range(0, len(x), 4))]

def xtea_decrypt(
    src: Tuple[int, int], key: List[int], delta: int = 0x9E3779B9, rounds: int = 32
):
    l, r = c_uint32(src[0]), c_uint32(src[1])
    sum = c_uint32(delta * rounds)
    k = [c_uint32(key[0]), c_uint32(key[1]), c_uint32(key[2]), c_uint32(key[3])]
    for _ in range(rounds):
        r.value -= (((l.value << 4) ^ (l.value >> 5)) + l.value) ^ (
            sum.value + k[(sum.value >> 11) & 3].value
        )
        sum.value -= delta
        l.value -= (((r.value << 4) ^ (r.value >> 5)) + r.value) ^ (
            sum.value + k[sum.value & 3].value
        )
    return (l.value, r.value)

def dword2byte(x: List[int]):
    result = []
    if type(x) == int:
        for j in range(4):
            result.append((x >> j*8) & 0xff)
        return bytes(result)
    for i in range(len(x)):
        for j in range(4):
            result.append((x[i] >> j*8) & 0xff)
    return bytes(result)
'''
Internal Functions Of Regadgets
'''

所以最后的flag是moectf{moectf2024!!}

dotNet(d0tN3t)

dll(核心代码)拖入dnSpy进行分析

using System;
using System.Runtime.CompilerServices;

// Token: 0x02000002 RID: 2
[CompilerGenerated]
internal class Program
{
    // Token: 0x06000001 RID: 1 RVA: 0x00002050 File Offset: 0x00000250
    private static void <Main>$(string[] args)
    {
        byte[] array = new byte[]
        {
            0xAD,
            .....
            0xA4
        };
        Console.WriteLine("Input Your Flag:");
        string text = Console.ReadLine();
        if (text.Length != array.Length)
        {
            Console.WriteLine("Flag is WRONG!!!");
            return;
        }
        int num = 1;
        for (int i = 0; i < array.Length; i++)
        {
            if ((byte)((int)((byte)text[i] + 0x72 ^ 0x72) ^ i * i) != array[i])
            {
                num &= 0;
            }
        }
        if (num == 1)
        {
            Console.WriteLine("Correct Flag!!!");
            return;
        }
        Console.WriteLine("Flag is WRONG!!!");
    }
}

注意到flag的加密逻辑

(byte)((int)((byte)text[i] + 0x72 ^ 0x72) ^ i * i)

写出解密脚本即可

byte[] s3cr3t = {173, 146, 161, 174, 132, 179, 187, 234, 231, 244, 177, 161, 65, 13, 18, 12, 166, 247, 229, 207, 125, 109, 67, 180, 230, 156, 125, 127, 182, 236, 105, 21, 215, 148, 92, 18, 199, 137, 124, 38, 228, 55, 62, 164};
for (int i = 0; i < s3cr3t.Length; i++)
{
    Console.Out.Write((char)((byte)(s3cr3t[i] ^ i*i ^ 114) - 114));
}

同时我们给出python的exp

enc = [173, 146, 161, 174, 132, 179, 187, 234, 231, 244, 177, 161, 65, 13, 18, 12, 166, 247, 229, 207, 125, 109, 67, 180, 230, 156, 125, 127, 182, 236, 105, 21, 215, 148, 92, 18, 199, 137, 124, 38, 228, 55, 62, 164]

for i in range(len(enc)):
    print(chr(((i*i ^ 114 ^ enc[i]) - 114) & 0xff), end='')

# moectf{7ce581d2-b2ab-4ceb-9bbe-435873083db6}

由于很多人用Python写的,但是没有注意到Python的int是无限精度的,所以我们必须限制它在0xff(byte)的范围内,才可以解出flag,不然可能会出现负数的情况。
flag: moectf{7ce581d2-b2ab-4ceb-9bbe-435873083db6}

RC4

拖入IDA,我们发现ida没有找到main函数,这时按Shift+F12,定位String,然后对着String按X(交叉引用),就可以定位到main函数。

__int64 __fastcall sub_4013F0(__int64 a1, const char *a2)
{
  unsigned __int64 v2; // rax
  __int64 v3; // rax
  __int64 v4; // rax
  __int64 v6; // [rsp+18h] [rbp-88h]
  int i; // [rsp+24h] [rbp-7Ch]
  __int64 v8; // [rsp+28h] [rbp-78h]
  _BYTE v9[72]; // [rsp+50h] [rbp-50h] BYREF
  unsigned __int64 v10; // [rsp+98h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  v2 = sub_404748("A71A68ECD82711CC8C9B16155CD2673E82ADCE75D4BC5756C28A52B86BD6CCF8A4BA722FE05715B92411");
  v8 = sub_401B01(v2 >> 1);
  for ( i = 0;
        i < (unsigned __int64)sub_404748("A71A68ECD82711CC8C9B16155CD2673E82ADCE75D4BC5756C28A52B86BD6CCF8A4BA722FE05715B92411") >> 1;
        ++i )
  {
    sub_40202D((__int64)&aA71a68ecd82711[2 * i], (__int64)"%02x", i + v8);
  }
  sub_401F8D("Welcome to MoeCTF 2024!");
  sub_401F8D("This is a simple RC4 challenge.");
  sub_401ECD((__int64)"Enter your flag: ");
  sub_401BCC(&unk_40E120);
  sub_401D17(v9, 64LL, &unk_40E020);
  v9[sub_40469F(v9, "\n")] = 0;
  v3 = sub_404748(v9);
  v6 = sub_401B01(4 * v3);
  sub_401360("RC4_1s_4w3s0m3", v9, v6);
  v4 = sub_404748(v9);
  if ( (unsigned int)sub_404679(v6, v8, v4) )
    sub_401F8D("Wrong!");
  else
    sub_401F8D("Correct!");
  if ( __readfsqword(0x28u) != v10 )
    sub_40183E();
  return 0LL;
}

显然题目说了是Rc4,并且RC4_1s_4w3s0m3很可能是密码,我们直接拿上面的HEX进行解密就出了。

print(rc4_crypt(rc4_init(b"RC4_1s_4w3s0m3"), bytes.fromhex('A71A68ECD82711CC8C9B16155CD2673E82ADCE75D4BC5756C28A52B86BD6CCF8A4BA722FE05715B92411')))
# b'moectf{why_Rc4_haS_The_Rev32sabl3_pr0ceSS}'

'''
from regadgets import *
Internal Functions Of Regadgets
'''
from typing import List, Iterator
def rc4_init(key: bytes, box_size: int = 256) -> List[int]:
    if type(key) == str:
        key = key.encode()
    s = list(range(box_size))
    j = 0
    key_length = len(key)

    # Key scheduling algorithm (KSA)
    for i in range(box_size):
        # permit key is empty.
        j = (j + s[i] + 0 if key_length == 0 else j + s[i] + key[i % key_length]) % box_size
        # Swap s[i], s[j]
        s[i], s[j] = s[j], s[i]

    return s

def rc4_crypt(s: bytes, data: bytes, box_size: int = 256) -> bytes:
    i, j = 0, 0
    result = bytearray()

    # Pseudo-random generation algorithm (PRGA)
    for k in range(len(data)):
        i = (i + 1) %  box_size
        j = (j + s[i]) % box_size

        # Swap s[i], s[j]
        s[i], s[j] = s[j], s[i]

        t = (s[i] + s[j]) % box_size
        result.append(data[k] ^ s[t])

    return bytes(result)
'''
Internal Functions Of Regadgets
'''

xxtea

给出题目源码

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[((p&3)^e)&0xff] ^ z)))

void btea(uint32_t* v, int n, const uint32_t key[4])
{
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1)            /* Coding Part */
    {
        rounds = 6 + 52 / n;
        sum = 0;
        z = v[n - 1];
        do
        {
            sum += DELTA;
            e = (sum >> 2) & 3;
            for (p = 0; p < n - 1; p++)
            {
                y = v[p + 1];
                z = v[p] += MX;
            }
            y = v[0];
            z = v[n - 1] += MX;
        } while (--rounds);
    }
    else if (n < -1)      /* Decoding Part */
    {
        n = -n;
        rounds = 6 + 52 / n;
        sum = rounds * DELTA;
        y = v[0];
        do
        {
            e = (sum >> 2) & 3;
            for (p = n - 1; p > 0; p--)
            {
                z = v[p - 1];
                y = v[p] -= MX;
            }
            z = v[n - 1];
            y = v[0] -= MX;
            sum -= DELTA;
        } while (--rounds);
    }
}
int main() {
    unsigned char enc[] = { 0x64,0xf5,0xe1,0x78,0xe1,0xf0,
0x35,0xa8,0x34,0xff,0x12,0x05,
0xfb,0x13,0xe9,0xb0,0x50,0xa3,
0xb9,0x89,0xb1,0xda,0x43,0xc9,
0x4f,0xc8,0xdb,0x01,0x20,0xdb,
0x16,0xaf,0xed,0x67,0x17,0x96
    };
    int r = 9;
    unsigned char input_key[13] = "moectf2024!!"; 
    const uint32_t k[4] = { *((uint32_t*)input_key),*((uint32_t*)(input_key + 4)), *((uint32_t*)(input_key + 8)),0xccffbbbb };
    btea(((uint32_t*)enc),-r,k);
    //btea(((uint32_t*)input), -r, ((uint32_t*)input_key));
    for (int i = 0; i < 36; i++) {
        printf("%c",enc[i]);
    }
    return 0;
}

EXP:

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



#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[((p&3)^e)&0xff] ^ z)))

void btea(uint32_t* v, int n, const uint32_t key[4])
{
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1)            /* Coding Part */
    {
        rounds = 6 + 52 / n;
        sum = 0;
        z = v[n - 1];
        do
        {
            sum += DELTA;
            e = (sum >> 2) & 3;
            for (p = 0; p < n - 1; p++)
            {
                y = v[p + 1];
                z = v[p] += MX;
            }
            y = v[0];
            z = v[n - 1] += MX;
        } while (--rounds);
    }
    else if (n < -1)      /* Decoding Part */
    {
        n = -n;
        rounds = 6 + 52 / n;
        sum = rounds * DELTA;
        y = v[0];
        do
        {
            e = (sum >> 2) & 3;
            for (p = n - 1; p > 0; p--)
            {
                z = v[p - 1];
                y = v[p] -= MX;
            }
            z = v[n - 1];
            y = v[0] -= MX;
            sum -= DELTA;
        } while (--rounds);
    }
}
int main() {
    unsigned char enc[] = { 0x64,0xf5,0xe1,0x78,0xe1,0xf0,
0x35,0xa8,0x34,0xff,0x12,0x05,
0xfb,0x13,0xe9,0xb0,0x50,0xa3,
0xb9,0x89,0xb1,0xda,0x43,0xc9,
0x4f,0xc8,0xdb,0x01,0x20,0xdb,
0x16,0xaf,0xed,0x67,0x17,0x96
    };
    int r = 9;
    unsigned char input_key[13] = "moectf2024!!"; // Adjusted size to fit 12 characters + null terminator
    
    const uint32_t k[4] = { *((uint32_t*)input_key),*((uint32_t*)(input_key + 4)), *((uint32_t*)(input_key + 8)),0xccffbbbb };
    btea(((uint32_t*)enc),-r,k);
    //moectf{j9h8hg75nky6vhkslh5v5awibr4i}
    //btea(((uint32_t*)input), -r, ((uint32_t*)input_key));
    for (int i = 0; i < 36; i++) {
        printf("%c",enc[i]);
    }
    return 0;
}

选手只需要在网上找到xxtea实现并且对照逆向即可。
moectf{j9h8hg75nky6vhkslh5v5awibr4i}

TEA

本题是标准的TEA(难度低于XTEA和XXTEA),选手只需要打开IDA找到v = {0x284c2234, 0x3910c558}, k = "base64xorteaxtea",再通过观察,对比程序内TEA和正常TEA逻辑相同,故写出解密脚本:

#include <iostream>
#include <stdint.h>

void decrypt(uint32_t* v, uint32_t* k) {
        uint32_t v0 = v[0], v1 = v[1];
        uint32_t delta = 0x9e3779b9;
        uint32_t sum = delta * 32;
        uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];

        for (int i = 0; i < 32; i++) {
                v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
                v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
                sum -= delta;
        }

        v[0] = v0;
        v[1] = v1;
}


int main()
{
        uint32_t v[2]; uint32_t v2;
        v[0] = 0x284c2234;
        v[1] = 0x3910c558;
        decrypt(v, (uint32_t*)"base64xorteaxtea");
        printf("moectf{%x-", v[0]);
        printf("%x-", v[1] >> 16 );
        printf("%x-9c42-caf30620caaf}", v[1] & 0xffff);
        return 0;
}

moectf{836153a5-8e00-49bd-9c42-caf30620caaf}

逆向工程进阶之北

这个指北中的问题是,如何求解乘法逆元。
我们使用Python的库pycryptodome3

>>> from Crypto.Util.number import *
>>> inverse(0xccffbbbb, 0xffffffff+1) 
2371998067

很容易就可以求得乘法逆元,注意的是要+1,是因为0xffffffff范围内还有0,而模数(第二个参数)取决于有限域的大小而不是最大值。
EXP

void flag_decryption()
{
        DWORD flag[12] = { 0xb5073388 , 0xf58ea46f , 0x8cd2d760 , 0x7fc56cda , 0x52bc07da , 0x29054b48 , 0x42d74750 , 0x11297e95 , 0x5cf2821b , 0x747970da , 0x64793c81, 0x00000000 };
        for (int i = 0; i < 11; i++)
        {
                *(flag + i) ^= 0xdeadbeef + 0xd3906;
                *(flag + i) -= 0xdeadc0de;
                *(flag + i) *= 2371998067; // 0xccffbbbb 的乘法逆元(在mod 0xffffffff+1下) Ref: https://zh.planetcalc.com/3311/
        }

        std::cout << (unsigned char*)flag << std::endl;

        // moectf{c5f44c32-cbb9-444e-aef4-c0fa7c7a6b7a}
}

moectf{c5f44c32-cbb9-444e-aef4-c0fa7c7a6b7a}

moedaily

题目实现了一个Excel表格,利用其中的Function功能实现了一个TEA加密。
在表单可以发现有s3cr3t,进去后可以看到满屏的计算过程
进行分析,发现是密码为 114514 1919810 415144 19883
偏移为114514的标准TEA加密,而且加密了两次。
回到第一页,找到判断条件

=IF(LEN(D11)=48,IF(AND(AND(AND(AND(AND(AND(H14=1397140385,I14=2386659843),AND(H15=962571399,I15=3942687964)),AND(H16=3691974192,I16=863943258)),AND(H17=216887638,I17=3212824238)),AND(H18=3802077983,I18=1839161422)),AND(H19=1288683919,I19=3222915626)),"恭喜你,拿到了真的FLAG","FLAG输入错了,再试试"),"flag长度不对")

可以得到结论

H14=1397140385,I14=2386659843
H15=962571399,I15=3942687964
H16=3691974192,I16=863943258
H17=216887638,I17=3212824238
H18=3802077983,I18=1839161422
H19=1288683919,I19=3222915626

带入TEA可以得到(注意程序进行了两次TEA加密)
moectf{3xC3l_1S_n0t_just_f0r_d41ly_w0rk_bu7_R3V}

moejvav

题目是在Java下的简单虚拟机(vm)逆向工程。
稍微分析一下,得到下面的结论

StepInstruction
0getByte
1xor store, p1
2add store, p1
3sub store, p1
4shl store, p1
5or store, p1
6check store, p1
7Exit

把VM的Insn进行排列

0 1 60 2 -20 6 -25
0 1 60 2 -20 6 -27
0 1 60 2 -20 6 -33
0 1 60 2 -20 6 -31
0 1 60 2 -20 6 -50
0 1 60 2 -20 6 -36
0 1 60 2 -20 6 -39
0 1 60 2 -20 6 -24
0 1 60 2 -20 6 -52
0 1 60 2 -20 6 -29
0 1 60 2 -20 6 -52
0 1 14 2 5 6 -64
0 1 14 2 5 6 -58
0 1 14 2 5 6 -63
0 1 14 2 5 6 -52
0 1 14 2 5 6 -90
0 1 14 2 5 6 -39
0 1 14 2 5 6 -43
0 1 14 2 5 6 26
0 1 14 2 5 6 25
0 1 14 2 5 6 -49
0 1 14 2 5 6 -64
0 1 10 2 5 6 -51
0 1 10 2 5 6 25
0 1 10 2 5 6 -45
0 1 10 2 5 6 -55
0 1 10 2 5 6 -47
0 1 10 2 5 6 24
0 1 10 2 5 6 -41
0 1 10 2 5 6 -60
0 1 10 2 5 6 22
0 1 10 2 5 6 -40
0 1 10 2 5 6 -60
0 2 14 2 10 6 -15
0 2 14 2 10 6 50
0 2 14 2 10 6 -51
0 2 14 2 10 6 -31
0 2 14 2 10 6 50
0 2 14 2 10 6 50
0 2 14 2 10 6 -35
0 2 14 2 10 6 50
0 2 14 2 10 6 -35
0 2 14 2 10 6 51
0 2 14 2 10 6 -17

这里我们使用Z3-Solver进行约束求解

from z3 import *
import copy
x = [BitVec(f"x{i}", 8) for i in range(44)]
y = copy.deepcopy(x)
for i in range(len(x)):
   x[i] ^= 0xca
   x[i] += 0x20

s = Solver()

ans1 = [-25,-27,-33,-31,-50,-36,-39,-24,-52,-29,-52]
for i in range(0, 11):
   s.add((x[i] ^ 60) - 20 == ans1[i]) 

ans2 = [-64,-58,-63,-52,-90,-39,-43,26,25,-49,-64]
for i in range(0, 11):
   s.add((x[11 + i] ^ 14) + 5 == ans2[i]) 

ans3 = [-51,25,-45,-55,-47,24,-41,-60,22,-40,-60]
for i in range(0, 11):
   s.add((x[22 + i] ^ 10) + 5 == ans3[i]) 

ans4 = [-15,50,-51,-31,50,50,-35,50,-35,51,-17]
for i in range(0, 11):
   s.add((x[33 + i] + 14) + 10 == ans4[i]) 

print(s.check())
m = s.model()
for i in y:
   print(chr(m[i].as_long()), end='') 
# sat
# moectf{jvav_eXcEpt10n_h4ndl3r_1s_s0_c00o0o1}

moectf{jvav_eXcEpt10n_h4ndl3r_1s_s0_c00o0o1}

sm4

题目头文件

#ifndef _SM4_H_
#define _SM4_H_
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define u8 unsigned char
#define u32 unsigned long

void four_uCh2uLong(u8 *in, u32 *out);             //四字节转换成u32

void uLong2four_uCh(u32 in, u8 *out);              //u32转换成四字节

unsigned long move(u32 data, int length);          //左移,保留丢弃位放置尾部

unsigned long func_key(u32 input);                 //先使用Sbox进行非线性变化,再将线性变换L置换为L'

unsigned long func_data(u32 input);                //先使用Sbox进行非线性变化,再进行线性变换L

void print_hex(u8 *data, int len);                 //无符号字符数组转16进制打印

void encode_fun(u8 len,u8 *key, u8 *input, u8 *output);   //加密函数

void decode_fun(u8 len,u8 *key, u8 *input, u8 *output);   //解密函数

/******************************定义系统参数FK的取值****************************************/
const u32 TBL_SYS_PARAMS[4] = {
        0xa3b1bac6,
        0x56aa3350,
        0x677d9197,
        0xb27022dc
};

/******************************定义固定参数CK的取值****************************************/
const u32 TBL_FIX_PARAMS[32] = {

    0x00070e15,0x1c232a31,0x383f464d,0x545b6269,
        0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
        0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,
        0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,
        0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,
        0x30373e45,0x4c535a61,0x686f767d,0x848b9299,
        0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,
        0x10171e25,0x2c333a41,0x484f565d,0x646b7279
};

/******************************SBox参数列表****************************************/
const u8 TBL_SBOX[256] = {

    0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
        0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
        0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
        0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
        0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
        0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
        0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
        0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
        0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
        0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
        0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
        0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
        0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
        0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
        0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
        0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
};

#endif

主程序

#include "sm4.h"


//4字节无符号数组转无符号long型
void four_uCh2uLong(u8 *in, u32 *out)
{
        int i = 0;
        *out = 0;
        for (i = 0; i < 4; i++)
                *out = ((u32)in[i] << (24 - i * 8)) ^ *out;
}

//无符号long型转4字节无符号数组
void uLong2four_uCh(u32 in, u8 *out)
{
        int i = 0;
        //从32位unsigned long的高位开始取
        for (i = 0; i < 4; i++)
                *(out + i) = (u32)(in >> (24 - i * 8));
}

//左移,保留丢弃位放置尾部
u32 move(u32 data, int length)
{
        u32 result = 0;
        result = (data << length) ^ (data >> (32 - length));

        return result;
}

//秘钥处理函数,先使用Sbox进行非线性变化,再将线性变换L置换为L'
u32 func_key(u32 input)
{
        int i = 0;
        u32 ulTmp = 0;
        u8 ucIndexList[4] = { 0 };
        u8 ucSboxValueList[4] = { 0 };
        uLong2four_uCh(input, ucIndexList);
        for (i = 0; i < 4; i++)
        {
                ucSboxValueList[i] = TBL_SBOX[ucIndexList[i]];
        }
        four_uCh2uLong(ucSboxValueList, &ulTmp);
        ulTmp = ulTmp ^ move(ulTmp, 13) ^ move(ulTmp, 23);

        return ulTmp;
}

//加解密数据处理函数,先使用Sbox进行非线性变化,再进行线性变换L
u32 func_data(u32 input)
{
        int i = 0;
        u32 ulTmp = 0;
        u8 ucIndexList[4] = { 0 };
        u8 ucSboxValueList[4] = { 0 };
        uLong2four_uCh(input, ucIndexList);
        for (i = 0; i < 4; i++)
        {
                ucSboxValueList[i] = TBL_SBOX[ucIndexList[i]];
        }
        four_uCh2uLong(ucSboxValueList, &ulTmp);
        ulTmp = ulTmp ^ move(ulTmp, 2) ^ move(ulTmp, 10) ^ move(ulTmp, 18) ^ move(ulTmp, 24);

        return ulTmp;
}

//加密函数(可以加密任意长度数据,16字节为一次循环,不足部分补0凑齐16字节的整数倍)
//len:数据长度(任意长度数据) key:密钥(16字节) input:输入的原始数据 output:加密后输出数据
void encode_fun(u8 len,u8 *key, u8 *input, u8 *output)
{
        int i = 0,j=0; 
        u8 *p = (u8 *)malloc(50);      //定义一个50字节缓存区
        u32 ulKeyTmpList[4] = { 0 };   //存储密钥的u32数据
        u32 ulKeyList[36] = { 0 };     //用于密钥扩展算法与系统参数FK运算后的结果存储
        u32 ulDataList[36] = { 0 };    //用于存放加密数据

        /***************************开始生成子秘钥********************************************/
        four_uCh2uLong(key, &(ulKeyTmpList[0]));
        four_uCh2uLong(key + 4, &(ulKeyTmpList[1]));
        four_uCh2uLong(key + 8, &(ulKeyTmpList[2]));
        four_uCh2uLong(key + 12, &(ulKeyTmpList[3]));

        ulKeyList[0] = ulKeyTmpList[0] ^ TBL_SYS_PARAMS[0];
        ulKeyList[1] = ulKeyTmpList[1] ^ TBL_SYS_PARAMS[1];
        ulKeyList[2] = ulKeyTmpList[2] ^ TBL_SYS_PARAMS[2];
        ulKeyList[3] = ulKeyTmpList[3] ^ TBL_SYS_PARAMS[3];

        for (i = 0; i < 32; i++)             //32次循环迭代运算
        {
                //5-36为32个子秘钥
                ulKeyList[i + 4] = ulKeyList[i] ^ func_key(ulKeyList[i + 1] ^ ulKeyList[i + 2] ^ ulKeyList[i + 3] ^ TBL_FIX_PARAMS[i]);
        }
        /***********************************生成32轮32位长子秘钥结束**********************************/

        for (i = 0; i < len; i++)        //将输入数据存放在p缓存区
                *(p + i) = *(input + i);
        for (i = 0; i < 16-len % 16; i++)//将不足16位补0凑齐16的整数倍
                *(p + len + i) = 0;

        for (j = 0; j < len / 16 + ((len % 16) ? 1:0); j++)  //进行循环加密,并将加密后数据保存(可以看出此处是以16字节为一次加密,进行循环,即若16字节则进行一次,17字节补0至32字节后进行加密两次,以此类推)
        {
                /*开始处理加密数据*/
                four_uCh2uLong(p + 16 * j, &(ulDataList[0]));
                four_uCh2uLong(p + 16 * j + 4, &(ulDataList[1]));
                four_uCh2uLong(p + 16 * j + 8, &(ulDataList[2]));
                four_uCh2uLong(p + 16 * j + 12, &(ulDataList[3]));
                //加密
                for (i = 0; i < 32; i++)
                {
                        ulDataList[i + 4] = ulDataList[i] ^ func_data(ulDataList[i + 1] ^ ulDataList[i + 2] ^ ulDataList[i + 3] ^ ulKeyList[i + 4]);
                }
                /*将加密后数据输出*/
                uLong2four_uCh(ulDataList[35], output + 16 * j);
                uLong2four_uCh(ulDataList[34], output + 16 * j + 4);
                uLong2four_uCh(ulDataList[33], output + 16 * j + 8);
                uLong2four_uCh(ulDataList[32], output + 16 * j + 12);
        }
}

//解密函数(与加密函数基本一致,只是秘钥使用的顺序不同,即把钥匙反着用就是解密)
//len:数据长度 key:密钥 input:输入的加密后数据 output:输出的解密后数据
void decode_fun(u8 len,u8 *key, u8 *input, u8 *output)
{
        int i = 0,j=0;
        u32 ulKeyTmpList[4] = { 0 };//存储密钥的u32数据
        u32 ulKeyList[36] = { 0 };  //用于密钥扩展算法与系统参数FK运算后的结果存储
        u32 ulDataList[36] = { 0 }; //用于存放加密数据

        /*开始生成子秘钥*/
        four_uCh2uLong(key, &(ulKeyTmpList[0]));
        four_uCh2uLong(key + 4, &(ulKeyTmpList[1]));
        four_uCh2uLong(key + 8, &(ulKeyTmpList[2]));
        four_uCh2uLong(key + 12, &(ulKeyTmpList[3]));

        ulKeyList[0] = ulKeyTmpList[0] ^ TBL_SYS_PARAMS[0];
        ulKeyList[1] = ulKeyTmpList[1] ^ TBL_SYS_PARAMS[1];
        ulKeyList[2] = ulKeyTmpList[2] ^ TBL_SYS_PARAMS[2];
        ulKeyList[3] = ulKeyTmpList[3] ^ TBL_SYS_PARAMS[3];

        for (i = 0; i < 32; i++)             //32次循环迭代运算
        {
                //5-36为32个子秘钥
                ulKeyList[i + 4] = ulKeyList[i] ^ func_key(ulKeyList[i + 1] ^ ulKeyList[i + 2] ^ ulKeyList[i + 3] ^ TBL_FIX_PARAMS[i]);
        }
        /*生成32轮32位长子秘钥结束*/

        for (j = 0; j < len / 16; j++)  //进行循环加密,并将加密后数据保存
        {
                /*开始处理解密数据*/
                four_uCh2uLong(input + 16 * j, &(ulDataList[0]));
                four_uCh2uLong(input + 16 * j + 4, &(ulDataList[1]));
                four_uCh2uLong(input + 16 * j + 8, &(ulDataList[2]));
                four_uCh2uLong(input + 16 * j + 12, &(ulDataList[3]));

                //解密
                for (i = 0; i < 32; i++)
                {
                        ulDataList[i + 4] = ulDataList[i] ^ func_data(ulDataList[i + 1] ^ ulDataList[i + 2] ^ ulDataList[i + 3] ^ ulKeyList[35 - i]);//与加密唯一不同的就是轮密钥的使用顺序
                }
                /*将解密后数据输出*/
                uLong2four_uCh(ulDataList[35], output + 16 * j);
                uLong2four_uCh(ulDataList[34], output + 16 * j + 4);
                uLong2four_uCh(ulDataList[33], output + 16 * j + 8);
                uLong2four_uCh(ulDataList[32], output + 16 * j + 12);
        }
}

//无符号字符数组转16进制打印
void print_hex(u8 *data, int len)
{
        int i = 0;
        char alTmp[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
        for (i = 0; i < len; i++)
        {
                printf("%c", alTmp[data[i] / 16]);
                printf("%c", alTmp[data[i] % 16]);
        putchar(' ');
        }
        putchar('\n');
}
/*在主函数中实现任意字节加密与解密,并且结果正确*/
int main(void)
{
        u8 i,len;
        u8 encode_Result[50] = { 0 };    //定义加密输出缓存区
        u8 decode_Result[50] = { 0 };    //定义解密输出缓存区
        u8 key[16] = { 0x74,0x68,0x65,0x6b,0x65,0x79,0x74,0x6f,0x73,0x6f,0x6d,0x65,0x74,0x68,0x69,0x6e };       //定义16字节的密钥
    /*u8 Data_plain[45]={0x6d,0x6f,0x65,0x63,0x74,0x66,
                             0x7b,0x43,0x6f,0x6e,0x67,0x72,
                                                 0x61,0x74,0x75,0x6c,0x61,0x74,
                                                 0x69,0x6f,0x6e,0x73,0x5f,0x79,
                                                 0x6f,0x75,0x5f,0x61,0x72,0x65,
                                                 0x5f,0x61,0x6e,0x5f,0x53,0x4d,
                                                 0x34,0x5f,0x6d,0x61,0x73,0x74,
                                                 0x65,0x72,0x21,0x21,0x21,0x7d};//moectf{Congratulations_you_are_an_SM4_master!!!}*/
        u8 Data_plain[48]={0};
        printf("please input your flag:\n");
        scanf("%s",Data_plain);
        len = 16 * (sizeof(Data_plain) / 16) + 16 * ((sizeof(Data_plain) % 16) ? 1 : 0);

        encode_fun(sizeof(Data_plain),key, Data_plain, encode_Result);            //数据加密
        //printf("加密后数据是:\n");
        //for (i = 0; i < len ; i++)
        //        printf("0x%x,", *(encode_Result + i));
        u8 enc[48]={0xad,0x6c,0xcd,0xc1,0x9,0xfc,0xdd,0xef,0x83,0xae,0x93,0x8,0x53,0x8e,0xc5,0x37,0x5c,0xdd,0x1b,0x4b,0x3,0x99,0x19,0xa2,0x69,0x24,0x96,0x42,0x77,0xc1,0x27,0x5f,0x2d,0xd4,0x5d,0xf5,0x2b,0xb0,0x32,0xf7,0xa5,0x97,0xc6,0x8a,0xee,0x48,0xae,0x93};                
        decode_fun(len,key, encode_Result, decode_Result);      //数据解密
        printf("解密后数据是:\n");
        for (i = 0; i < len; i++)
            printf("%x ", *(decode_Result + i)); 
        for(i=0;i<48;i++)
        {
                if(enc[i]!=*(encode_Result+i))
                {
                        printf("Wrong!");
                        exit(0);
                }
        }
        printf("Congratulations");
        system("pause");
        return 0;
}

是从网上找到的SM4加密实现,但是程序在输入的时候,使用了scanf并且当你输入正确的flag长度的时候,最后一个是0字符截断,他会覆盖下一个变量的首字节,刚好下一个变量是key,所以key的首字节变成了0,导致很多人解不出来,其实只要输入一个正确长度的flag就可以进行解密。
moectf{Congratulations_you_are_an_SM4_master!!!}

ezMAZE

题目使用了OI常用技巧——状态压缩来存储迷宫,使用二进制的1和0代表是否可以通过。
题目源码

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

char buf[1177] = { 0 };
unsigned char theMaze[56][10] = {
        0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
        0xbf,0xff,0xea,0xa8,0xa4,0x92,0x4f,0xff,0xff,0xff,
        0x80,0x00,0x3d,0x14,0x94,0x92,0xb8,0x00,0x00,0xff,
        0xff,0xff,0xbb,0xda,0x1f,0x29,0x7b,0xff,0xfe,0xff,
        0xc0,0x00,0x3b,0xda,0xdc,0x00,0x03,0x00,0x00,0xff,
        0xdf,0xff,0xfb,0xda,0xdd,0xff,0xff,0x7f,0xff,0xff,
        0xc0,0x00,0x3b,0xda,0xdc,0x00,0x03,0x00,0x00,0xff,
        0xff,0xff,0xb8,0x42,0x1f,0xff,0xfb,0xff,0xfe,0xff,
        0xc0,0x00,0x3f,0xff,0xfc,0x00,0x03,0x00,0x00,0xff,
        0xdf,0xff,0xf8,0x7c,0x3d,0xff,0xff,0x7f,0xff,0xff,
        0xc0,0x00,0x3b,0xfd,0xbc,0x00,0x03,0x00,0x3e,0x1f,
        0xff,0xff,0xb8,0x7c,0x7f,0xff,0xfb,0xff,0xbe,0xdf,
        0xff,0xff,0xbf,0x7d,0xbd,0x29,0x7b,0xff,0xbe,0xdf,
        0xff,0xff,0x98,0x7c,0x3f,0xff,0xfb,0xff,0xbe,0xdf,
        0x80,0x00,0x4f,0xff,0xf8,0x00,0x02,0x00,0x3e,0xdf,
        0xbf,0x7f,0x6f,0xff,0xfb,0xff,0xfe,0xff,0xfe,0xdf,
        0x96,0xab,0x60,0x00,0x08,0x00,0x02,0x01,0xfe,0xdf,
        0x96,0xab,0x7f,0xff,0xef,0xff,0xfb,0xfd,0xfe,0xdf,
        0x95,0x4b,0x70,0x00,0x0c,0x00,0x03,0x01,0xfe,0xdf,
        0x95,0x4b,0x77,0xff,0xfd,0xff,0xff,0x7f,0xfe,0xdf,
        0xaa,0x96,0x70,0x00,0x0c,0x00,0x03,0x00,0x06,0xdf,
        0x92,0x57,0x7f,0xff,0xef,0xff,0xfb,0xff,0xf6,0xdf,
        0x92,0x94,0x70,0x00,0x0c,0x00,0x03,0x00,0x06,0xdf,
        0x8a,0x4b,0x77,0xff,0xfd,0xff,0xff,0x7f,0xfe,0xdf,
        0xae,0xd5,0x70,0x00,0x0c,0x00,0x03,0x00,0x0e,0xdf,
        0x95,0x4a,0xff,0xff,0xef,0xff,0xfb,0xff,0xee,0xdf,
        0x8a,0x4a,0x4a,0x50,0xec,0x00,0x03,0xff,0xee,0xdf,
        0xbf,0x7f,0x7f,0xff,0xe1,0xff,0xff,0xff,0xee,0xdf,
        0xff,0xff,0xf5,0x5d,0x7f,0xff,0xff,0xff,0xee,0xdf,
        0x80,0x00,0x38,0xd5,0x35,0x2a,0x98,0x00,0x0e,0xdf,
        0xbf,0xff,0xbd,0x51,0x2f,0xff,0xfb,0xff,0xfe,0xdf,
        0xa0,0x00,0x3f,0xff,0xfc,0x00,0x03,0x00,0x00,0xdf,
        0xaf,0xff,0xff,0xc0,0x7d,0xff,0xff,0x7f,0xff,0xdf,
        0xa0,0x00,0x3f,0xdf,0x7c,0x00,0x03,0x00,0x00,0xdf,
        0xbf,0xff,0xbf,0xdf,0x7f,0xff,0xfb,0xff,0xfe,0xdf,
        0xa0,0x00,0x3f,0xdf,0x7c,0x00,0x03,0x00,0x00,0xdf,
        0xaf,0xff,0xff,0xdf,0x7d,0xff,0xff,0x7f,0xff,0xdf,
        0xa0,0x00,0x3f,0xdf,0x7c,0x00,0x03,0x00,0x00,0xdf,
        0xbf,0xff,0xbf,0xdf,0x7f,0xff,0xfb,0xff,0xfe,0xdf,
        0xbc,0x8b,0xbf,0xdf,0x7f,0xff,0xfb,0xff,0xfe,0xdf,
        0xa9,0x24,0x9f,0xdf,0x7f,0xff,0xfb,0xff,0xfe,0x5f,
        0xb2,0xa1,0xcf,0xdf,0x78,0x00,0x02,0x00,0x03,0x5f,
        0xb1,0x52,0xef,0xdf,0xfb,0xff,0xfe,0xff,0xfb,0x5f,
        0xaa,0x22,0xe0,0x00,0x08,0x00,0x02,0x01,0xf8,0x5f,
        0xbd,0x29,0xff,0xff,0xef,0xff,0xfb,0xfd,0xff,0xdf,
        0xb2,0x52,0xb0,0x00,0x0c,0x00,0x03,0x01,0xff,0xdf,
        0xb4,0x94,0xb7,0xff,0xfd,0xff,0xff,0x7f,0xff,0xdf,
        0xb2,0x4a,0xb0,0x00,0x0c,0x00,0x03,0x00,0x00,0xdf,
        0xb2,0x52,0x5f,0xff,0xef,0xff,0xfb,0xff,0xfe,0xdf,
        0xb2,0x54,0xd0,0x00,0x0c,0x00,0x03,0x00,0x00,0xdf,
        0xb2,0x52,0x57,0xff,0xfd,0xff,0xff,0x7f,0xff,0xdf,
        0xb2,0x91,0x30,0x00,0x00,0x00,0x0f,0x00,0x00,0xdf,
        0xb0,0x89,0x7f,0xff,0xff,0xff,0xf8,0x7f,0xfe,0xdf,
        0xbc,0x91,0x10,0xff,0xe0,0x00,0x03,0xff,0xfe,0xdf,
        0x80,0x00,0x00,0x00,0x0d,0xff,0xfe,0x94,0x8a,0x5f,
        0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
};

int checkForMaze(int x, int y)
{
        unsigned char val;
        // 1 means cannot pass.

        if (x > 10 * 8 || y > 56 || x < 1 || y < 1) return 1;
        val = theMaze[y - 1][(x - 1) / 8];
        theMaze[y - 1][(x - 1) / 8] = val | (1 << (7 - ((x - 1) % 8)));
        return ((val >> (7 - ((x - 1) % 8))) & 1);
}

char* calculateFlag(char* buf)
{
        unsigned long long sumOf;
        char* flag;
        flag = (char*)malloc(128);
        memcpy_s(flag, 40, "moectf{", 7);
        sumOf = 0;
        for (int i = 0; i < 1176; i++)
        {
                sumOf += (buf[i] * 3113131 * i + i * i + 0x114514) & 0xffffffffffffffff;
        }
        snprintf(flag + 7, 128 - 8, "the_%llu_amazing_maze!!}", sumOf);
        return flag;
}


int main()
{
        char* result;
        int x, y, i;
        printf("M0EC7F-2024 ezMaze\n");
        printf("ohno I'v stuck in a M4Z3... h3lp me (use w,a,s,d)\n");
        x = 2;
        y = 2;
        printf("please input the path sequence...\n");
        if (!scanf_s("%1177s", buf, 1177))
        {
                printf("input err.\n");
                return -1;
        }
        i = 0;
        while (1)
        {
                printf("\rx =%3d, y =%3d", x, y);
                if (x == 75 && y == 55)
                {
                        printf("\n%s\n", buf);
                        result = calculateFlag(buf);
                        printf("\nYOU WIN!!! the flag is %s\n", result);
                        free(result);
                        system("pause");
                        return 0;
                }
                if (!buf[i])
                {
                        printf("\nmission failed..\n");
                        return -1;
                }
                switch (buf[i++])
                {
                case 'w':
                        if (y > 1 && !checkForMaze(x, y - 1)) y -= 1;
                        else printf("You cannot walk the same path!!\n");
                        break;
                case 's':
                        if (y < 56 && !checkForMaze(x, y + 1)) y += 1;
                        else printf("You cannot walk the same path!!\n");
                        break;
                case 'a':
                        if (x > 1 && !checkForMaze(x - 1, y)) x -= 1;
                        else printf("You cannot walk the same path!!\n");
                        break;
                case 'd':
                        if (x < 10 * 8 && !checkForMaze(x + 1, y)) x += 1;
                        else printf("You cannot walk the same path!!\n");
                        break;
                default:
                        printf("you can only input w,a,s,d\n");
                        continue;
                }
        }
}

WP:
其一(抄出来,bfs):

using namespace std;


bool is_wall(int x, int y) {

uint8_t temp;


if (x > 80 || y > 56 || x < 1 || y < 1)
    return true;
temp = maze[10 * y - 10 + (x - 1) / 8];
maze[10 * y - 10 + (x - 1) / 8] = (1 << (7 - (x - 1) % 8)) | temp;
return ((int)temp >> (7 - (x - 1) % 8)) & 1;



}


string find_path(int begin_x, int begin_y, int end_x, int end_y) {

vector<pair<int, int>> dirs = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};

vector

<char> dir_chars = {'w', 's', 'a', 'd'};</char>




queue<tuple<int, int, string>> q;
set<pair<int, int>> visited;

q.push({begin_x, begin_y, ""});
visited.insert({begin_x, begin_y});

while (!q.empty()) {
    auto [x, y, path] = q.front();
    q.pop();

    if (x == end_x && y == end_y - 1) {
        return path;
    }
    
    for (int i = 0; i < dirs.size(); ++i) {
        int new_x = x + dirs[i].first;
        int new_y = y + dirs[i].second;

        if (!is_wall(new_x, new_y) && visited.find({new_x, new_y}) == visited.end()) {
            visited.insert({new_x, new_y});
            q.push({new_x, new_y, path + dir_chars[i]});
        }
    }
}

return nullptr;



}

因为那个函数其实就是判断(x,y)是否可通行,返回值也非常简单
其二(硬核爆破)

import subprocess

directions = ["w", "a", "s", "d"]

def move(path) -> str:
    result = subprocess.run(["echo", path, "|", "ezMaze.exe"], shell=True, capture_output=True, text=True)
    return result.stdout

def main() -> None:
    stack = [("", (2, 2))]
    visited = set()
    visited.add((2, 2))

    while stack:
        path, (x, y) = stack.pop()
        result = move(path)

        if "YOU WIN!!! the flag is" in result:
            print(path)
            return
        
        if "You cannot walk the same path!!" in result:
            continue

        for direction in directions:
            new_path = path + direction
            new_x, new_y = x, y

            if direction == "w":
                new_y -= 1
            elif direction == "a":
                new_x -= 1
            elif direction == "s":
                new_y += 1
            elif direction == "d":
                new_x += 1

            if (new_x, new_y) not in visited:
                visited.add((new_x, new_y))
                stack.append((new_path, (new_x, new_y)))

if __name__ == "__main__":
    main()

其三:
先用upx脱壳,本程序由于使用了x方向的状态压缩,把01的迷宫压缩成了一个数组,并且使用位运算进行读取。
所以把迷宫抄出来,用notepad++来显示路径,即可写出答案(或者使用bfs进行路径获取)

sddddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddsssdsdssddddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddsssdddwdddddddddddddddwwaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaaawwddddddddddddddddwwwwaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaawwdddddddddddddddwwddddddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddssaaaaaaaaaaaaaaassdddddddddssssaaaaaaaaaassdddddddssaaaaaassddddddddddddssaaaaaaaaaaaassdddddddddddsssssaaaaaaaaaaaaaassaaaaaaaaaaaaaaassdddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddssssaaaaaaaaaaaaaaaassddddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddssaaaaaaaaaaaaaaassaaaaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaaawwawawwwaaaaaaaaaaaaaawwddddddddddddddwwaaaaaaaaaaaaaawwddddddddddddddwwaaaaaaaaaaaaaaaasssssssssssssssssssssssssddddddddddddddddddddddddddddddddddwddddddddddddddddddwdddwwwdddddddddddddddwwaaaaaaaaaaaaaaawwddddddwwaaaaaaawwddddddddddddddssdddwwwawwwaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaawwdddddddddddddddwwwwwwwwwwwwwwwwwwwwwdddssssssssssssssssssssssssssssssssssssssssssss

路径是唯一解,因为不能往回走。(终点可以从程序里面拿到,是右下角最后一个0的位置)
moectf{the_18446744024826406994_amazing_maze!!}

Just-Run-It

包含了四个可执行文件,这个题可以载入静态分析,获取flag,也可以仅仅RUN,获得flag
0x0 与 0x1均加了upx壳
0x0 windows下的x64执行文件,运行后

moectf2024@xdsec ~> cat /flag.0
6257396c5933526d657a55355a6d45

0x1 linux下的x64执行文件,运行后

moectf2024@xdsec ~> cat /flag.1
324d444a6a4c5459794e4745744e44

0x2 Android apk,安装并运行后

moectf2024@xdsec ~> cat /flag.2
42694e7930345954566a4c57557a4e

APK源码

package cn.edu.xidian.myapplication

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import cn.edu.xidian.myapplication.ui.theme.MyApplicationTheme

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    color = MaterialTheme.colorScheme.background
                ) {
                    MyScreenContent()
                }
            }
        }
    }
}

@Composable
fun MyScreenContent() {
    Column(modifier = Modifier.fillMaxSize()) {
        Greeting("Android")
        render()
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello moectf2024!")
}

@Composable
fun render() {
    Column {
        Text(text = "moectf2024@xdsec ~> cat /flag.2")
        Text(text = "42694e7930345954566a4c57557a4e")
    }
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MyApplicationTheme {
        MyScreenContent()
    }
}

0x3 riscv ELF64程序

# Kali Linux 下
sudo apt install qemu-user
chmod +x ./0x3.riscv64.elf
qemu-riscv64 ./0x3.riscv64.elf
# [87, 85, 49, 78, 122, 82, 106, 90, 106, 108, 105, 79, 88, 48, 61]
a = [87, 85, 49, 78, 122, 82, 106, 90, 106, 108, 105, 79, 88, 48, 61]
"".join(map(chr,a))
# WU1NzRjZjliOX0=
6257396c5933526d657a55355a6d45324d444a6a4c5459794e4745744e4442694e7930345954566a4c57557a4e
bW9lY3RmezU5ZmE2MDJjLTYyNGEtNDBiNy04YTVjLWUzN
再加上最后0x3的WU1NzRjZjliOX0=
bW9lY3RmezU5ZmE2MDJjLTYyNGEtNDBiNy04YTVjLWUzNWU1NzRjZjliOX0=
base64 decode
# moectf{59fa602c-624a-40b7-8a5c-e35e574cf9b9}

moectf{59fa602c-624a-40b7-8a5c-e35e574cf9b9}

SecretModule

Magisk Module
Shell Script解密
然后是一点点Android知识
需要知道 getevent 命令

testk() {
  echo "Welcome to the Secret module!But before you begin,you need to prove your self."
  (/system/bin/getevent -lc 1 2>&1 | /system/bin/grep VOLUME | /system/bin/grep " DOWN" > $MODPATH/events) || return 1
  return 0
}   

choose() {
  while true; do
    /system/bin/getevent -lc 1 2>&1 | /system/bin/grep VOLUME | /system/bin/grep " DOWN" > $MODPATH/events
    if (`cat $MODPATH/events 2>/dev/null | /system/bin/grep VOLUME >/dev/null`); then
      break
    fi
  done
  if (`cat $MODPATH/events 2>/dev/null | /system/bin/grep VOLUMEUP >/dev/null`); then
    echo "114514"
  else
    echo "1919810"
  fi
}

if testk; then
  ui_print "Great! Now enter the secret."

else
  ui_print "Legacy Device. Use a newer device to do this challenge"
  exit
fi

concatenated=""

for i in 1 2 3 4 5 6 7
do
  result=$(choose)
  concatenated="${concatenated}${result}"
done

input_str=$(echo -n $concatenated | md5sum | awk '{print $1}')
sec="77a58d62b2c0870132bfe8e8ea3ad7f1"
if test $input_str = $sec
then
    echo 'You are right!Flag is'
    echo "moectf{$concatenated}"
else
    echo 'Wrong. Try again.'
    exit
fi

里面是一个MD5的选择爆破,用Python或者手动猜都可以。
114514114514191981011451411451419198101919810

Cython-Strike: Bomb Defusion

from bomb_defuse import Bomb, DefuseKit

def read_bombmemory(kit):
    processed_data = []
    for address in range(0x0, 0x400):
        try:
            data = kit.read_memory(address)
            if data != '0x0':
                #print(data)
                processed_data.append(data)
        except Exception as e:
            #print(e)
            processed_data.append('*')
    return processed_data

bomb = Bomb()
kit = DefuseKit(bomb)
print(f"Bomb State: {bomb.state}")
print(f"Bomb Mask: {bomb.mask}")
memory_data = read_bombmemory(kit)
code = ''.join(chr(int(data, 16)) if data != '*' else '*' for data in memory_data)

print(code)

password = 3786171  #60578736 >> ((7355608 % 5) + 1)
flag = bomb.enter_pwd(password)
print(f"Bomb has been defused! Counter Terrorists Win! Flag: {flag}")

#Your defusekit has successfully connected to the bomb! The wire are tangled, but you've found the bomb's inst ram!
#Scanning through the inst ram...
#We've got access to some juicy data. Time to defuse!
#tip: use read_memory method to read data from the inst ram
#Bomb State: planted
#Bomb Mask: 7355608
#de*i*e MAX_*** 0xffffff***unsign*d int ma**;***int p*ant_b**b(unsigned int input){if *input <= MAX_***) {ma** = input;r**urn 0;} e*se*{***ret**n -1;} }***void expl*de_b*m***oid)*{***void def**e_**mb(v*id){***}***check_p**d(uns*gned*int in*u*)*{if*(input*> MAX_***)*{***explode_b**b();***}***if*((in**t ^ ma** == 114) && (input << (ma** % 5) + 1 == 60578736))*{defuse_b**b();r**urn**}***explode_b**b();***retu**;***}
#Bomb defused! Flag: moectf{CoUnter_TerR0rists_w1n}

找我们的gpt补一下源码:

#define MAX_VAL 0xffffff
unsigned int mask;

int plant_bomb(unsigned int input) {
    if (input <= MAX_VAL) {
        max = input;
        return 0;
    } else {
        return -1;
    }
}

void explode_bomb(void) {
}

void defuse_bomb(void) {
}

void check_pwd(unsigned int input) {
    if (input > MAX_VAL) {
        explode_bomb();
    }

    if (input ^ mask == 114 && ((input << ((mask % 5) + 1)) == 60578736)) {
        defuse_bomb();
        return;
    }

    explode_bomb();
    return;
}
  • 值得注意的是 input ^ mask == 114 ,其中由于运算符优先级,右边 mask == 114 会被优先计算,很多选手都认为是题目的问题,而且python中,左边反而会被先计算...算是一个坑

moectf{CoUnter_TerR0rists_w1n}

SMCProMax

SMC + 偷偷改东西 + z3使用
先动调,发现smc解密异或0x90,然后就可以patch,然后拖入IDA,可以抄出来一个类似hash的算法,但是这个算法是可以求解其输入的,我们就可以解了。

from z3 import *
from Crypto.Util.number import long_to_bytes as l2b

keys = [2053092702,-490481854,-1704322843,-1418679088,86802781,987171458,965631658,264545711,-1342106783,-825370173]
x = [BitVec(f"x{i}", 32) for i in range(len(keys))]

def hash(v):
    for i in range(32):
        # 由于左移会变偶数,而xor后又会变成奇数,所以有唯一解
        v = If(v < 0 , (v << 1) ^ 0xc4f3b4b3, v << 1)
    return v

s = Solver()

for i in range(len(keys)):
    s.add(hash(x[i]) == keys[i])
s.check()
m = s.model()

result = [0] * len(keys)

for p in m.decls():
    result[int(p.name()[1:])] = m[p].as_long()
for i in result:
    print(l2b(i))

# moectf{y0u_mu5t_know_vvHAt_1s__SMC__n0w}

但是要注意,这是程序在后面判断flag的逻辑
在此之前,程序偷偷修改了H字符,让它异或了0x12,所以正确的flag是

moectf{y0u_mu5t_know_vvZAt_1s__SMC__n0w}
真不容易啊,终于对了!!这就是逆向,豪吃,多吃。
Press any key to continue . . .

moectf{y0u_mu5t_know_vvZAt_1s__SMC__n0w}

ezMAZE-彩蛋

接上文ezMAZE题目,在地图可以找到
moectf{LUOSB}

xor(大嘘)

XOR里面藏了一个TEA,并且有花指令,ida直接看不出来。
EXP

#include <iostream>

void decrypt(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1];
    uint32_t delta = 0x9e3779b9;
    uint32_t sum = delta * 32;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];

    for (int i = 0; i < 32; i++) {
        v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
        v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        sum -= delta;
    }

    v[0] = v0;
    v[1] = v1;
}


int main()
{
    unsigned char ans[] = {
0x2B, 0xF2, 0x82, 0x41, 0x48, 0x74, 0x9D, 0xAA, 0x7E, 0x4C, 0xDA, 0x04, 0x08, 0x2C, 0xA8, 0x52,
 0x97, 0x77, 0xB7, 0x3B, 0x16, 0x2D, 0xD4, 0xFC, 0x60, 0xBE, 0xC4, 0xB6, 0x73, 0x19, 0x94, 0x87
    };
    unsigned char axxxx[] = {
0x3C, 0x0D, 0x05, 0x1F, 0x30, 0x6E, 0x1E, 0x30, 0x04, 0x3C, 0x12, 0x52, 0x59, 0x03, 0x6D, 0x52,
 0x04, 0x04, 0x0B, 0x33, 0x1F, 0x33, 0x17, 0x3B, 0x17, 0x1A, 0x2B, 0x07, 0x55, 0x04, 0x5B, 0x5A
    };
    for (int i = 0; i < 32; i++)
    {
        axxxx[i] ^= ans[i];
    }

    const char* xxx = "hello_moectf2024";

    uint32_t* v = (uint32_t*)axxxx;
    uint32_t* k = (uint32_t*)xxx;
    decrypt(v, k);
    decrypt(v+2, k);
    decrypt(v+4, k);
    decrypt(v+6, k);

    unsigned char* vv = (unsigned char*)v;
    for (int i = 0; i < 32; i++)
    {
        vv[i] ^= xxx[i % 16];
        putchar(vv[i]);
        // moectf{how_an_easy_junk_and_tea}
    }
}

moectf{how_an_easy_junk_and_tea}

babe-z3

src

#include <iostream>
#include <Windows.h>
int main()
{
        puts("$ root@moectf2024 ~> Welcome to moectf 2024!\n | 0xcafebabe encountered a difficult challenge!!!\n | He was lost in the z3-solver's WORLD, Please Use Your MAGIC to help him.\n | input: moectf{YOUR_INPUT}");
        char x[36]; //9c0525dcbadf4cbd9715067159453e74
        fgets(x, 36, stdin);
        uint64_t a1 = *(0 + (uint64_t*)x);
        uint64_t a2 = *(1 + (uint64_t*)x);
        uint64_t a3 = *(2 + (uint64_t*)x);
        uint64_t a4 = *(3 + (uint64_t*)x);
/*

        std::cout << (a1 & a2 & (~(a1 | a2) | a3 & a2 | a4 & a1 & ~a4) | a4 & a1 & a3) << std::endl;
        std::cout << ((a2 ^ (a4 & ~a1 | a4 & ~a3 | (a1 + a3) & a3 | (a2 + a4) & a2 & ~a1))) << std::endl;
        std::cout << ((a1 - a2) ^ (a3 - a4)) << std::endl;
        std::cout << ((a4 + a2) ^ (a1 + a3)) << std::endl;
        std::cout << (a1 ^ a2 ^ a3 ^ a4) << std::endl;
        std::cout << (a1 & a2 & a3 & a4) << std::endl;
        std::cout << (a1 + a2 - a3 + a4) * (a1 + a3 - a4 + a2) << std::endl;
        std::cout << (a1 + a2 +a3 + a4) % 114514 << std::endl;
        std::cout << (a1 * a2 * a3 * a4) % 1919810 << std::endl;

*/
        uint64_t switch_0 = 0;
        uint64_t switch_1 = 0;
        uint64_t switch_2 = 0;
        uint64_t switch_3 = 0;
        uint64_t switch_4 = 0;
        uint64_t switch_5 = 0;
        uint64_t switch_6 = 0;
        uint64_t switch_7 = 0;
        uint64_t switch_8 = 0;
        
        // FAKE CONDITION
        if ((a3 & a1 & (~(a4 + a2) | a2 & a2 | a4 & a1 & ~a4) ^ a4 & a3) == 0xcafebabedeadc0de) (switch_0 |= 0x0000000010000000);

        if ((a1 & a2 & (~(a1 | a2) | a3 & a2 | a4 & a1 & ~a4) | a4 & a1 & a3) == 2316015897844654385) switch_0 = (switch_0 |= 0x0000000100000000);
        if(((a2 ^ (a4 & ~a1 | a4 & ~a3 | (a1 + a3) & a3 | (a2 + a4) & a2 & ~a1))) == 8102257287365753684) switch_1 = 1;
        if(((a1 - a2) ^ (a3 - a4)) == 287668530830180307) switch_2 = 1;
        if(((a4 + a2) ^ (a1 + a3)) == 865433324338348261) switch_3 = 1;
        if((a1 ^ a2 ^ a3 ^ a4) == 145809558366915671) switch_4 = 1;
        if((a1 & a2 & a3 & a4) == 2314885599605039392) switch_5 = 1;
        if(((a1 + a2 - a3 + a4) * (a1 + a3 - a4 + a2)) == 1840182356754417097) switch_6 = 1;
        if((a1 + a2 + a3 + a4) % 114514 == 21761) switch_7 = 1;
        if((a1 * a2 * a3 * a4) % 1919810 == 827118) switch_8 = 1;

        DWORD fake = (DWORD)switch_0;
        if (switch_0 && switch_1 && switch_2 && switch_3 && switch_4 && switch_5 && switch_6 && switch_7 && switch_8 && !fake)
        {
                printf("Finally, You Win!!!");
        }
        else
        {
                printf("NO!! Try Again. PLEASE");
        }

}

这题主要的坑其实是一个64位的变量的高32位和低32位控制了两个不同的条件,最后检查的时候,分别进行了检查,致敬RCTF2024的 PPTT(同样的操作,当时被恶心了)
Exp

from z3 import *
from Crypto.Util.number import *

x = [BitVec(f"x{i}", 64) for i in range(4)]
s = Solver()

a1 = x[0]
a2 = x[1]
a3 = x[2]
a4 = x[3]

# This Condition Is Fake!(!=)
s.add((a3 & a1 & (~(a4 + a2) | a2 & a2 | a4 & a1 & ~a4) ^ a4 & a3) != 0xd81ac01fbba91837)

# These Are True.
s.add((a1 & a2 & (~(a1 | a2) | a3 & a2 | a4 & a1 & ~a4) | a4 & a1 & a3) == 2316015897844654385)
s.add(((a2 ^ (a4 & ~a1 | a4 & ~a3 | (a1 + a3) & a3 | (a2 + a4) & a2 & ~a1))) == 8102257287365753684)
s.add(((a1 - a2) ^ (a3 - a4)) == 287668530830180307)
s.add(((a1 + a2 - a3 + a4) * (a1 + a3 - a4 + a2)) == 1840182356754417097)
s.add((a1 + a2 + a3 + a4) % 114514 == 21761)
s.add((a1 * a2 * a3 * a4) % 1919810 == 827118)

print(s.check())
m = s.model()
result = b""
for i in x:
    result += long_to_bytes(m[i].as_long())[::-1]
print(result)
# b'9c0525dcbadf4cbd9715067159453e74'

moectf{9c0525dcbadf4cbd9715067159453e74}

BlackHole

根据纸条内容,我们采用LoadLibrary然后调用函数进行暴力破解即可。
这是源码

extern "C" {
        __declspec(dllexport) int checkMyFlag(char* your_flag, size_t length)
        {
                if (length != 15)
                {
                        return 0;
                }
                if (memcmp(your_flag, "moectf{", 7)) return 0;
                if (your_flag[14] != '}') return 0;
                if (your_flag[7] == 'c' &&
                        your_flag[8] == 'r' &&
                        your_flag[9] == '4' &&
                        your_flag[10] == 'c' &&
                        your_flag[11] == 'k' &&
                        your_flag[12] == 'm' &&
                        your_flag[13] == '3')
                {
                        return 1;
                }

                return 0;
        }
}

EXP

#include <iostream>
#include <Windows.h>


void bruteForce()
{
        HMODULE h = NULL;
        h = LoadLibraryA("you_cannot_crack_me.vmp.dll");
        if (!h)
        {
                printf("failed to load dll...\n");
                return;
        }
        typedef int(*checkFlag)(char*, size_t);
        char* cracking = (char*)malloc(16);
        if (!cracking) return;
        cracking[0] = 'm';
        cracking[1] = 'o';
        cracking[2] = 'e';
        cracking[3] = 'c';
        cracking[4] = 't';
        cracking[5] = 'f';
        cracking[6] = '{';

        cracking[7] = 'c';
        cracking[12] = 'm';

        cracking[14] = '}';
        cracking[15] = '\0';
        checkFlag check = (checkFlag)GetProcAddress(h, "checkMyFlag");
        for (char b = 'a'; b <= 'z'; b++)
        {
                for (char c = '0'; c <= '9'; c++)
                {
                        for (char d = 'a'; d <= 'z'; d++)
                        {
                                for (char e = 'a'; e <= 'z'; e++)
                                {
                                        for (char g = '0'; g <= '9'; g++)
                                        {
                                                cracking[8] = b;
                                                cracking[9] = c;
                                                cracking[10] = d;
                                                cracking[11] = e;
                                                cracking[13] = g;
                                                if (check(cracking, 15))
                                                {
                                                        printf("found answer: %s\n", cracking);
                                                        return;
                                                }
                                        }
                                }
                        }
                }
        }


        CloseHandle(h);
}

int main()
{
        bruteForce();
        system("pause");
        return 0;
}

moectf{cr4ckm3}

moeprotector

考点SEH, 反调试
解法:挂上x64dbg+ScyllaHide(不要开TitanHide,有检测),然后通过软件断点来不断追踪flag变化。
容易得到下面的伪代码

flag[0]^=0x31;
flag[0]+=0x31;
flag[0]-=0x31;
flag[0]^=0x31;
for(inti=0;i<57;i++)
{
        flag[i]^=21+i;
        flag[i]+=20;
}
for(inti=0;i<57;i++)
{
        flag[i]^=26+i;
        flag[i]+=20;
}
for(inti=0;i<57;i++)
{
        flag[i]^=25+i;
        flag[i]+=20;
}
for(inti=0;i<57;i++)
{
        if(hexData[i]!=flag[i])
                die();
}

找到hexData的值,写解密脚本就行了。
moectf{w1Nd0Ws_S3H_15_A_g0oD_m37h0d_70_h4nd13_EXCEPTI0NS}

特工luo: 闻风而动

1.考察易语言程序的逆向分析,Keygen就不说了,一个简单的异或转换工具,cli在连接后会收到服务器发来的DES加密后flag,加密密码是WIFI密码走keygen,而客户端保存的时候再会走一次RC4加密,密码是 flag.fromserver 对没错 密码就是文件名
2.考察无线安全中对于无线握手包的密码破解。选手需要将cap文件转换为可以供hashcat读取的哈希,并使用hashcat结合题目提示来进行暴力破解(cap文件分析发现是WAP2类型,所以哈希类型是22000)跑出来就可以写解密脚本了(用易语言写最好)
3.值得注意的是,在爆破的时候,需要对密码keygen进行分析,可以从异或数字的大小来观察是数字还是字符,然后反异或过来,就可以缩减密码文本,从而降低爆破时间。
解密

输出调试文本(到文本(解密数据(解密数据(读取文件("flag.fromserver"), "flag.fromserver", #RC4加密), "4g3n71u0", #DES加密)))

moectf{h4cker_Lu0Q1@n_is_trying_to_0p3n_your_r3g15t3r3d_res1denc3}

特工luo: 深入敌营

简单VM题目,拿去年 NCTF ezVM 的题改的简单版本,去掉了trash code,用z3-solver即可化简、求解。属于本比赛最难的逆向工程题目。
首先UPX -d 脱一下壳,放入ida分析一下

int __fastcall main(int argc, const char **argv, const char **envp)
{
  unsigned __int8 saver; // r15
  __int64 _code; // rdi
  __int64 _rsp; // rax
  __int64 _rip; // rcx
  unsigned __int8 v7; // cl
  __int16 v8; // cx
  int v9; // ecx
  __int64 v10; // rcx
  int v11; // ebp
  unsigned __int64 v12; // rbx
  unsigned __int8 v13; // r14
  rsize_t v14; // rdx
  __int64 v15; // rax
  __int64 v16; // rdx
  __int64 v17; // rcx

  saver = 0;
  LODWORD(_code) = 0;
LABEL_2:
  _rsp = (unsigned int)save;
  while ( 1 )
  {
    _rip = (unsigned int)_code;
    _code = (unsigned int)(_code + 1);
    switch ( insn[_rip] )
    {
      case 0xBu:                                // v_add_rsp_8
        _rsp = (unsigned int)(_rsp + 8);
        save = _rsp;
        continue;
      case 0xCu:
        _rsp = (unsigned int)(_rsp - 2);        // v_push_word
        save = _rsp;
        stack[_rsp + 1] = stack[(unsigned int)(_rsp + 2) + 1];
        stack[(unsigned int)(_rsp + 1) + 1] = stack[(unsigned int)(_rsp + 3) + 1];
        continue;
      case 0xEu:                                // v_notSelf
        stack[(unsigned int)(_rsp + 1) + 1] = ~stack[(unsigned int)(_rsp + 1) + 1];
        continue;
      case 0x14u:                               // v_and
        v16 = (unsigned int)(_rsp + 1);
        stack[v16 + 1] &= stack[_rsp + 1];
        _rsp = (unsigned int)v16;
        save = v16;
        continue;
      case 0x19u:                               // v_exit
        return *(_DWORD *)&insn[_code];
      case 0x32u:
        v7 = insn[_code];                       // v_push_byte
        _rsp = (unsigned int)(_rsp - 1);
        save = _rsp;
        LODWORD(_code) = _code + 1;
        stack[_rsp + 1] = v7;
        continue;
      case 0x49u:                               // v_dup
        _rsp = (unsigned int)(_rsp - 1);
        save = _rsp;
        stack[(unsigned int)_rsp + 1] = stack[(unsigned int)(_rsp + 1) + 1];
        continue;
      case 0x72u:                               // v_not
        stack[_rsp + 1] = ~stack[_rsp + 1];
        continue;
      case 0x7Bu:                               // v_read_chr
        v14 = (char)stack[_rsp + 1];
        v15 = (unsigned int)(_rsp - 7);
        save = v15;
        *(_QWORD *)&stack[v15 + 1] = Buffer;
        gets_s(Buffer, v14);
        goto LABEL_2;
      case 0x7Cu:
        saver = stack[_rsp + 1];                // v_save
        goto LABEL_9;
      case 0x8Du:
        if ( !stack[_rsp + 1] )
          LODWORD(_code) = *(_DWORD *)&stack[_rsp + 2];
        _rsp = (unsigned int)(_rsp + 5);
        save = _rsp;
        continue;
      case 0x8Eu:                               // v_print
        v11 = insn[_code];
        v12 = 0LL;
        _code = (unsigned int)(_code + 1);
        v13 = insn[_code] ^ v11;
        LODWORD(_code) = _code + 1;
        if ( v13 )
        {
          do
          {
            putchar(v11 ^ stack[v13 + (unsigned int)_rsp - v12]);
            LODWORD(_rsp) = save;
            ++v12;
          }
          while ( v12 < v13 );
        }
        save = v13 + (_DWORD)_rsp;
        putchar(10);
        goto LABEL_2;
      case 0x91u:                               // v_push_dword
        v9 = *(_DWORD *)&insn[_code];
        _rsp = (unsigned int)(_rsp - 4);
        save = _rsp;
        LODWORD(_code) = _code + 4;
        *(_DWORD *)&stack[_rsp + 1] = v9;
        continue;
      case 0x99u:
        _rsp = (unsigned int)(_rsp + 4);        // v_add_rsp_4
        save = _rsp;
        continue;
      case 0xADu:
        _rsp = (unsigned int)(_rsp - 1);        // v_push_saver
        save = _rsp;
        stack[(unsigned int)_rsp + 1] = saver;
        continue;
      case 0xB5u:
        _rsp = (unsigned int)(_rsp + 2);        // v_add_rsp_2
        save = _rsp;
        continue;
      case 0xB7u:
        v8 = *(_WORD *)&insn[_code];            // v_push_word
        _rsp = (unsigned int)(_rsp - 2);
        save = _rsp;
        LODWORD(_code) = _code + 2;
        *(_WORD *)&stack[_rsp + 1] = v8;
        continue;
      case 0xB8u:
        stack[_rsp + 1] = *(_BYTE *)(stack[_rsp + 1] + *(_QWORD *)&stack[_rsp + 2]);// v_add
        continue;
      case 0xD3u:
LABEL_9:
        _rsp = (unsigned int)(_rsp + 1);        // v_add_rsp_1
        save = _rsp;
        break;
      case 0xEAu:
        v10 = *(_QWORD *)&insn[_code];          // v_push_qword
        _rsp = (unsigned int)(_rsp - 8);
        save = _rsp;
        LODWORD(_code) = _code + 8;
        *(_QWORD *)&stack[_rsp + 1] = v10;
        break;
      case 0xFBu:
        if ( stack[_rsp + 1] )                  // v_not_not
          stack[_rsp + 1] = 1;
        break;
      case 0xFFu:
        v17 = stack[_rsp + 1];                  // v_not
        if ( (_BYTE)v17 )
        {
          if ( (_BYTE)v17 == 1 )
          {
            if ( (unsigned int)_rsp >= 0x100uLL )
              _report_rangecheckfailure(v17, (unsigned int)_rsp, envp);
            stack[_rsp + 1] = 0;
          }
        }
        else
        {
          stack[_rsp + 1] = 1;
        }
        break;
      default:
        continue;
    }
  }
}

可以看到是一个标准化的VM,我们可以写出z3-solver解题脚本。
moectf{vv1ii?AGENT+1U0_-*7_$[TH/./e]+$_FinAL_v!c70Ry?}

ez_rust

x64dbg开调,搜that's,找到一块
spatchEvent(new Event("input")))}const ln=Symbol("_assign"),zi={created(e,{modifiers:{lazy:t,trim:n,number:s}},r){e[ln]=ws(r);const o=s||r.props&&r.props.type==="number";We(e,t?"change":"input",i=>{if(i.target.composing)return;let c=e.value;n&&(c=c.trim()),o&&(c=cn(c)),e[ln](c)}),n&&We(e,"change",()=>{e.value=e.value.trim()}),t||(We(e,"compositionstart",qi),We(e,"compositionend",Cs),We(e,"change",Cs))},mounted(e,{value:t}){e.value=t??""},beforeUpdate(e,{value:t,modifiers:{lazy:n,trim:s,number:r}},o){if(e[ln]=ws(o),e.composing)return;const i=r||e.type==="number"?cn(e.value):e.value,c=t??"";i!==c&&(document.activeElement===e&&e.type!=="range"&&(n||s&&e.value.trim()===c)||(e.value=c))}},Ji=["ctrl","shift","alt","meta"],Yi={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Ji.some(n=>e[`${n}Key`]&&!t.includes(n))},Qi=(e,t)=>{const n=e._withMods||(e._withMods={}),s=t.join(".");return n[s]||(n[s]=(r,...o)=>{for(let i=0;i<t.length;i++){const c=Yi[t[i]];if(c&&c(r,t))return}return e(r,...o)})},Xi=Y({patchProp:Wi},Ii);let Os;function Zi(){return Os||(Os=ri(Xi))}const ki=(...e)=>{const t=Zi().createApp(...e),{mount:n}=t;return t.mount=s=>{const r=tl(s);if(!r)return;const o=t._component;!A(o)&&!o.render&&!o.template&&(o.template=r.innerHTML),r.innerHTML="";const i=n(r,!1,el(r));return r instanceof Element&&(r.removeAttribute("v-cloak"),r.setAttribute("data-v-app","")),i},t};function el(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function tl(e){return z(e)?document.querySelector(e):e}const nl=ve("button",{type:"submit"},"go!",-1),sl={__name:"Greet",setup(e){const t=ts(""),n=ts("");function s(o,i="secret"){for(var c="",u=i.length,d=0;d<o.length;d++){var h=o.charCodeAt(d),x=i.charCodeAt(d%u),w=h^x;c+=String.fromCharCode(w)}return c}async function r(){if(n.value===""){t.value="Please enter a name.";return}btoa(s(n.value))==="JFYvMVU5QDoNQjomJlBULSQaCihTAFY="?t.value="Great, you got the flag!":t.value="No, that's not my name."}return(o,i)=>(mr(),br(ge,null,[ve("form",{class:"row",onSubmit:Qi(r,["prevent"])},[Mo(ve("input",{id:"greet-input","onUpdate:modelValue":i[0]||(i[0]=c=>n.value=c),placeholder:"Enter a name..."},null,512),[[zi,n.value]]),nl],32),ve("p",null,Nr(t.value),1)],64))}},rl=(e,t)=>{const n=e.__vccOpts||e;for(const[s,r]of t)n[s]=r;return n},Kn=e=>(bo("data-v-bacdabd6"),e=e(),yo(),e),ol={class:"container"},il=Kn(()=>ve("h1",null,"Welcome to L3HCTF!",-1)),ll=Kn(()=>ve("p",null,"Hope you have a good time playing.",-1)),cl=Kn(()=>ve("p",null,"Now please tell me a name.",-1)),fl={__name:"App",setup(e){return(t,n)=>(mr(),br("div",ol,[il,ll,cl,Ae(sl)]))}},ul=rl(fl,[["__scopeId","data-v-bacdabd6"]]);ki(ul).mount("#app");
分析可知,"JFYvMVU5QDoNQjomJlBULSQaCihTAFY=" == base64_encode(bxor_cycle(input,b'secret'))
L3HCTF{W3LC0M3_n0_RU57_AnyM0r3}

babycom

x64dbg附加,注意到中途主程序会往外写一个com(从资源里面读取)。
注意到是babycom.dll,我们直接断输入的字符串,发现进去后是一个xtea
xtea参数:
key=EA 3E D4 1C 70 CB D7 47 98 5E CA DB 53 0C 39 2B
delta=0x114514
round=32
然后经过对比发现是一个标准的。
2024-09-25T09:52:54.png
继续往下分析,xtea之后还有一个加密。
用了一个Bcrypt的加密,调API就可以解密。

#include <iostream>
#include <Windows.h>
int main()
{
    HCRYPTPROV phProv;
    HCRYPTHASH phHash;
    HCRYPTKEY phKey;
    const BYTE key[] = {
        0xEA, 0x3E, 0xD4, 0x1C, 0x70, 0xCB, 0xD7, 0x47, 0x98, 0x5E, 0xCA, 0xDB, 0x53, 0x0C, 0x39, 0x2B
    };

    const BYTE MultiByteStr[] = {0xb, 0xaf, 0x51, 0x21, 0x9c, 0x52, 0x10, 0x89,
                    0x3f, 0x2c, 0x34, 0x30, 0x87, 0x13, 0xc1, 0x4c,
                    0xc1, 0x7f, 0x81, 0x6e, 0xba, 0xbd, 0xdf, 0x43,
                    0x1a, 0xf0, 0xd7, 0xde, 0x8e, 0x66, 0xb9, 0x7c };
    DWORD pdwDataLen = 32;
    if (CryptAcquireContextA(&phProv, 0LL, 0LL, 0x18u, 0xF0000000)
        && CryptCreateHash(phProv, 32771u, 0LL, 0, &phHash)
        && CryptHashData(phHash, key, 0x10u, 0)
        && CryptDeriveKey(phProv, 0x660Eu, phHash, 1u, &phKey))
    {
        CryptDecrypt(phKey, 0LL, 0, 0, (BYTE*)MultiByteStr, &pdwDataLen);
    }
    return 0;
/* Decrypted
2a b4 c1 74 d6 59 aa 05 73 10 7f 9c 40 49 99 62
3c 84 51 8f 3f 37 ab f1 0e fe 61 96 45 ad 41 6a
*/
}

综上所述

from regadgets import *
encc = pack_dword(byte2dword(bytes.fromhex("""
2a b4 c1 74 d6 59 aa 05 73 10 7f 9c 40 49 99 62
3c 84 51 8f 3f 37 ab f1 0e fe 61 96 45 ad 41 6a
""")))
key = byte2dword(b"\xEA\x3E\xD4\x1C\x70\xCB\xD7\x47\x98\x5E\xCA\xDB\x53\x0C\x39\x2B")
r = b''
for i in encc:
    result = xtea_decrypt(i, key, delta=0x114514, rounds=32)
    r += dword2byte(list(result))
print(r)
# L3HCTF{C0M_Th3C0d3_1s_FuN!!!!!!}

丢一个Ctypes版本的(纯python解决)

from regadgets import *
from ctypes import *
lib_advapi32 = windll.LoadLibrary('advapi32.dll')
pfunc_CryptAcquireContextA = lib_advapi32.CryptAcquireContextA
pfunc_CryptCreateHash = lib_advapi32.CryptCreateHash
pfunc_CryptHashData = lib_advapi32.CryptHashData
pfunc_CryptDeriveKey = lib_advapi32.CryptDeriveKey
pfunc_CryptDecrypt = lib_advapi32.CryptDecrypt
phProv = c_uint64(0)
phHash = c_uint64(0)
phKey = c_uint64(0)
pdwDataLen = c_uint32(32)
if pfunc_CryptAcquireContextA(pointer(phProv), 0, 0, 0x18, 0xF0000000) == 0:
    print("failed 0")
    exit(0)
if pfunc_CryptCreateHash(phProv, 32771, 0, 0, pointer(phHash)) == 0:
    print("failed 1")
    exit(0)
key = b"\xEA\x3E\xD4\x1C\x70\xCB\xD7\x47\x98\x5E\xCA\xDB\x53\x0C\x39\x2B"
if pfunc_CryptHashData(phHash, create_string_buffer(key), 0x10, 0) == 0:
    print("failed 2")
    exit(0)
if pfunc_CryptDeriveKey(phProv, 0x660E, phHash, 1, pointer(phKey)) == 0:
    print("failed 3")
    exit(0)

correct = b"\x0B\xAF\x51\x21\x9C\x52\x10\x89\x3F\x2C\x34\x30\x87\x13\xC1\x4C\xC1\x7F\x81\x6E\xBA\xBD\xDF\x43\x1A\xF0\xD7\xDE\x8E\x66\xB9\x7C"
buf = create_string_buffer(correct)
pfunc_CryptDecrypt(phKey, 0, 0, 0, buf, pointer(pdwDataLen))

encc = pack_dword(byte2dword(buf.raw), padding=True)
r = b''
for i in encc:
    result = xtea_decrypt(i, byte2dword(key), delta=0x114514, rounds=32)
    r += dword2byte(list(result))
print(r)

# b'L3HCTF{C0M_Th3C0d3_1s_FuN!!!!!!}\xa0\xbf\xbc\xcf>\xb1\x194'

迷宫

逆天题目,答案直接给你了。

FindTheKey

.so的JNI_OnLoad中,最后调用了sub_B004(前面都是frida检测),疑似是某个加固软件的,见了很多次。

int __fastcall sub_B004(JNIEnv *a1)
{
  jobject v2; // [sp+38h] [bp-E8h]
  jobject v3; // [sp+3Ch] [bp-E4h]
  jsize j; // [sp+40h] [bp-E0h]
  jobject ObjectArrayElement; // [sp+44h] [bp-DCh]
  jsize i; // [sp+48h] [bp-D8h]
  jobject v7; // [sp+4Ch] [bp-D4h]
  struct _jmethodID *v8; // [sp+50h] [bp-D0h]
  struct _jmethodID *v9; // [sp+54h] [bp-CCh]
  struct _jmethodID *v10; // [sp+58h] [bp-C8h]
  jclass v11; // [sp+5Ch] [bp-C4h]
  jobject v12; // [sp+60h] [bp-C0h]
  jobject v13; // [sp+64h] [bp-BCh]
  jobject v14; // [sp+68h] [bp-B8h]
  jobject v15; // [sp+6Ch] [bp-B4h]
  jobject v16; // [sp+70h] [bp-B0h]
  struct _jmethodID *v17; // [sp+74h] [bp-ACh]
  jclass v18; // [sp+78h] [bp-A8h]
  struct _jfieldID *v19; // [sp+7Ch] [bp-A4h]
  jclass v20; // [sp+80h] [bp-A0h]
  struct _jfieldID *v21; // [sp+84h] [bp-9Ch]
  jclass v22; // [sp+88h] [bp-98h]
  jobject clazz_loader; // [sp+8Ch] [bp-94h]
  struct _jmethodID *v24; // [sp+90h] [bp-90h]
  jclass v25; // [sp+94h] [bp-8Ch]
  jclass main_activity; // [sp+98h] [bp-88h]
  struct _jmethodID *v27; // [sp+9Ch] [bp-84h]
  struct _jmethodID *v28; // [sp+A0h] [bp-80h]
  void *v29; // [sp+A4h] [bp-7Ch]
  jmethodID StaticMethodID; // [sp+A8h] [bp-78h]
  jclass v31; // [sp+ACh] [bp-74h]
  void *ptr; // [sp+B4h] [bp-6Ch]
  void *v33; // [sp+B8h] [bp-68h]
  jint v34; // [sp+BCh] [bp-64h]
  struct _jmethodID *read__; // [sp+C0h] [bp-60h]
  jclass v36; // [sp+C4h] [bp-5Ch]
  jbyteArray _array_; // [sp+C8h] [bp-58h]
  jobject v38; // [sp+CCh] [bp-54h]
  struct _jmethodID *v39; // [sp+D0h] [bp-50h]
  size_t size; // [sp+D4h] [bp-4Ch]
  struct _jmethodID *v41; // [sp+D8h] [bp-48h]
  jclass v42; // [sp+DCh] [bp-44h]
  jobject v43; // [sp+E0h] [bp-40h]
  jstring v44; // [sp+E4h] [bp-3Ch]
  struct _jmethodID *v45; // [sp+ECh] [bp-34h]
  jclass v46; // [sp+F0h] [bp-30h]
  jobject v47; // [sp+F4h] [bp-2Ch]
  struct _jmethodID *MethodID; // [sp+F8h] [bp-28h]
  jclass v49; // [sp+FCh] [bp-24h]
  jobject ObjectField; // [sp+100h] [bp-20h]
  jobject StaticObjectField; // [sp+104h] [bp-1Ch]
  struct _jfieldID *FieldID; // [sp+108h] [bp-18h]
  struct _jfieldID *StaticFieldID; // [sp+10Ch] [bp-14h]
  jclass Class; // [sp+110h] [bp-10h]

  Class = FindClass(a1, asc_1F0A0);             // android/app/ActivityThread
  StaticFieldID = GetStaticFieldID(a1, Class, aWgqvvajpegpmrm, aDiflzgalIxxIkA);
  FieldID = GetFieldID(a1, Class, byte_1F280, byte_1F2A0);
  StaticObjectField = GetStaticObjectField(a1, Class, StaticFieldID);
  ObjectField = GetObjectField(a1, StaticObjectField, FieldID);
  v49 = FindClass(a1, aTHus5Jj5JjvsyN);         // android/app/Application
  MethodID = GetMethodID(a1, v49, byte_1F2D8, byte_1F2F0);
  v47 = CallObjectMethodV(a1, ObjectField, MethodID);
  v46 = FindClass(a1, byte_1F320);              // android/content/res/AssetManager
  v45 = GetMethodID(a1, v46, byte_1F341, byte_1F350);
  v44 = NewStringUTF(a1, aOKzLg);               // asset.bin
  v43 = CallObjectMethodV(a1, v47, v45, v44);
  v42 = FindClass(a1, aFicuhncDhisbis);         // android/content/res/AssetFileDescriptor
  v41 = GetMethodID(a1, v42, byte_1F3C8, byte_1F3D2);// getLength
  size = CallLongMethodV(a1, v43, v41);
  v39 = GetMethodID(a1, v46, "7(=6X", byte_1F3E0);// open
  v38 = CallObjectMethodV(a1, v47, v39, v44);
  _array_ = NewByteArray(a1, size);
  v36 = FindClass(a1, byte_1F410);              // java/io/InputStream
  read__ = GetMethodID(a1, v36, aRead, byte_1F429);// read ([BII)I
  v34 = CallIntMethodV(a1, v38, read__, _array_, 0, size);
  v33 = malloc(size);
  if ( v34 >= 1 )
    GetByteArrayRegion(a1, (int)_array_, 0, size, (int)v33);
  ptr = malloc(size);
  sub_AC38(unk_1F431, v33, size, ptr);          // RC4(goodluck)
  SetByteArrayRegion(a1, (int)_array_, 0, size, (int)ptr);
  if ( ptr )
    free(ptr);
  if ( v33 )
    free(v33);
  v31 = FindClass(a1, byte_1F440);
  StaticMethodID = GetStaticMethodID(a1, v31, byte_1F454, byte_1F460);
  v29 = (void *)sub_A234(a1, v31, StaticMethodID, size);
  v28 = GetMethodID(a1, v31, byte_1F479, byte_1F480);// put ([B)Ljava/nio/ByteBuffer;
  v27 = GetMethodID(a1, v31, byte_1F49A, byte_1F4B0);// position (I)Ljava/nio/Buffer;
  CallObjectMethodV(a1, v29, v28, _array_);
  CallObjectMethodV(a1, v29, v27, 0);
  main_activity = FindClass(a1, byte_1F4D0);    // com/ctf/findthekey/MainActivity
  v25 = FindClass(a1, a388v587V);               // java/lang/Class
                                                // 
  v24 = GetMethodID(a1, v25, byte_1F500, byte_1F510);// getClassLoader
  clazz_loader = CallObjectMethodV(a1, main_activity, v24);
  v22 = FindClass(a1, byte_1F530);
  v21 = GetFieldID(a1, v22, byte_1F54E, aVVlsq5icin);// pathList Ldalvik/system/DexPathList;
  v20 = FindClass(a1, byte_1F580);              // dalvik/system/DexPathList
  v19 = GetFieldID(a1, v20, ":;&\x1B2;3;0*-^", byte_1F5B0);// dexElements [Ldalvik/system/DexPathList$Element;
  v18 = FindClass(a1, byte_1F5E0);              // dalvik/system/InMemoryDexClassLoader
  v17 = GetMethodID(a1, v18, "k>9>#iW", byte_1F610);
  v16 = NewObjectV(a1, v18, v17, v29, clazz_loader);
  v15 = GetObjectField(a1, v16, v21);
  v14 = GetObjectField(a1, v15, v19);
  v13 = GetObjectField(a1, clazz_loader, v21);
  v12 = GetObjectField(a1, v13, v19);
  v11 = FindClass(a1, byte_1F640);              // java/util/ArrayList
  v10 = GetMethodID(a1, v11, aJoo, byte_1F660);
  v9 = GetMethodID(a1, v11, aCxveevn7, byte_1F680);
  v8 = GetMethodID(a1, v11, "k>9>#iW", byte_1F696);
  v7 = NewObjectV(a1, v11, v8);
  for ( i = 0; i < GetArrayLength(a1, v14); ++i )
  {
    ObjectArrayElement = GetObjectArrayElement(a1, v14, i);
    CallBooleanMethodV(a1, v7, v10, ObjectArrayElement);
  }
  for ( j = 0; j < GetArrayLength(a1, v12); ++j )
  {
    v3 = GetObjectArrayElement(a1, v12, j);
    CallBooleanMethodV(a1, v7, v10, v3);
  }
  v2 = CallObjectMethodV(a1, v7, v9);
  return SetObjectField(a1, (int)v13, (int)v19, (int)v2);
}

显然是读取了Assets的asset.bin,然后走rc4解密

int __fastcall sub_AC38(const char *k, char *key, unsigned int len, char *buf)
{
  char S[256]; // [sp+34h] [bp-114h] BYREF

  rc4_init(k, S);
  rc4_crypt(S, key, buf, len);
  return 0;
}
int __fastcall rc4_init(const char *k, const char *S)
{
  int j; // [sp+18h] [bp-20h]
  int i; // [sp+1Ch] [bp-1Ch]
  int v5; // [sp+20h] [bp-18h]
  signed int v6; // [sp+24h] [bp-14h]

  v6 = strlen(k);
  v5 = 0;
  for ( i = 0; i <= 255; ++i )
    S[i] = i;
  for ( j = 0; j <= 255; ++j )
  {
    v5 = ((unsigned __int8)k[j % v6] + v5 + (unsigned __int8)S[j]) % 256;
    swap(&S[j], &S[v5]);
  }
  return 0;
}
int __fastcall rc4_crypt(char *S, char *a2, char *dest, unsigned int len)
{
  unsigned int i; // [sp+1Ch] [bp-24h]
  int v6; // [sp+20h] [bp-20h]
  int v7; // [sp+24h] [bp-1Ch]

  v7 = 0;
  v6 = 0;
  for ( i = 0; i < len; ++i )
  {
    v7 = (v7 + 1) % 256;
    v6 = (v6 + (unsigned __int8)S[v7]) % 256;
    swap(&S[v7], &S[v6]);
    dest[i] = S[(unsigned __int8)(S[v7] + S[v6])] ^ a2[i];
  }
  return 0;
}
int __fastcall swap(_BYTE *a1, _BYTE *a2)
{
  int result; // r0
  int v3; // [sp+8h] [bp-Ch]

  v3 = (unsigned __int8)*a1;
  *a1 = *a2;
  result = v3;
  *a2 = v3;
  return result;
}

然后解密了asset.bin就可以直接拖入jadx了
2024-09-18T09:07:20.png
覆盖Util.java,换表base64+rc4,就可以CyberChef解了。

picStore(re)

https://impartial-poinsettia-a05.notion.site/Lua-953ca64d37c9478b82d394ea2f2a0ca1#ce05742c1bd04285ba87bf4d398a861c

dump 后 用 https://luadec.metaworm.site/在线反编译

-- filename: 
-- version: lua53
-- line: [0, 0] id: 0
menu = function()
  -- line: [2, 12] id: 1
  print("-------------------Pictrue Store System-------------------")
  print("1. upload")
  print("2. download")
  print("3. delete")
  print("4. list")
  print("5. check")
  print("6. exit")
  io.write("choice>> ")
end
upload_impl = function()
  -- line: [14, 21] id: 2
  local r0_2 = a_f3_9a7nhRC()
  if r0_2 ~= nil then
    io.write("img data: ")
    a_1sV7zC5yL_(r0_2)
  end
end
download_impl = function()
  -- line: [23, 31] id: 3
  io.write("link: ")
  local r0_3 = io.read("*number")
  if a_IjKn_GF3FE(r0_3) == 1 then
    io.write("img data: ")
    a_TUBSK2FAhN(r0_3)
  end
end
delete_impl = function()
  -- line: [33, 41] id: 4
  io.write("link: ")
  if a_8jzNK8OZ4i(io.read("*number")) == 0 then
    print("error")
  end
end
list_impl = function()
  -- line: [43, 57] id: 5
  print("-------------------img list-------------------")
  local r0_5 = 0
  local r1_5 = 1
  while r0_5 < 30 do
    if a_IjKn_GF3FE(r0_5) == 1 then
      r1_5 = r1_5 + 1
      print(string.format("%d. pic_%04d. link: http://%d\n", r1_5, r0_5, r0_5))
    end
    r0_5 = r0_5 + 1
  end
end
check_impl = function()
  -- line: [59, 84] id: 6
  local r0_6 = 0
  local r1_6 = 0
  local r2_6 = ""
  local r3_6 = false
  while r0_6 < 30 do
    local r4_6 = a_IjKn_GF3FE(r0_6)
    if r0_6 % 2 == 0 and r4_6 == 1 then
      r1_6 = r1_6 + 1
      local r5_6 = a_Cc_ClWQsa5(r0_6)
      if #r5_6 ~= 2 then
        r3_6 = true
      end
      r2_6 = r2_6 .. r5_6
    end
    r0_6 = r0_6 + 1
  end
  if r1_6 == 15 and #r2_6 == 30 and r3_6 == false and check_func(r2_6) == true then
    print("now, you know the flag~")
    print(r2_6)
  else
    print("you fail!")
  end
end
main_logic = function()
  -- line: [86, 108] id: 7
  while true do
    menu()
    local r0_7 = io.read("*l")
    if r0_7 == "1" then
      upload_impl()
    elseif r0_7 == "2" then
      download_impl()
    elseif r0_7 == "3" then
      delete_impl()
    elseif r0_7 == "4" then
      list_impl()
    elseif r0_7 == "5" then
      check_impl()
    elseif r0_7 == "6" then
      print("bye~")
      break
    else
      print("bad choice")
    end
  end
end
value_list = function(r0_8)
  -- line: [111, 118] id: 8
  local r1_8 = {}
  for r5_8 = 1, string.len(r0_8), 1 do
    r1_8[#r1_8 + 1] = string.byte(r0_8, r5_8)
  end
  return r1_8
end
tobinary = function(r0_9)
  -- line: [122, 135] id: 9
  local r1_9 = r0_9
  local r2_9 = ""
  repeat
    if r1_9 % 2 == 1 then
      local r3_9 = r2_9
      r2_9 = r3_9 .. "1"
    else
      local r3_9 = r2_9
      r2_9 = r3_9 .. "0"
    end
    local r3_9 = math.modf(r1_9 / 2)
    r1_9 = r3_9
  until r1_9 == 0
  return string.reverse(r2_9)
end
xor = function(r0_10, r1_10)
  -- line: [137, 170] id: 10
  local r2_10 = tobinary(r0_10)
  local r3_10 = tobinary(r1_10)
  local r4_10 = string.len(r2_10)
  local r5_10 = string.len(r3_10)
  local r6_10 = 0
  local r7_10 = 0
  local r8_10 = ""
  if r5_10 < r4_10 then
    for r12_10 = 1, math.floor(r4_10 - r5_10), 1 do
      r3_10 = "0" .. r3_10
    end
    r6_10 = r4_10
  elseif r4_10 < r5_10 then
    for r12_10 = 1, math.floor(r5_10 - r4_10), 1 do
      r2_10 = "0" .. r2_10
    end
    r6_10 = r5_10
  end
  for r12_10 = 1, r6_10, 1 do
    if string.sub(r2_10, r12_10, r12_10) == string.sub(r3_10, r12_10, r12_10) then
      r8_10 = r8_10 .. "0"
    else
      r8_10 = r8_10 .. "1"
    end
  end
  return tonumber(r8_10, 2)
end
check_func = function(r0_11)
  -- line: [172, 195] id: 11
  local input = value_list(r0_11)
  local r2_11 = {}
  local ans = {
    105,
    244,
    63,
    10,
    24,
    169,
    248,
    107,
    129,
    138,
    25,
    182,
    96,
    176,
    14,
    89,
    56,
    229,
    206,
    19,
    23,
    21,
    22,
    198,
    179,
    167,
    152,
    66,
    28,
    201,
    213,
    80,
    162,
    151,
    102,
    36,
    91,
    37,
    50,
    17,
    170,
    41,
    3,
    84,
    85,
    226,
    131,
    38,
    71,
    32,
    18,
    142,
    70,
    39,
    112,
    220,
    16,
    219,
    159,
    222,
    11,
    119,
    99,
    203,
    47,
    148,
    185,
    55,
    93,
    48,
    153,
    113,
    1,
    237,
    35,
    75,
    67,
    155,
    161,
    74,
    108,
    76,
    181,
    233,
    186,
    44,
    125,
    232,
    88,
    8,
    95,
    163,
    200,
    249,
    120,
    243,
    174,
    212,
    252,
    234,
    58,
    101,
    228,
    86,
    109,
    144,
    104,
    121,
    117,
    87,
    15,
    132,
    12,
    20,
    165,
    115,
    136,
    135,
    118,
    69,
    68,
    2,
    82,
    123,
    250,
    251,
    53,
    255,
    51,
    221,
    211,
    195,
    145,
    140,
    254,
    0,
    116,
    43,
    29,
    217,
    197,
    183,
    168,
    188,
    34,
    218,
    146,
    147,
    98,
    149,
    246,
    180,
    103,
    33,
    40,
    207,
    208,
    192,
    143,
    26,
    154,
    225,
    100,
    141,
    175,
    124,
    230,
    62,
    177,
    205,
    110,
    202,
    253,
    173,
    46,
    52,
    114,
    164,
    166,
    137,
    158,
    122,
    13,
    83,
    178,
    133,
    189,
    187,
    7,
    184,
    77,
    245,
    216,
    190,
    194,
    72,
    157,
    172,
    171,
    199,
    160,
    45,
    49,
    27,
    204,
    81,
    6,
    92,
    59,
    209,
    239,
    130,
    97,
    61,
    214,
    215,
    73,
    90,
    126,
    42,
    30,
    240,
    79,
    224,
    78,
    223,
    111,
    60,
    4,
    5,
    196,
    231,
    106,
    64,
    139,
    235,
    150,
    227,
    238,
    191,
    127,
    31,
    156,
    54,
    241,
    242,
    134,
    247,
    128,
    65,
    94,
    57,
    210,
    236,
    9,
    193
  }
  for i = 1, #input, 1 do
    input[i] = xor(input[i], i - 1)
    input[i] = xor(input[i], 255)
    input[i] = input[i] & 255
    r2_11[#r2_11 + 1] = ans[input[i] + 1]
  end
  local r4_11 = a_AHy3JniQH4(r2_11) == 1
end
main = function()
  -- line: [198, 201] id: 12
  a_bfBfrMZriK()
  main_logic()
end
main()

注意到是调用了check_23,并且这之前还有个简单加密

from z3 import Int, Solver
l = 30
a1 = [Int(f"x{i}") for i in range(l)]
s = Solver()
v1=a1[0]
v2=a1[1]
v3=a1[2]
v4=a1[3]
v5=a1[4]
v6=a1[5]
v7=a1[6]
v8=a1[7]
v10=a1[8]
v24=a1[9]
v25=a1[10]
v26=a1[11]
v27=a1[12]
v28=a1[13]
v29=a1[14]
v30=a1[15]
v31=a1[16]
v32=a1[17]
v33=a1[18]
v34=a1[19]
v35=a1[20]
v36=a1[21]
v37=a1[22]
v38=a1[23]
v39=a1[24]
v40=a1[25]
v20=a1[26]
v41=a1[27]
v22=a1[28]
s.add(255036*v7+-90989*v3+-201344*v4+122006*v5+-140538*v6+109859*v2-109457*v1-9396023 == 0)
s.add(277432*v6+110191*v3+-186022*v4+175123*v2-75564*v5-252340*v1-12226612 == 0)
s.add(127326*v4+260948*v2+-102835*v1+225038*v5-129683*v3-45564209 == 0)
s.add(-170345*v2+217412*v3-26668*v1+38500*v4-27440782 == 0)
s.add(25295*v2+69369*v3+191287*v1-24434293 == 0)
s.add(72265*v1-2384745 == 0)
s.add(264694*v1-190137*v2+19025100 == 0)
s.add(101752*v24+67154*v8+-20311*v1+-30496*v6+-263329*v7+-99420*v10+255348*v3+169511*v4-121471*v2+231370*v5-33888892 == 0)
s.add(17253*v8+-134891*v7+144501*v4+220594*v2+263746*v3+122495*v6+74297*v10+205480*v1-32973*v5-115484799 == 0)
s.add(251337*v3+-198187*v6+-217900*v2+-62192*v8+-138306*v7+-165151*v4-118227*v1-22431*v5+72699617 == 0)
s.add(243012*v27+-233931*v4+66595*v7+-273948*v5+-266708*v24+75344*v8-108115*v3-17090*v25+240281*v10+202327*v1-253495*v2+233118*v26+154680*v6+25687761 == 0)
s.add(41011*v8+-198187*v1+-117171*v7+-178912*v3+9797*v24+118730*v10-193364*v5-36072*v6+10586*v25-110560*v4+173438*v2-176575*v26+54358815 == 0)
s.add(-250878*v24+108430*v1+-136296*v5+11092*v8+154243*v7+-136624*v3+179711*v4+-128439*v6+22681*v25-42472*v10-80061*v2+34267161 == 0)
s.add(65716*v30+-18037*v26+-42923*v7+-33361*v4+161566*v6+194069*v25+-154262*v2+173240*v3-31821*v27-80881*v5+217299*v8-28162*v10+192716*v1+165565*v24+106863*v29-127658*v28-75839517 == 0)
s.add(-236487*v24+-45384*v1+46984*v26+148196*v7+15692*v8+-193664*v6+6957*v10+103351*v29-217098*v28+78149*v4-237596*v5-236117*v3-142713*v25+24413*v27+232544*v2+78860648 == 0)
s.add(-69129*v10+-161882*v3+-39324*v26+106850*v1+136394*v5+129891*v2+15216*v27+213245*v24-73770*v28+24056*v25-123372*v8-38733*v7-199547*v4-10681*v6+57424065 == 0)
s.add(-268870*v30+103546*v24+-124986*v27+42015*v7+80222*v2+-77247*v10+-8838*v25+-273842*v4+-240751*v28-187146*v26-150301*v6-167844*v3+92327*v8+270212*v5-87705*v33-216624*v1+35317*v31+231278*v32-213030*v29+114317949 == 0)
s.add(-207225*v1+-202035*v3+81860*v27+-114137*v5+265497*v30+-216722*v8+276415*v28+-201420*v10-266588*v32+174412*v6+249222*v24-191870*v4+100486*v2+37951*v25+67406*v26+55224*v31+101345*v7-76961*v29+33370551 == 0)
s.add(175180*v29+25590*v4+-35354*v30+-173039*v31+145220*v25+6521*v7+99204*v24+72076*v27+207349*v2+123988*v5-64247*v8+169099*v6-54799*v3+53935*v1-223317*v26+215925*v10-119961*v28-83559622 == 0)
s.add(43170*v3+-145060*v2+199653*v6+14728*v30+139827*v24+59597*v29+2862*v10+-171413*v31+-15355*v25-71692*v7-16706*v26+264615*v1-149167*v33+75391*v27-2927*v4-187387*v5-190782*v8-150865*v28+44238*v32-276353*v34+82818982 == 0)
s.add(-3256*v27+-232013*v25+-261919*v29+-151844*v26+11405*v4+159913*v32+209002*v7+91932*v34+270180*v10+-195866*v3-135274*v33-261245*v1+24783*v35+262729*v8-81293*v24-156714*v2-93376*v28-163223*v31-144746*v5+167939*v6-120753*v30-13188886 == 0)
s.add(-240655*v35+103437*v30+236610*v27+100948*v8+82212*v6+-60676*v5+-71032*v3+259181*v7+100184*v10+7797*v29+143350*v24+76697*v2-172373*v25-110023*v37-13673*v4+129100*v31+86759*v1-101103*v33-142195*v36+28466*v32-27211*v26-269662*v34+9103*v28-96428951 == 0)
s.add(-92750*v28+-151740*v27+15816*v35+186592*v24+-156340*v29+-193697*v2+-108622*v8+-163956*v5+78044*v4+-280132*v36-73939*v33-216186*v3+168898*v30+81148*v34-200942*v32+1920*v1+131017*v26-229175*v10-247717*v31+232852*v25+25882*v7+144500*v6+175681562 == 0)
s.add(234452*v34+-23111*v29+-40957*v2+-147076*v8+16151*v32+-250947*v35+-111913*v30+-233475*v24+-2485*v28+207006*v26+71474*v3+78521*v1-37235*v36+203147*v5+159297*v7-227257*v38+141894*v25-238939*v10-207324*v37-168960*v33+212325*v6+152097*v31-94775*v27+197514*v4+62343322 == 0)
s.add(-142909*v34+-111865*v31+258666*v36+-66780*v2+-13109*v35+-72310*v25+-278193*v26+-219709*v24+40855*v8+-270578*v38+96496*v5+-4530*v1+63129*v28-4681*v7-272799*v30-225257*v10+128712*v37-201687*v39+273784*v3+141128*v29+93283*v32+128210*v33+47550*v6-84027*v4+52764*v40-140487*v27+105279220 == 0)
s.add(216020*v38+-248561*v29+-86516*v33+237852*v26+-132193*v31+-101471*v3+87552*v25+-122710*v8+234681*v5+-24880*v7+-245370*v1+-17836*v36-225714*v34-256029*v4+171199*v35+266838*v10-32125*v24-43141*v32-87051*v30-68893*v39-242483*v28-12823*v2-159262*v27+123816*v37-180694*v6+152819799 == 0)
s.add(-116890*v3+67983*v27+-131934*v4+256114*v40+128119*v24+48593*v33+-41706*v2+-217503*v26+49328*v6+223466*v7+-31184*v5+-208422*v36+261920*v1+83055*v20+115813*v37+174499*v29-188513*v35+18957*v25+15794*v10-2906*v28-25315*v8+232180*v32-102442*v39-116930*v34-192552*v38-179822*v31+265749*v30-54143007 == 0)
s.add(-215996*v4+-100890*v40+-177349*v7+-159264*v6+-227328*v27+-91901*v24+-28939*v10+206392*v41+6473*v25+-22051*v20+-112044*v34+-119414*v30+-225267*v35+223380*v3+275172*v5+95718*v39-115127*v29+85928*v26+169057*v38-204729*v1+178788*v36-85503*v31-121684*v2-18727*v32+109947*v33-138204*v8-245035*v28+134266*v37+110228962 == 0)
s.add(-165644*v32+4586*v39+138195*v25+155259*v35+-185091*v3+-63869*v31+-23462*v30+150939*v41+-217079*v8+-122286*v6+5460*v38+-235719*v7+270987*v26+157806*v34+262004*v29-2963*v28-159217*v10+266021*v33-190702*v24-38473*v20+122617*v2+202211*v36-143491*v27-251332*v4+196932*v5-155172*v22+209759*v40-146511*v1+62542*v37+185928391 == 0)
s.add(57177*v24+242367*v39+226332*v31+15582*v26+159461*v34+-260455*v22+-179161*v37+-251786*v32+-66932*v41+134581*v1+-65235*v29+-110258*v28+188353*v38+-108556*v6+178750*v40+-20482*v25+127145*v8+-203851*v5+-263419*v10+245204*v33+-62740*v20+103075*v2-229292*v36+142850*v30-1027*v27+264120*v3+264348*v4-41667*v35+130195*v7+127279*a1[29]-51967523 == 0)


print(s.check())
m = s.model()

result = []
for i in a1:
    result.append(m[i].as_long())
print(result)

ans = [105,244,63,10,24,169,248,107,129,138,25,182,96,176,14,89,56,229,206,19,23,21,22,198,179,167,152,66,28,201,213,80,162,151,102,36,91,37,50,17,170,41,3,84,85,226,131,38,71,32,18,142,70,39,112,220,16,219,159,222,11,119,99,203,47,148,185,55,93,48,153,113,1,237,35,75,67,155,161,74,108,76,181,233,186,44,125,232,88,8,95,163,200,249,120,243,174,212,252,234,58,101,228,86,109,144,104,121,117,87,15,132,12,20,165,115,136,135,118,69,68,2,82,123,250,251,53,255,51,221,211,195,145,140,254,0,116,43,29,217,197,183,168,188,34,218,146,147,98,149,246,180,103,33,40,207,208,192,143,26,154,225,100,141,175,124,230,62,177,205,110,202,253,173,46,52,114,164,166,137,158,122,13,83,178,133,189,187,7,184,77,245,216,190,194,72,157,172,171,199,160,45,49,27,204,81,6,92,59,209,239,130,97,61,214,215,73,90,126,42,30,240,79,224,78,223,111,60,4,5,196,231,106,64,139,235,150,227,238,191,127,31,156,54,241,242,134,247,128,65,94,57,210,236,9,193]

for i in range(l):
    t = ans.index(result[i])
    print(chr(t ^ 0xff ^ i), end='')
# flag{U_90t_th3_p1c5t0re_fl49!}

主要是要注意下不能用BitVec,可能是因为有负数的运算吧。

RTTT

发现一个异或,出来结果是 Welc0me to RCTF 2O22
然后硬件断点一路跟下去,发现生成RC4的S盒。
然后拿到KeyStream(我的keystream是加密了'f'的结果)
然后对着结果硬件断点,发现一个strcmp。
不过我发现解出来后顺序不对,应该是有个树的遍历(看着比较像)
于是我弄了一个唯一确定的字符序列,用来获取SBox。

keystream = bytes.fromhex("""
11 93 47 0F 85 91 E1 FE  0C 8E 4D F8 6F 8A 87 CC
A4 7C 70 1B 09 96 30 26  5D 30 39 5E 43 BD 0F 81
09 74 B0 F4 4E 0B 90 63  48 11
""")
ans = bytes.fromhex("""
34 C2 65 2D DA C6 B1 AD  47 BA 06 A9 3B C1 CC D7
F1 29 24 39 2A C0 15 02  7E 10 66 7B 5E EA 5E D0
59 46 E1 D6 6E 5E B2 46  6B 31""")

k = [ans[i] ^ keystream[i] ^ ord('f') for i in range(len(keystream))]
t = "".join(map(chr, k))
print(k)
# generate SBOx
a = "yJzLkHwDxaCAtnsPipmIBfhljdGbeOqKNcEMugvFor"
b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
sbox = [a.index(b[i]) for i in range(len(a))]
for i in range(len(t)):
    print(t[sbox[i]], end='') # RCTF{03C3E9B2-E37F-2FD6-CD7E-57C91D77DD61}

CheckYourKey

RegisterNatives -> True Function -> AES -> B58Encode(table1) -> B64Encode(table2) -> Strcmp
2024-09-17T05:58:20.png

web_run

JEB 反编译,找到main函数
2024-09-17T09:14:05.png

SEED = (202211110054 - 1) & 0xffffffff
def _f7():
    global SEED
    v0 = SEED * 6364136223846793005 + 1
    SEED = v0
    return (v0 >> 33) % 16


def _f8(par):
    if par >= 0 and par <= 9:
        return 48 + par
    val = par - 10 
    if val == 0:
        return 97
    elif val == 1:
        return 98
    elif val == 2:
        return 99
    elif val == 3:
        return 100
    elif val == 4:
        return 101
    elif val == 5:
        return 102
    return 48


def generate(s):
    for i in range(len(s)):
        if not (s[i] != 52 and s[i] != 45):
            continue
        if s[i] == 120:
            v2 = _f8(_f7())
            s[i] = v2
        else:
            v2 = _f8((_f7() & 3) | 0x8)
            s[i] = v2 
    return bytes(s)


A = generate(list(b'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'))
print(f"RCTF{{{A.decode()}}}")
# RCTF{40959ea7-26e0-4c9d-8f4a-62faf14ff392}

Destination

挂上TitanHide,注意到NtSetInformationThread, NtGetContextThread这两个反调试
x64dbg断下后在线程开始ret即可。
NtSetInformationThread会使当前线程从调试器隐藏,调试器跑飞
NtGetContextThread可以获取到当前线程的寄存器状况,用来检测DR寄存器来反硬件断点
还有一个反调试是我进入后发现的,主线程卡住,但是线程窗口里面有一个线程一直在Sleep(1);,于是干掉就行了。

于是我们就得到了第一个patch的附件,我们以这个附件为基础继续调。
注意到我们输入完flag之后给我们来了个int3,导致进入SEH流程,如下图所示。
2024-08-20T18:01:48.png
图中框起来的是SEH处理函数,我们进去后,显然是似了。
2024-08-20T18:02:35.png
我们观察到,这些花指令由2种构成
1.call $+5; add [esp], 5; retn
2.jnz addr; jz addr
显然第一种可以化简为nop,第二种我们可以化简为jmp addr
处理脚本如下

from pwn import u32, u8
binfile = open("Destination_patched_2.exe","rb").read()
 
founds = []
# find all junkcode
index = 0
while True:
    index = binfile.find(b'\xE8\x00\x00\x00\x00\x83\x04\x24\x05\xC3',index+1) #! index+1 ! 不然死循环了...
    if index == -1:
        break
    founds.append(index)
    print(index)
binfile = bytearray(binfile)
for i in founds:
    print(binfile[i:i+10])
    for j in range(i,i+10):
        binfile[j] = 0x90
    print(binfile[i:i+10])
# find all junkcode
index = 0
while True:
    i = binfile.find(b'\x0f\x84',index+1)
    index = i
    if i == -1:
        break
    if binfile[index+6:index+8] == b'\x0f\x85' and u32(binfile[index+2:index+6]) - u32(binfile[index+8:index+12]) == 6:
        binfile[i] = 0x90
        binfile[i+1] = 0xe9
        binfile[i+7] = 0x66
        binfile[i+8] = 0x0f
        binfile[i+9] = 0x18
        binfile[i+10] = 0x20
        binfile[i+11] = 0x90

index = 0
while True:
    i = binfile.find(b'\x74',index+1)
    index = i
    if i == -1:
        break
    if binfile[i+2] == 0x75 and binfile[i+1] - binfile[i+3] == 2:
        binfile[i] = 0xeb
        binfile[i+2] = 0x90
        binfile[i+3] = 0x90
        binfile[i+4] = 0x90

# replace all matches
print(open("Destination_p3.exe","wb").write(binfile))

处理完花指令后,我们进入Ghidra,直接当成Func,然后手动处理一点东西,就得到了:
2024-08-20T18:05:47.png


void xxtea(void)

{
  uint e;
  uint sum;
  uint z;
  uint i;
  int round;
  uint y;
  
  round = 50;
  sum = 0;
  z = src[0xb];
  do {
    sum += 0xa4b46062;
    e = sum >> 2 & 3;
    src[0xb] = z;
    for (i = 0; i < 11; i += 1) {
      y = src[i + 1];
      z = src[i] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[i & 3 ^ e] ^ z));
      src[i] = z;
    }
    z = src[0xb] +
        ((z >> 5 ^ src[0] << 2) + (src[0] >> 3 ^ z << 4) ^ (sum ^ src[0]) + (key[i & 3 ^ e] ^ z));
    src[0xb] = z;
    round += -1;
  } while (round != 0);
  return;
}

显然的xxtea。
分析完这个后,我们下硬件断点,发现没断下来而且值还被改了,我们回到主函数,找到这个地址
2024-08-20T18:09:13.png
有一个jmp far
2024-08-20T18:09:26.png
ida的反汇编这里有点问题,我们x32dbg看一看,发现是jmp far 0x33:xxxxx
2024-08-20T18:10:09.png
这是windows下的天堂之门,0x23就是转32位,0x33就是转64位运行,这个是CS寄存器的值。
我们把jmp far的目标地址函数dump下来,放到一个二进制文件,拖入ida,选中64 bit mode,就可以看到真正的源码了。
2024-08-20T18:11:37.png
可以注意到,这是一个已经玩烂的不安全CRC,它曾出现在:北邮去年招新赛的五子棋程序逆向、D^3CTF,moectf2024中。
提供两种解法,第一种就是z3-Solver

from z3 import *
from copy import deepcopy
s = Solver()
x = [BitVec(f"x{i}", 32) for i in range(12)]
y = deepcopy(x)
ans = [0xA790FAD6, 0xE8C8A277, 0xCF0384FA, 0x2E6C7FD7, 0x6D33968B, 0x5B57C227, 0x653CA65E, 0x85C6F1FC, 0xE1F32577, 0xD4D7AE76, 0x3FAF6DC4, 0x0D599D8C]
print(len(ans))
for i in range(12):
    for j in range(32):
        x[i] = If(LShR(x[i], 31) == 1, 2*x[i] ^ 0x84A6972F, 2*x[i])
    s.add(x[i] == ans[i])
print(s.check())
print(s.model())
'''
12
sat
[x1 = 1585230664,
 x10 = 950802794,
 x0 = 2656343363,
 x3 = 2246810078,
 x7 = 688747158,
 x11 = 422273590,
 x9 = 2522199337,
 x4 = 3061995652,
 x5 = 3304541857,
 x6 = 582058634,
 x8 = 428794995,
 x2 = 3651684911]
'''

也是最方便的,下面的解法是按照原理来的:
由于由于左移会变偶数,而xor后又会变成奇数,所以有唯一解。
我们先获取最低位是否为1,如果是1,说明是奇数,如果为奇数,说明它刚刚走的是最高位是1的那条分支,也说明它异或了立即数,我们直接给它异或回来,然后右移1,由于我们已经确定最高位是1,我们给它补上1即可。
如果最低位是0,我们就直接右移1,然后确定最高位是0即可。

解密脚本

#include <iostream>
#include <Windows.h>
// n = 12
typedef DWORD uint;
void xxtea(uint32_t* src, int n, uint32_t const key[4])
{

    if (n > 0) /* Coding Part */
    {
        uint e;
        uint sum;
        uint z;
        uint i;
        int round;
        uint y;

        round = 50;
        sum = 0;
        z = src[0xb];
        do {
            sum += 0xa4b46062;
            e = sum >> 2 & 3;
            src[n - 1] = z;
            for (i = 0; i < n - 1; i += 1) {
                y = src[i + 1];
                z = src[i] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[i & 3 ^ e] ^ z));
                src[i] = z;
            }
            z = src[n - 1] +
                ((z >> 5 ^ src[0] << 2) + (src[0] >> 3 ^ z << 4) ^ (sum ^ src[0]) + (key[i & 3 ^ e] ^ z));
            src[n - 1] = z;
            round += -1;
        } while (round != 0);
    }
    else if (n <= 0)
    {
        uint32_t y, z, sum;
        unsigned p, rounds, e;

        n = -n;
        rounds = 50;
        sum = rounds * 0xa4b46062;
        y = src[0];
        do
        {
            e = (sum >> 2) & 3;
            for (p = n - 1; p > 0; p--)
            {
                z = src[p - 1];
                y = src[p] -= ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[p & 3 ^ e] ^ z));
            }
            z = src[n - 1];
            y = src[0] -= ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[p & 3 ^ e] ^ z));
            sum -= 0xa4b46062;
        } while (--rounds);

    }
}

unsigned char a[] = {0xd6, 0xfa, 0x90, 0xa7, 0x77, 0xa2, 0xc8, 0xe8, 0xfa, 0x84, 0x03, 0xcf, 0xd7, 0x7f, 0x6c, 0x2e, 0x8b, 0x96, 0x33, 0x6d, 0x27, 0xc2, 0x57, 0x5b, 0x5e, 0xa6, 0x3c, 0x65, 0xfc, 0xf1, 0xc6, 0x85, 0x77, 0x25, 0xf3, 0xe1, 0x76, 0xae, 0xd7, 0xd4, 0xc4, 0x6d, 0xaf, 0x3f, 0x8c, 0x9d, 0x59, 0x0d};
//unsigned char a[] = "flag{111111111111111111111111111111111111111}";
unsigned char k[] = {0x6b, 0x7a, 0x0e, 0x6b, 0xee, 0x11, 0x30, 0xd1, 0x6d, 0x2c, 0xe1, 0xa7, 0xa6, 0xac, 0x99, 0xc1 };
int main()
{
    for (int i = 0; i < 12; i++)
    {
        uint32_t n = *(i + (uint32_t*)a);
        for (int j = 0; j < 32; j++)
        {
            if (n & 1)
            {
                n ^= 0x84A6972F;
                n >>= 1;
                n |= 0x80000000;
            }
            else
            {
                n >>= 1;
                n &= 0x7fffffff;
            }
        }
        *(i + (uint32_t*)a) = n;
    }

    xxtea((uint32_t*)a, -12, (uint32_t*)k);
    xxtea((uint32_t*)a, -12, (uint32_t*)k);
    std::cout << a; //DubheCTF{82e1e3f8-85fe469f-8499dd48-466a9d60}
    return 0;
}

regadgets 版本的xxtea exp

from regadgets import *
k = [0x6b, 0x7a, 0x0e, 0x6b, 0xee, 0x11, 0x30, 0xd1, 0x6d, 0x2c, 0xe1, 0xa7, 0xa6, 0xac, 0x99, 0xc1]
k4 = byte2dword(k)
enc = byte2dword(bytes.fromhex("""
43 95 54 9e 48 b3 7c 5e 2f 4a a8 d9 de 99 eb 85
84 58 82 b6 a1 4e f7 c4 8a 82 b1 22 96 72 0d 29
73 e4 8e 19 29 b5 55 96 6a 19 ac 38 36 62 2b 19
"""))


dec = xxtea_decrypt(enc, k4, delta=0xa4b46062, additional_rounds=40)
dec = xxtea_decrypt(dec, k4, delta=0xa4b46062, additional_rounds=40)

print(dword2bytes(dec))