2024年9月

什么是REGadgets

REGadgets,顾名思义,就是一个Reverse中常用的,提供给使用者代码片段的库,可以加快逆向速度,甚至直接爆破出结果。

REGadgets 如何获取

REGadgets目前还在开发中,也就是只在小范围测试中,开发者将用它参加每一场XCTF分站赛,所有的WP都是用它来写的,以保证REGadgets库与时俱进。

REGadgets 项目结构

截至到 2024/09/30 REGadgets项目目录如下。

C:.
│   .gitignore
│   README.md
│   __init__.py
│
├───bases
│       base128.py
│       base2048.pyd
│       base2048.pyi
│       base45.py
│       base58.py
│       base62.py
│       base65536.py
│       base91.py
│       bases.py
│       py3base92.py
│       __init__.py
│
├───bits
│       bits.py
│       cstyle.py
│       __init__.py
│
├───bruteforce
│       bruteforcer.py
│       __init__.py
│
├───crypto
│       aes.py
│       blowfish.py
│       bxors.py
│       rc4.py
│       tea.py
│       xtea.py
│       xxtea.py
│       __init__.py
│
├───trans
│       sbox.py
│       __init__.py
│
└───utils
        z3util.py
        __init__.py

REGadgets库相关介绍及使用方法

regadgets.bases

这个库中,提供了 base 相关的编码解码,而且,regadgets提供了换表的快捷方案。

例题 BaseCTF2024 BasePlus

from regadgets import *
enc = b'lvfzBiZiOw7<lhF8dDOfEbmI]i@bdcZfEc^z>aD!'
k = 0xe
dec = decode_b64(bxor(enc, [k for _ in range(len(enc))]).decode(),
                  '/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC')

print(dec)
# b'BaseCTF{BA5e_DEcoD1N6_sEcr3t}\x00'

在本例中,通过regadgets.bases.decode_b64我们对enc异或后的东西(base64编码后的东西)进行了解码,表是第二个参数。

例题 CDDC2024 Quals Emoji32

regadget的bases,支持换emoji表,因为Python的str对于Unicode非常友好。

from regadgets import *
emoji_table = ['😀','😥','😠','😱','😴','🤢','😇','😈','👋','✊','👌','✌','🤲','🙌','👏','🤝','🐶','🐱','🐭','🐹','🐰','🦊','🐻','🐼','🍎','🍌','🍓','🍑','🍕','🍔','🍟','🍦']
emoji_table = "".join(emoji_table)
encoded = "🤢😥😇😴🐭🦊😥✊✌️🍕🐰😴🍎🐭👌🐰🤢😴"
s = encoded.replace('\ufe0f', '') # Remove Wrong Char
while True:
    try:
        print(decode_b32(s, emoji_table))
        # (LIT)_(LIT)
        break
    except:
        s += '=' # Fix Padding
        continue

regadgets.bits

这个子库中,提供了位与数的相关操作。

# 相关提供的函数有这些
from .bits import rol16, rol32, rol64, rol8, ror16, ror32, ror64, ror8, byte2dword, dword2byte, byte2qword, qword2byte, pack_dword, unpack_dword, b2l, l2b
from .cstyle import cstyle_arr32

rol & ror

这些函数用于在Python下执行相应位数的循环左移和循环右移。

byte2word byte2dword byte2qword

用于将Python的bytes 2个/4个/8个 打包成为 List[int],可选是否进行Padding。其中int是word或dword或qword,小端序,方便后续进行TEA族加密。

word2byte dword2byte qword2byte

用于将List[int] 解包为Python bytes,小端序。用于将外部的数据(程序中提取出来的)快速进行处理。

pack_dword

用于将[1, 2, 3, 4, 5, 6] 这样的List[int],两两打包为形如 [(1, 2), (3, 4), (5, 6)] ,方便进行TEA族加密。

unpack_dword

用于将 [(1, 2), (3, 4), (5, 6)] 解包为 [1, 2, 3, 4, 5, 6] 这样的List[int]

l2b b2l

从Crypto.Util.number里面复制出来的long_to_bytes和bytes_to_long,用于大端序的互相转换。

regadgets.bruteforce

本子库用于解决逆向工程中的一些基础爆破问题。

regadgets.bruteforce.rg_brute_forcer

基础的Reverse下的爆破函数,通过检测解密后文本是否含有flag(可以进行修改),且解密后均为明文,来进行反向爆破,并还原出解密链。
本函数可以进行定制化扩展,由于特殊的模块化设计,可以自己手写一些常见算法,并在这些算法中爆破。
未来的话,这个函数可能会写成Cython实现的,用来加速爆破进程。

使用例

from regadgets import *

raw = list(b'flag{124214782134798217}')

for i in range(len(raw)):
    raw[i] ^= 114
    raw[i] += 514
    raw[i] ^= 810
    raw[i] &= 0xff

rg_brute_forcer(raw, 3)
'''
尝试深度 1 的解密...
[失败] 没有在深度 1 找到解密方案,用时 0.003998994827270508秒
尝试深度 2 的解密...
[失败] 没有在深度 2 找到解密方案,用时 2.940094470977783秒
尝试深度 3 的解密...
找到正确解密链: [('xor_decrypt', 20), ('mod_add_decrypt', 2), ('xor_decrypt', 76)]
解密结果: b'flag{124214782134798217}'
[成功] 在深度 3 找到解密方案,用时 59.33841252326965秒
'''

regadgets.crypto

regadgets.crypto.aes

标准的AES实现,根据比赛可以进行手动魔改。

regadgets.crypto.blowfish

标准的blowfish实现,根据比赛可以进行手动魔改。

regadgets.crypto.rc4

提供rc4_init, rc4_crypt, rc4_keystream三个函数,在init的时候返回S盒,crypt的时候提供s盒和data用于加密,keystream函数可以只返回S盒生成的key长度,可以手动指定box_size,因为在 BaseCTF2024 出了一个 S盒大小128的题目。

regadgets.crypto.bxors

提供了相关的XOR实现,比如 x_0 ^ x_1, x_1 ^ x_2, x_2 ^ x_3, ..... , x_(n-1) ^ x_n, x_n (我们称之为向右异或,bxorr)的加密解密。
除此之外,还提供了bxor(用于将两个等长的bytes | list进行互相异或)。
最常用的是,bxor_cycle,用于将第一个参数进行循环异或第二个参数(bytes | list)
一个常用的例子

# 对于一个数组循环异或 b'my_key'
enc = [...]
raw = bxor_cycle(enc, b'my_key')
# 对于一个数组进行异或 0xEE
raw = bxor_cycle(enc, b'\xee')
# 对于一个数组进行异或循环变量 i
raw = bxor_cycle(enc, range(len(enc)))

regadgets.crypto.tea / xtea / xxtea

用 python ctypes 实现的 TEA族加密。可以自行指定 delta / rounds, 对于xxtea可以指定很多参数,甚至是shift函数,具体请见其他题解。

regadgets.trans

用于S盒之类的变换

regadgets.trans.sbox

提供了 generate_sboxsbox_transform 两个函数。
如例题

from regadgets import *
from hashlib import md5
str1 = [0x28, 0x5F, 0x40, 0x34, 0x36, 0x32, 0x30, 0x21, 0x30, 0x38, 0x21, 0x36, 0x5F, 0x30, 0x2A, 0x30, 0x34, 0x34, 0x32, 0x21, 0x40, 0x31, 0x38, 0x36, 0x25, 0x25, 0x30, 0x40, 0x33, 0x3D,
        0x36, 0x36, 0x21, 0x21, 0x39, 0x37, 0x34, 0x2A, 0x33, 0x32, 0x33, 0x34, 0x3D, 0x26, 0x30, 0x5E, 0x33, 0x26, 0x31, 0x40, 0x3D, 0x26, 0x30, 0x39, 0x30, 0x38, 0x21, 0x36, 0x5F, 0x30, 0x2A, 0x26]
str2 = [0x35, 0x35, 0x35, 0x36, 0x35, 0x36, 0x35, 0x33, 0x32, 0x35, 0x35, 0x35, 0x35, 0x32, 0x32, 0x32, 0x35, 0x35, 0x36, 0x35, 0x35, 0x36, 0x35, 0x35, 0x35, 0x35, 0x32, 0x34, 0x33, 0x34,
        0x36, 0x36, 0x33, 0x33, 0x34, 0x36, 0x35, 0x33, 0x36, 0x36, 0x33, 0x35, 0x34, 0x34, 0x34, 0x32, 0x36, 0x35, 0x36, 0x35, 0x35, 0x35, 0x35, 0x35, 0x32, 0x35, 0x35, 0x35, 0x35, 0x32, 0x32, 0x32]
xxx = [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2D, 0x3D, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29, 0x5F, 0x2B, 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75, 0x69, 0x6F, 0x70, 0x5B, 0x5D, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49, 0x4F,
       0x50, 0x7B, 0x7D, 0x61, 0x73, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x3B, 0x27, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0x3A, 0x22, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0x3C, 0x3E, 0x3F, 0x7A, 0x78, 0x63, 0x76, 0x62, 0x6E, 0x6D, 0x2C, 0x2E, 0x2F]
r = []
for i in range(62):
    a = xxx.index(str1[i])
    b = xxx.index(str2[i])
    r.append(b * 23 + a)
# private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)
print(bytes(r))

# 手动转换一下 查表
r = b'?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z'

###### 从这里开始是S盒变换
data_from = b'abcdefghijklmnopqrstuvwxyz12345'
data_to = b'pqhrsidtujvwkebxylz1mf23n45ogca'
_, ibox = generate_sbox(data_from, data_to)
r = sbox_transform(r, ibox)

print(bytes(r))
print(f"flag{{{md5(bytes(r)).hexdigest()}}}")
# flag{63b148e750fed3a33419168ac58083f5}

regadgets.utils

regadgets.utils.z3util

目前只有z3_get_models,用于获取Z3的Solver的所有解。

ez_cython

from regadgets import *
ans = [4108944556, 3404732701, 1466956825, 788072761, 1482427973, 782926647, 
       1635740553, 4115935911, 2820454423, 3206473923, 1700989382, 2460803532,
       2399057278, 968884411, 1298467094, 1786305447, 3953508515, 2466099443,
       4105559714, 779131097, 288224004, 3322844775, 4122289132, 2089726849, 
       656452727, 3096682206, 2217255962, 680183044, 3394288893, 697481839, 
       1109578150, 2272036063]

def xxtea_sctf2024_shift(z, y, sum, k, p, debug = False):
    e = (sum.value >> 3) & 3
    PE = (p & 2) ^ e
    Ly = y.value << 3
    Ry = y.value >> 4
    Lz = z.value << 2
    Rz = z.value >> 3 

    LzRy = Rz ^ Ly
    LyRz = Ry ^ Lz
    SY = sum.value ^ y.value
    K = k[PE].value
    KZ = K ^ z.value
    result = (LzRy + LyRz) ^ (KZ + SY)
    return result
        
key = [0x53, 0x79, 0x43, 0x31] # Syc1

dec = xxtea_decrypt(ans, key, delta=0x9e3779b9, round_base=4, round_addi=60, shift_func=xxtea_sctf2024_shift)
print(bytes(dec))
# b'SCTF{w0w_y0U_wE1_kNOw_of_cYtH0N}'

BBox

Android
超绝一键apk混淆捏

const char *__fastcall Java_com_example_bbandroid_MainActivity_checkFlag(_JNIEnv *a1, __int64 a2, __int64 a3)
{
  time_t v5; // w22
  const char *result; // x0
  const char *v7; // x21
  unsigned int v8; // w23
  signed int v9; // w22
  __int64 v10; // x19
  __int64 v11; // x20
  char *v12; // x22
  char v13; // w0
  int v14; // w9
  signed int v15; // w8
  unsigned __int64 v16; // x10
  unsigned __int64 v17; // x11
  int v18; // w12
  int v19; // w13
  char flag[256]; // [xsp+8h] [xbp-108h] BYREF
  __int64 v21; // [xsp+108h] [xbp-8h]

  v21 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  v5 = time(0LL);
  result = (const char *)((__int64 (__fastcall *)(_JNIEnv *, __int64, _QWORD))a1->functions->GetStringUTFChars)(
                           a1,
                           a3,
                           0LL);
  if ( result )
  {
    v7 = result;
    v8 = v5 / 1000000 / 100;
    strncpy(flag, result, 0xFFu);
    flag[255] = 0;
    v9 = __strlen_chk(flag, 0x100u);
    ((void (__fastcall *)(_JNIEnv *, __int64, const char *))a1->functions->ReleaseStringUTFChars)(a1, a3, v7);
    srand(v8);
    if ( v9 >= 4 )
    {
      v10 = 0LL;
      v11 = (unsigned int)v9 >> 2;
      do
      {
        v12 = &flag[4 * v10];
        *v12 ^= rand();
        v12[1] ^= rand();
        v12[2] ^= rand();
        v13 = rand();
        v14 = 32;
        v12[3] ^= v13;
        v15 = *(_DWORD *)v12;
        do
        {
          if ( v15 >= 0 )
            v15 *= 2;
          else
            v15 = (2 * v15) ^ 0x85B6874F;
          --v14;
        }
        while ( v14 );
        *(_DWORD *)&flag[4 * v10++] = v15;
      }
      while ( v10 != v11 );
    }
    if ( flag[0] == '3' )
    {
      v16 = 0LL;
      do
      {
        v17 = v16;
        if ( v16 == 0x27 )
          break;
        v18 = (unsigned __int8)flag[v16 + 1];
        v19 = dword_B14[++v16];
      }
      while ( v18 == v19 );
      return (const char *)(v17 > 0x26);
    }
    else
    {
      return 0LL;
    }
  }
  return result;
}

Exp

from regadgets import *
from z3 import *
from copy import deepcopy
enc =  [0x33, 0xC0, 0xC8, 0xA3, 0xF3, 0xBF, 0x1D, 0x1A, 0x3B, 0x41, 
  0xB7, 0xC6, 0xF1, 0x5E, 0x86, 0x52, 0x52, 0xCF, 0x6B, 0x1E, 
  0xC5, 0xF9, 0xCB, 0xBF, 0xED, 0x7B, 0x62, 0xF1, 0xF7, 0x43, 
  0x48, 0x54, 0xFB, 0x85, 0x4C, 0xD9, 0x35, 0x30, 0xF2, 0x6E]
print(bytes(enc))
dw = byte2dword(enc)

print(dw)
s = Solver()
x = [BitVec(f"x{i}", 32) for i in range(len(dw))]
y = deepcopy(x)
for i in range(len(dw)):
    for j in range(32):
        x[i] = If(LShR(x[i], 31) == 1, 2*x[i] ^ 0x85b6874f, 2*x[i])
    s.add(x[i] == dw[i])
print(s.check())
m = s.model()
r = []
for i in y:
    r.append(m[i].as_long())
print(r)
randv = [0x49308bb9,0x3cb3ad,0xfb4e87f,0x75655103,0x6d505b9f,0x1d20580f,
         0xdcf4af1,0x3e381967,0x54bcf579,0x73c09db7,0x501b2039,0x1b8950dd,
         0x23e73393,0x2b480a88,0x6818cdae,0x61d009ea,0x44c0c5b0,0x385aff3d,
         0x5cfb2a7a,0x587f9c07,0x158172f2,0x4d334c89,0x302b76e5,0x5e17f434,
         0x692de923,0x806d155,0x3d2c61d8,0x1d09ef4e,0x7c3d83b7,0x1d7621da,
         0x2dc0a3ec,0x456e0f71,0x1db2d588,0x3d758c6c,0x3ad36074,0xb033127,0x5a95e47b,0x48a2ab65,0x493b4a8e,0x2f52d9f5 ]
randv = [i & 0xff for i in randv]

v1 = bxor(dword2byte(r)[:len(enc)], bytes(randv)[:len(enc)])
v2 = bxor_cycle(v1, b'\x1e').decode()
print(decode_b64(v2, "nopqrstDEFGHIJKLhijklUVQRST/WXYZabABCcdefgmuv6789+wxyz012345MNOP"))
# b'Y0u_@re_r1ght_r3ver53_is_easy!'

其中 randv 是通过hook得到的rand()的结果。中间是一个不安全的CRC。

easyMCU

我自认为这是一个逆向题目,所以把它放到了Reverse下。
首先,使用010Editor,打开mcu.s19,发现它直接自动给我转hex了,我们Ctrl+S保存到文件即可。
隔壁队伍使用的是bincopy convert mcu.s19 -o binary out.bin进行的转换,也是一种方法。

通过图片中的TriCore,我们可以知道固件是Tri
经过翻找,我们找到关键代码


/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */

undefined4 FUN_80000690(void)

{
  byte bVar1;
  bool bVar2;
  uint uVar3;
  undefined4 uVar4;
  int i;
  
  bVar2 = FUN_8000125a(0x6000009c,(undefined *)0x60000004,(short *)0x60000000,_DAT_80003990,
                       iRam80003994);
  if (bVar2) {
    AES_ENCRYPT(0x60000004,-0x7fffc65d,0x6000007c,0x20);
    for (i = 0; i < 0x20; i += 1) {
      uVar3 = rol((uint)*(byte *)(i + 0x6000007c),3);
      *(char *)(i + 0x6000007c) = (char)uVar3;
      bVar1 = bRam6000007c;
      if (i < 0x1f) {
        bVar1 = *(byte *)(i + 0x6000007d);
      }
      *(byte *)(i + 0x6000007c) = bVar1 ^ *(byte *)(i + 0x6000007c);
      *(byte *)(i + 0x6000007c) = *(byte *)(i + 0x6000007c) ^ 0xff;
    }
    FUN_80001278((int *)0x6000009c,(undefined *)0x6000007c,(short *)0x60000000,_DAT_80003990,
                 iRam80003994);
    uVar4 = 0;
  }
  else {
    uVar4 = 0xffffffff;
  }
  return uVar4;
}


void AES_ENCRYPT(int param_1,int param_2,int param_3,uint param_4)

{
  uint uVar1;
  undefined8 auStack_c0 [2];
  undefined key [176];
  
  FUN_800002b0(param_2,(int)key);
  for (uVar1 = 0; uVar1 < param_4; uVar1 += 0x10) {
    FUN_80003782(auStack_c0,(undefined8 *)(param_1 + uVar1),0x10);
    aes((int)auStack_c0,(int)key);
    FUN_80003782((undefined8 *)(param_3 + uVar1),auStack_c0,0x10);
  }
  return;
}


void AES_ENCRYPT(int param_1,int param_2,int param_3,uint param_4)

{
  uint uVar1;
  undefined8 auStack_c0 [2];
  undefined key [176];
  
  FUN_800002b0(param_2,(int)key);
  for (uVar1 = 0; uVar1 < param_4; uVar1 += 0x10) {
    FUN_80003782(auStack_c0,(undefined8 *)(param_1 + uVar1),0x10);
    aes((int)auStack_c0,(int)key);
    FUN_80003782((undefined8 *)(param_3 + uVar1),auStack_c0,0x10);
  }
  return;
}

写出EXP。

from regadgets import *
enc = bytes.fromhex('63 D4 DD 72 B0 8C AE 31 8C 33 03 22 03 1C E4 D3 C3 E3 54 B2 1D EB EB 9D 45 B1 BE 86 CD E9 93 D8')
print(len(enc))
key = [ 0x2e, 0x35, 0x7d, 0x6a, 0xed, 0x44, 0xf3, 0x4d, 0xad, 0xb9, 0x11, 0x34, 0x13, 0xea, 0x32, 0x4e ]
enc = list(enc)
for i in range(len(enc)):
    enc[31-i] ^= 0xff
    enc[31-i] ^= enc[(32-i) % 32]
    enc[31-i] = ror8(enc[31-i], 3)

aes = AES(key)
dec = aes.decrypt_ecb_block(enc[:16]) + aes.decrypt_ecb_block(enc[16:])
print(dec) # b'SCTF{Wlc_t0_the_wd_oF_IOT_s3cur}'

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'

[Week1] Ez Xor

这里是加密后的东西,由于是局部变量,直接变成一坨了,我们把它弄出来就行,或者直接动态调试,从hex-view里面找,也是很方便的。
2024-09-22T12:16:13.png

from regadgets import *

# data
enc = qword2byte([0x1D0B2D2625050901, 0x673D491E20317A24, 0x34056E2E2508504D])
enc += b"\"@;%"
assert len(enc) == 28

# keystream
k = b'Xor'
keyStream = bytes([i ^ k[i % len(k)] for i in range(len(enc))])

# xor
print(bxor(enc, keyStream[::-1]))
# b'BaseCTF{X0R_I5_345Y_F0r_y0U}'

[Week1] You are good at IDA

2024-09-22T12:22:51.png
Y0u_4Re_
2024-09-22T12:23:41.png
900d_47_
2024-09-22T12:24:18.png
id4
BaseCTF{Y0u_4Re_900d_47_id4}

[Week1] BasePlus

2024-09-22T12:26:23.png
Encode 里面经过分析是一个Base64 encode
Secret是/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC
也就是一个换表Base64,不过base64里面偷偷给你异或了一个0xE,这个要注意

from regadgets import *
enc = b'lvfzBiZiOw7<lhF8dDOfEbmI]i@bdcZfEc^z>aD!'
k = 0xe
dec = decode_b64(bxor(enc, [k for _ in range(len(enc))]).decode(),
                  '/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC')

print(dec)
# b'BaseCTF{BA5e_DEcoD1N6_sEcr3t}\x00'

[Week1] UPX mini

UPX 解压
2024-09-22T12:31:39.png
2024-09-22T12:32:02.png
base64_encode,名字没有清理掉,而且进去是标准base64

from regadgets import *
enc = 'QmFzZUNURntIYXYzX0BfZzBvZF90MW0zISEhfQ=='
dec = decode_b64(enc)
print(dec)
# b'BaseCTF{Hav3_@_g0od_t1m3!!!}'

[Week1] ez_maze

2024-09-22T12:35:53.png
进去之后分析一下,把一些字符还原了,wasd,这几个分别是上左下右,看起来是没问题的
显然迷宫是用一个数组进行存储,pos % 15 == 14可以知道迷宫行宽15(因为pos从0开始), pos > 209可知道迷宫总共210个数,也就是15*14的迷宫
2024-09-22T12:38:29.png
然后如果是y就是终点,如果是$就是invalid(墙壁,不能走)。
我们提取出来,看一看

>>> a = "x$$$$$$$$$$$$$$&&&&&&$$$$$$$$$&$&$$&$$&&&&&$$&$&$$$&&$$$$&$$&$$$&&&$$$$$&$$&$$$&$&&$&$$$$$&$$$&$&$$&&&$$$&&&&&$&&&&$&$$$$$$$$$&&&&&&$$$$$$$$$&$$$$$$$$$$$&&&&$$&&&$$$$$$&&&&&&&$$$$$$$$$$$$$$&$$&$$$$$$$$$$$&$&$$$$$$$$$&&&&&&&&y"
>>>
>>> for i in range(0, len(a), 15):
...     print(a[i:i+15])
...
x$$$$$$$$$$$$$$
&&&&&&$$$$$$$$$
&$&$$&$$&&&&&$$
&$&$$$&&$$$$&$$
&$$$&&&$$$$$&$$
&$$$&$&&$&$$$$$
&$$$&$&$$&&&$$$
&&&&&$&&&&$&$$$
$$$$$$&&&&&&$$$
$$$$$$&$$$$$$$$
$$$&&&&$$&&&$$$
$$$&&&&&&&$$$$$
$$$$$$$$$&$$&$$
$$$$$$$$$&$&$$$
$$$$$$&&&&&&&&y
>>>

然后使用notepad++进行标记,手动走一下
2024-09-22T12:40:40.png
sssssssddddwwwddsssssssdddsssddddd
然后程序提示,flag是BaseCTF{md5(path)}
所以

from hashlib import md5
print(f"BaseCTF{{{md5(b'sssssssddddwwwddsssssssdddsssddddd').hexdigest()}}}")
# BaseCTF{131b7d6e60e8a34cb01801ae8de07efe}

[Week2] Ezpy

┌──(Administrator💀NullCafe)-[ base]-[~/Desktop/BaseCTF2024/Reverse/[Week2] Ezpy]                               0ms  
└─#  python .\pyinstxtractor.py .\Ezpy.exe
[+] Processing .\Ezpy.exe
[+] Pyinstaller version: 2.1+
[+] Python version: 3.9
[+] Length of package: 5835441 bytes
[+] Found 59 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: Ezpy.pyc
[!] Warning: This script is running in a different Python version than the one used to build the executable.
[!] Please run this script in Python 3.9 to prevent extraction errors during unmarshalling
[!] Skipping pyz extraction
[+] Found 81 files in PYZ archive
[+] Successfully extracted pyinstaller archive: .\Ezpy.exe

注意到

[!] Warning: This script is running in a different Python version than the one used to build the executable.
[!] Please run this script in Python 3.9 to prevent extraction errors during unmarshalling
[!] Skipping pyz extraction

这个其实是因为你python版本不对导致的,我们可以修改pyinstxtractor.py让它不跳过一些pyc的提取,也可以换python版本,我们这里为了反编译更完美,选择更换python版本。
我是用conda进行多版本python环境共存。
2024-09-22T12:49:30.png
可以注意到,是python3.9.7,所以我们安装
conda create -n py397 python=3.9.7
然后切换
conda activate py397

┌──(Administrator💀NullCafe)-[ py397]-[~/Desktop/BaseCTF2024/Reverse/[Week2] Ezpy]                             37ms  
└─#  python .\pyinstxtractor.py .\Ezpy.exe
[+] Processing .\Ezpy.exe
[+] Pyinstaller version: 2.1+
[+] Python version: 3.9
[+] Length of package: 5835441 bytes
[+] Found 59 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: Ezpy.pyc
[+] Found 81 files in PYZ archive
[+] Successfully extracted pyinstaller archive: .\Ezpy.exe

You can now use a python decompiler on the pyc files within the extracted directory

这下就完美了,我们使用pycdc来反编译
2024-09-22T12:51:53.png

# Source Generated with Decompyle++
# File: Ezpy.pyc (Python 3.9)

import Key
import sys

def init_Sbox(seed):
    k_b = (lambda .0 = None: [ ord(seed[i % len(seed)]) for i in .0 ])(range(256))
    s = list(range(256))
    j = 0
    for i in range(256):
        j = (j + s[i] + k_b[i]) % 256
        s[i] = s[j]
        s[j] = s[i]
    return s


def KeyStream(text, Sbox):
    s = Sbox.copy()
    (i, j) = (0, 0)
    k = [
        0] * len(text)
    for r in range(len(text)):
        i = (i + 1) % 256
        j = (j + s[i]) % 256
        s[i] = s[j]
        s[j] = s[i]
        t = (s[i] + s[j]) % 256
        k[r] = s[t] ^ Key.keykey[r % len(Key.keykey)]
    return k


def Encrypt(text, seed):
    Sbox = init_Sbox(seed)
    key = KeyStream(text, Sbox)
    enc = (lambda .0 = None: [ text[i] ^ key[i] for i in .0 ])(range(len(text)))
    return bytes(enc)

enc = b'\xe6\xaeC~F\xf2\xe3\xbb\xac\x9a-\x02U\x85p\xeb\x19\xd1\xe4\xc93sG\xb0\xeb1\xb5\x05\x05\xc3\xd7\x00\x18+D\xbc\x0cO\x9em\xf1\xbd'
flag = input('Please input Your flag:')
flag = (lambda .0: [ ord(i) for i in .0 ])(flag)
flag = Encrypt(flag, Key.key)
if flag != enc:
    print("It's not flag!")
    continue
print('You are right!')
sys.exit(1)
continue

可以注意到,init_Sbox是RC4的init,KeyStream是在获取RC4的密钥流。
不过我们还需要知道Key.key,keykey这俩是什么
我们在子目录下找到Key.pyc,进行反编译

└─# C:\Users\Administrator\Desktop\Reverse\Python\pycdc\pycdc.exe .\Key.pyc
# Source Generated with Decompyle++
# File: Key.pyc (Python 3.9)

key = 'yOU_f1nd_m3'
keykey = [
    66,
    97,
    115,
    101]

exp

from regadgets import *
key = 'yOU_f1nd_m3'
keykey = [
    66,
    97,
    115,
    101]
enc = b'\xe6\xaeC~F\xf2\xe3\xbb\xac\x9a-\x02U\x85p\xeb\x19\xd1\xe4\xc93sG\xb0\xeb1\xb5\x05\x05\xc3\xd7\x00\x18+D\xbc\x0cO\x9em\xf1\xbd'

s = rc4_init(key.encode())
keystream = list(rc4_keystream(s, len(enc)))
keystream = bxor_cycle(keystream, keykey)
print(bxor(keystream, enc))
# b'BaseCTF{Y0u_kn0W_d3C0Mp1l4710N_PY_4ND_rC4}'

不过这里我推荐使用黑手办法
我们使用hypno进行注入

>>> from hypno import *
>>> code = """
... Sbox = init_Sbox(Key.key)
... keys = KeyStream("a"*len(enc), Sbox)
... print(keys)
... """
>>>
>>> inject_py(9024, code)

2024-09-22T13:14:58.png
然后就能得到KeyStream,注入原理是,attach到Python39.dll然后

void run_python_code() {
    if (PYTHON_CODE[0]) {
        int saved_errno = errno;
        PyGILState_STATE gstate = PyGILState_Ensure();
        PyRun_SimpleString(PYTHON_CODE);
        PyGILState_Release(gstate);
        errno = saved_errno;
    }
}

通过调用PyRun_SimpleString就可以执行代码注入。
然后我们继续
exp

from regadgets import *
enc = b'\xe6\xaeC~F\xf2\xe3\xbb\xac\x9a-\x02U\x85p\xeb\x19\xd1\xe4\xc93sG\xb0\xeb1\xb5\x05\x05\xc3\xd7\x00\x18+D\xbc\x0cO\x9em\xf1\xbd'
box = [164, 207, 48, 27, 5, 166, 165, 192, 245, 170, 88, 93, 62, 235, 64, 188, 70, 181, 215, 138, 3, 62, 55, 129, 135, 5, 130, 52, 53, 141, 136, 80, 65, 116, 112, 242, 72, 16, 236, 46, 197, 192]
print(bxor(enc, box))
# b'BaseCTF{Y0u_kn0W_d3C0Mp1l4710N_PY_4ND_rC4}'

注入应该是解决这种类型题目的最优雅的办法。

[Week2] RivestCipher

题目名字明示是rc4,但是我们还是看一看
2024-09-22T13:45:17.png
main_main,是go编译的产物
由于ida对go的字符串支持不太行(因为go的字符串和rust的一样,都是连着的而不是x00截断)
我们直接看流程图
2024-09-22T13:46:35.png
可以看到这里,mov ecx, 7代表后面的字符串长度7
所以字符串是BaseCTF
2024-09-22T13:48:44.png
下面直接进rc4加密函数了,所以大概率是key
2024-09-22T13:51:09.png
很清晰,所以我就直接写wp了(enc是后面的hex字符串 可以直接找到)

from regadgets import *
enc = bytes.fromhex('0ebb0c573dd548f3d2b2525f02895cd5275b6f6e5776538982ea41246dae8517')
s = rc4_init(b'BaseCTF')
print(rc4_crypt(s, enc))
# b'BaseCTF{go1@ng_!S_RuNNin9_RC4}\r\n'

[Week2] UPX

x64dbg 零帧起手,找到个

$rg7_dhd~Alidg+zeyhz`vnz_d,7sy0=

这时候我就已经猜到是魔改base64了,但是还是继续看看
2024-09-22T14:07:37.png
果然找到base64表(& 0x3f)
2024-09-22T14:08:47.png

A,.1fgvw#`/2ehux$~"3dity%_;4cjsz^+{5bkrA&=}6alqB*-[70mpC()]89noX
from regadgets import *
print(decode_b64('$rg7_dhd~Alidg+zeyhz`vnz_d,7sy0=', 'A,.1fgvw#`/2ehux$~"3dity%_;4cjsz^+{5bkrA&=}6alqB*-[70mpC()]89noX'))
# b'BaseCTF{UPX_1s_$o_e@sy}'

好吧,已经解出来了wmmm

[Week2] lk

z3基础题,直接搬运官方wp了(bushi

from z3 import *

a = [Int('a[%d]' % i) for i in range(21)]
s = Solver()
s.add(948 * a[20]
     + 887 * a[19]
     + 410 * a[18]
     + 978 * a[17]
     + 417 * a[16]
     + 908 * a[15]
     + 965 * a[14]
     + 987 * a[13]
     + 141 * a[12]
     + 257 * a[11]
     + 323 * a[10]
     + 931 * a[9]
     + 773 * a[8]
     + 851 * a[7]
     + 758 * a[6]
     + 891 * a[5]
     + 575 * a[4]
     + 616 * a[3]
     + 860 * a[2]
     + 283 * a[1] == 913686)
s.add( 938 * a[20]
     + 490 * a[19]
     + 920 * a[18]
     + 50 * a[17]
     + 568 * a[16]
     + 68 * a[15]
     + 35 * a[14]
     + 708 * a[13]
     + 938 * a[12]
     + 718 * a[11]
     + 589 * a[10]
     + 954 * a[9]
     + 974 * a[8]
     + 62 * a[7]
     + 580 * a[6]
     + 80 * a[5]
     + 111 * a[4]
     + 151 * a[3]
     + 421 * a[2]
     + 148 * a[1] == 630335)
s.add( 908 * a[20]
     + 590 * a[19]
     + 668 * a[18]
     + 222 * a[17]
     + 489 * a[16]
     + 335 * a[15]
     + 778 * a[14]
     + 622 * a[13]
     + 95 * a[12]
     + 920 * a[11]
     + 932 * a[10]
     + 892 * a[9]
     + 409 * a[8]
     + 392 * a[7]
     + 11 * a[6]
     + 113 * a[5]
     + 948 * a[4]
     + 674 * a[3]
     + 506 * a[2]
     + 182 * a[1] == 707525)
s.add( 479 * a[20]
     + 859 * a[19]
     + 410 * a[18]
     + 399 * a[17]
     + 891 * a[16]
     + 266 * a[15]
     + 773 * a[14]
     + 624 * a[13]
     + 34 * a[12]
     + 479 * a[11]
     + 465 * a[10]
     + 728 * a[9]
     + 447 * a[8]
     + 427 * a[7]
     + 890 * a[6]
     + 570 * a[5]
     + 716 * a[4]
     + 180 * a[3]
     + 571 * a[2]
     + 707 * a[1] == 724203)
s.add( 556 * a[20]
     + 798 * a[19]
     + 380 * a[18]
     + 716 * a[17]
     + 71 * a[16]
     + 901 * a[15]
     + 949 * a[14]
     + 304 * a[13]
     + 142 * a[12]
     + 679 * a[11]
     + 459 * a[10]
     + 814 * a[9]
     + 282 * a[8]
     + 49 * a[7]
     + 873 * a[6]
     + 169 * a[5]
     + 437 * a[4]
     + 199 * a[3]
     + 771 * a[2]
     + 807 * a[1] == 688899)
s.add( 465 * a[20]
     + 898 * a[19]
     + 979 * a[18]
     + 198 * a[17]
     + 156 * a[16]
     + 831 * a[15]
     + 856 * a[14]
     + 322 * a[13]
     + 25 * a[12]
     + 35 * a[11]
     + 369 * a[10]
     + 917 * a[9]
     + 522 * a[8]
     + 654 * a[7]
     + 235 * a[6]
     + 385 * a[5]
     + 469 * a[4]
     + 231 * a[3]
     + 496 * a[2]
     + 83 * a[1] == 604784)
s.add( 305 * a[20]
     + 928 * a[19]
     + 260 * a[18]
     + 793 * a[17]
     + 787 * a[16]
     + 708 * a[15]
     + 758 * a[14]
     + 236 * a[13]
     + 688 * a[12]
     + 747 * a[11]
     + 711 * a[10]
     + 195 * a[9]
     + 50 * a[8]
     + 648 * a[7]
     + 787 * a[6]
     + 376 * a[5]
     + 220 * a[4]
     + 33 * a[3]
     + 194 * a[2]
     + 585 * a[1] == 665485)
s.add( 767 * a[20]
     + 573 * a[19]
     + 22 * a[18]
     + 909 * a[17]
     + 598 * a[16]
     + 588 * a[15]
     + 136 * a[14]
     + 848 * a[12]
     + 964 * a[11]
     + 311 * a[10]
     + 701 * a[9]
     + 653 * a[8]
     + 541 * a[7]
     + 443 * a[6]
     + 7 * a[5]
     + 976 * a[4]
     + 803 * a[3]
     + 273 * a[2]
     + 859 * a[1] == 727664)
s.add( 776 * a[20]
     + 59 * a[19]
     + 507 * a[18]
     + 164 * a[17]
     + 397 * a[16]
     + 744 * a[15]
     + 377 * a[14]
     + 768 * a[13]
     + 456 * a[12]
     + 799 * a[11]
     + 9 * a[10]
     + 215 * a[9]
     + 365 * a[8]
     + 181 * a[7]
     + 634 * a[6]
     + 818 * a[5]
     + 81 * a[4]
     + 236 * a[3]
     + 883 * a[2]
     + 95 * a[1] == 572015)
s.add( 873 * a[20]
     + 234 * a[19]
     + 381 * a[18]
     + 423 * a[17]
     + 960 * a[16]
     + 689 * a[15]
     + 617 * a[14]
     + 240 * a[13]
     + 933 * a[12]
     + 300 * a[11]
     + 998 * a[10]
     + 773 * a[9]
     + 484 * a[8]
     + 905 * a[7]
     + 806 * a[6]
     + 792 * a[5]
     + 606 * a[4]
     + 942 * a[3]
     + 422 * a[2]
     + 789 * a[1] == 875498)
s.add( 766 * a[20]
     + 7 * a[19]
     + 283 * a[18]
     + 900 * a[17]
     + 211 * a[16]
     + 305 * a[15]
     + 343 * a[14]
     + 696 * a[13]
     + 590 * a[12]
     + 736 * a[11]
     + 817 * a[10]
     + 603 * a[9]
     + 414 * a[8]
     + 828 * a[7]
     + 114 * a[6]
     + 845 * a[5]
     + 175 * a[4]
     + 212 * a[3]
     + 898 * a[2]
     + 988 * a[1] == 714759)
s.add( 220 * a[20]
     + 30 * a[19]
     + 788 * a[18]
     + 106 * a[17]
     + 574 * a[16]
     + 501 * a[15]
     + 366 * a[14]
     + 952 * a[13]
     + 121 * a[12]
     + 996 * a[11]
     + 735 * a[10]
     + 689 * a[9]
     + 998 * a[8]
     + 689 * a[7]
     + 729 * a[6]
     + 886 * a[5]
     + 860 * a[4]
     + 70 * a[3]
     + 466 * a[2]
     + 961 * a[1] == 778853)
s.add( 313 * a[20]
     + 748 * a[19]
     + 522 * a[18]
     + 864 * a[17]
     + 156 * a[16]
     + 362 * a[15]
     + 283 * a[14]
     + 49 * a[13]
     + 316 * a[12]
     + 79 * a[11]
     + 136 * a[10]
     + 299 * a[9]
     + 271 * a[8]
     + 604 * a[7]
     + 907 * a[6]
     + 540 * a[5]
     + 141 * a[4]
     + 620 * a[3]
     + 701 * a[2]
     + 866 * a[1] == 584591)
s.add( 922 * a[20]
     + 399 * a[19]
     + 425 * a[18]
     + 26 * a[17]
     + 159 * a[16]
     + 224 * a[15]
     + 438 * a[14]
     + 770 * a[13]
     + 144 * a[12]
     + 406 * a[11]
     + 110 * a[10]
     + 991 * a[9]
     + 749 * a[8]
     + 701 * a[7]
     + 646 * a[6]
     + 147 * a[5]
     + 979 * a[4]
     + 674 * a[3]
     + 999 * a[2]
     + 913 * a[1] == 717586)
s.add( 13 * a[20]
     + 537 * a[19]
     + 225 * a[18]
     + 421 * a[17]
     + 153 * a[16]
     + 484 * a[15]
     + 654 * a[14]
     + 743 * a[13]
     + 779 * a[12]
     + 74 * a[11]
     + 325 * a[10]
     + 439 * a[9]
     + 797 * a[8]
     + 41 * a[7]
     + 784 * a[6]
     + 269 * a[5]
     + 454 * a[4]
     + 725 * a[2]
     + 164 * a[1] == 537823)
s.add( 591 * a[20]
     + 210 * a[19]
     + 874 * a[18]
     + 204 * a[17]
     + 485 * a[16]
     + 42 * a[15]
     + 433 * a[14]
     + 176 * a[13]
     + 436 * a[12]
     + 634 * a[11]
     + 82 * a[10]
     + 978 * a[9]
     + 818 * a[8]
     + 683 * a[7]
     + 404 * a[6]
     + 562 * a[5]
     + 41 * a[4]
     + 789 * a[3]
     + 200 * a[2]
     + 220 * a[1] == 587367)
s.add( 584 * a[20]
     + 597 * a[19]
     + 928 * a[18]
     + 532 * a[17]
     + 902 * a[16]
     + 858 * a[15]
     + 820 * a[14]
     + 240 * a[13]
     + 124 * a[12]
     + 899 * a[11]
     + 848 * a[10]
     + 822 * a[9]
     + 409 * a[8]
     + 491 * a[7]
     + 587 * a[6]
     + 715 * a[5]
     + 410 * a[4]
     + 268 * a[3]
     + 721 * a[2]
     + 915 * a[1] == 842245)
s.add( 421 * a[20]
     + 302 * a[19]
     + 327 * a[18]
     + 180 * a[17]
     + a[16] * 512
     + 160 * a[15]
     + 623 * a[14]
     + 28 * a[13]
     + 411 * a[12]
     + 53 * a[11]
     + 633 * a[10]
     + 560 * a[9]
     + 623 * a[8]
     + 477 * a[7]
     + 901 * a[6]
     + 287 * a[5]
     + 149 * a[4]
     + 726 * a[3]
     + 934 * a[2]
     + 875 * a[1] == 610801)
s.add( 838 * a[20]
     + 434 * a[19]
     + 792 * a[18]
     + 649 * a[17]
     + 462 * a[16]
     + 170 * a[15]
     + 980 * a[14]
     + 15 * a[13]
     + 295 * a[12]
     + 495 * a[11]
     + 666 * a[10]
     + 934 * a[9]
     + 17 * a[8]
     + 69 * a[7]
     + 367 * a[6]
     + 780 * a[5]
     + 291 * a[4]
     + 834 * a[3]
     + 587 * a[2]
     + 133 * a[1] == 653127)
s.add( 41 * a[20]
     + 422 * a[19]
     + 420 * a[18]
     + 224 * a[17]
     + 475 * a[16]
     + 854 * a[15]
     + 233 * a[14]
     + 179 * a[13]
     + 620 * a[12]
     + 69 * a[11]
     + 42 * a[10]
     + 684 * a[9]
     + 300 * a[8]
     + 745 * a[7]
     + 894 * a[6]
     + 554 * a[5]
     + 495 * a[4]
     + 66 * a[3]
     + 316 * a[2]
     + 391 * a[1] == 533470 )
if s.check() == sat:
    ans = s.model()
    for i in range(1,21):
        print(chr(ans[a[i]].as_long()), end="")
    #print(s.model())
        
 #CDBBDCAAABBDBCCBCCAC

[Week2] 最简单的编码

2024-09-22T14:16:32.png
动调一下,就能拿到变换后的table

CDABGHEFKLIJOPMNSTQRWXUVabYZefcdijghmnklqropuvstyzwx23016745+/89

然后这是魔改的base64
2024-09-22T14:20:01.png
我是用cpp写的,网上搬的,改了几个偏移,不过年久已丢,此处用官方的

from regadgets import *
table = "CDABGHEFKLIJOPMNSTQRWXUVabYZefcdijghmnklqropuvstyzwx23016745+/89"
enc = "TqK1YUSaQryEMHaLMnWhYU+Fe0WPenqhRXahfkV6WE2fa3iRW197Za62eEaD"
index = []
number = [1,2,3,4]
for i in range(len(enc)):
    tmp = table.index(enc[i]) - number[i % 4]
    if tmp >= 0:
        index.append(tmp)
    else:
        index.append(tmp + 64)
print(index)
for i in range(0,len(index),4):
    sum = index[i] << 18 | index[i + 1] << 12 | index[i + 2] << 6 | index[i + 3]
    for j in range(3):
        print(chr((sum >> ((2 - j) * 8)) & 0xff), end="")
# [16, 38, 5, 51, 25, 20, 13, 20, 17, 39, 45, 2, 13, 3, 21, 5, 13, 35, 17, 31, 25, 20, 57, 3, 27, 52, 17, 9, 27, 35, 37, 31, 18, 19, 21, 31, 28, 36, 20, 52, 19, 4, 49, 25, 23, 51, 29, 15, 19, 53, 60, 53, 26, 22, 53, 48, 27, 4, 21, 61]
# BaseCTF{B45E64_eNCoDIn9_I5_rE4LLY_7OO_5implE}

[Week2] 喝杯下午茶

找key, 找enc,找delta就可以了。

from regadgets import *
k = [0] * 4
k[0] = 287454020
k[1] = 1432778632
k[2] = -1716864052
k[3] = -571539695

v = [0] * 10
v[0] = -1800277529
v[1] = 567661394
v[2] = 1380415805
v[3] = 67968151
v[4] = -210862220
v[5] = -1672218865
v[6] = 1793773528
v[7] = 1872692980
v[8] = -352477650
v[9] = 850810359
v = pack_dword(v)
r = b''
for i in v:
    a, b = tea_decrypt(i, k, 1131796)
    r += l2b(a)[::-1] + l2b(b)[::-1]

print(r)
# b'BaseCTF{h3r3_4_cuP_0f_734_f0R_y0U!!!!!!}'   

[Week2] neuro爱数学

没做,搬一下官方的:
考点是简单的代码审计加代码逻辑仿写
2024-09-22T14:34:48.png
点开IDA发现需要输入9个数
2024-09-22T14:34:55.png
接下来简单分析逻辑
第一段进行了一个简单的运算操作
即计算: $$x_1*i^8+x_2*i^7+x_3*i^6+x_4*i^5+x_5*i^4+x_6*i^3+x_7*i^2+x_8*i^1+x_9*i^0$$
第二段计算(i-44)(i-58)(i-17)(i-6)(i-5)(i+4)(i+9)(i+37)
很多人看不懂,为什么不考虑一下本地模拟呢 or 动调
2024-09-22T14:35:07.png
接下来事情显而易见 将第二段展开,得到的式子和第一段对比即可得到x1-x9
这里用python脚本完成,有能力的同学可以手算。

from sympy import symbols, expand
x = symbols('x')
polynomial = (x - 44) * (x - 58) * (x - 5) * (x + 37) * (x - 17) * (x + 9) * (x - 6) * (x + 4)
expanded_polynomial = expand(polynomial)
standard_form = expanded_polynomial.as_poly()
coefficients = standard_form.all_coeffs()
print(coefficients[::-1])

2024-09-22T14:35:29.png
输入进程序即可得到flag
2024-09-22T14:35:35.png

[Week3] UPX PRO

Linux 的加了upx,而且魔改了一些东西,我们放gdb里面
2024-09-22T14:37:33.png
发现有反调试,我们考虑ptrace反调试
2024-09-22T14:38:01.png
catch syscall ptrace然后s一下(过syscall),再运行
2024-09-22T14:38:24.png
这样我们就能绕过ptrace反调试,然后
2024-09-22T14:38:57.png
就可以绕过反调试,其实在反调试的地方已经过了,我们直接
2024-09-22T14:40:27.png
得到coredump,然后拖入ida
2024-09-22T14:42:41.png
显然我们已经得到了解压后的
随便翻一下,找到个base58,表是
ABCDEFGHJKLMNPQRSTUVWXYZ123456789abcdefghijkmnopqrstuvwxyz
2024-09-22T14:43:37.png
2024-09-22T14:48:32.png

__int64 __fastcall sub_7FFFF7FF9518(__int64 a1, unsigned __int64 a2, __int64 a3, __int64 a4)
{
  _BYTE v5[139]; // [rsp+20h] [rbp-A0h] BYREF
  char v6; // [rsp+ABh] [rbp-15h]
  int v7; // [rsp+ACh] [rbp-14h]
  unsigned __int64 i; // [rsp+B0h] [rbp-10h]
  int v9; // [rsp+B8h] [rbp-8h]
  int v10; // [rsp+BCh] [rbp-4h]

  sub_7FFFF7FF9362(v5, a3, a4);
  v10 = 0;
  v9 = 0;
  v7 = 0;
  for ( i = 0LL; i < a2; ++i )
  {
    v10 = (v10 + 1) % 128;
    v9 = (v9 + (unsigned __int8)v5[v10]) % 128;
    v6 = v5[v10];
    v5[v10] = v5[v9];
    v5[v9] = v6;
    v7 = (v5[v10] + v5[v9]) & 0x7F;
    *(_BYTE *)(a1 + i) ^= v5[v7];
  }
  return 0LL;
}

还有个魔改rc4
稍微还原一下

__int64 __fastcall rc4_init(char *s_box, char *a2, unsigned __int64 len)
{
  char key_box[128]; // [rsp+18h] [rbp-90h] BYREF
  char v5; // [rsp+9Fh] [rbp-9h]
  int v6; // [rsp+A0h] [rbp-8h]
  int i; // [rsp+A4h] [rbp-4h]

  v6 = 0;
  memset(key_box, 0, sizeof(key_box));
  v5 = 0;
  for ( i = 0; i <= 127; ++i )
  {
    s_box[i] = i;
    key_box[i] = key_box[i % len];
  }
  for ( i = 0; i <= 127; ++i )
  {
    v6 = (key_box[i] + v6 + (unsigned __int8)s_box[i]) % 128;
    v5 = s_box[i];                              // swap
    s_box[i] = s_box[v6];
    s_box[v6] = v5;
  }
  return 0LL;
}
__int64 __fastcall rc4_crypt(char *a1, unsigned __int64 a2, char *key, unsigned __int64 key_len)
{
  unsigned __int8 s[128]; // [rsp+20h] [rbp-A0h] BYREF
  unsigned __int8 tmp; // [rsp+ABh] [rbp-15h]
  int v7; // [rsp+ACh] [rbp-14h]
  unsigned __int64 iter; // [rsp+B0h] [rbp-10h]
  int j; // [rsp+B8h] [rbp-8h]
  int i; // [rsp+BCh] [rbp-4h]

  rc4_init((char *)s, key, key_len);
  i = 0;
  j = 0;
  v7 = 0;
  for ( iter = 0LL; iter < a2; ++iter )
  {
    i = (i + 1) % 128;
    j = (j + s[i]) % 128;
    tmp = s[i];                                 // swap
    s[i] = s[j];
    s[j] = tmp;
    v7 = (s[i] + s[j]) & 0x7F;
    a1[iter] ^= s[v7];
  }
  return 0LL;
}

我们发现key根本没用上,rc4的key_box全是0,是空的,可能是出题的时候的一个漏洞。
思路已经很明显了,就是把rc4的sbox空间改成128,就行了。
EXP

from regadgets import *
enc = [0x364A65466C271216, 0x2447243568082139, 0x29323C0F5A1A7D60, 0x4D647C3C45531544, 0x74152339130F7024]
enc = qword2byte(enc)

print(rc4_crypt(rc4_init(b'', 128), enc, 128))
# b'BaseCTF{Rc4_1$_@_G0od_3nCrypt!on_MethOd}'

[Week3] Dont-debug-me

ScyllaHide

[Week4] BaseRE

TLS Callback 和Main里面有花指令
Change base64 table to : HElRNYGmBOMWnbDvUCgcpu1QdPqJIS+iTry39KXse4jLh/x26Ff5Z7Vokt8wzAa0
b64decode('UXY5PpbpCshkP1CrPcU7DlZZSGd5WcO6qRB/D1dfbyZFP3ToncKKd5rXDGCA', my_table);
BaseCTF{8edae458-4tf3-2ph2-9f26-1f8719ec8f8d}

ByteAPK

Flutter rust bridge
ByteCTF{},总长度45
flag被通过ffi传到rust,然后utf8转成c string,去掉了ByteCTF{和后面的},传到一个函数里面check

unsigned char arr1[] =
{
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 
  0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
  0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
  0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 
  0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
  0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 
  0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned int enc[32] = {
    0x0001EE59, 0x0000022A, 0x00001415, 0x00040714, 0x000013E0, 0x000008B8, 0xFFFDCEA0, 0x0000313B, 
    0x0003D798, 0xFFFFFE6B, 0x00000C4E, 0x00023884, 0x0000008D, 0x00001DB4, 0xFFFC1328, 0x00001EAC, 
    0x00043C64, 0x0000142B, 0xFFFFF622, 0x00023941, 0xFFFFEF6D, 0x0000120C, 0xFFFBD30F, 0x00001EBE, 
    0x00045158, 0xFFFFEF66, 0x00001D3F, 0x0004C46B, 0xFFFFF97A, 0x00001BFD, 0xFFFBA235, 0x00001ED2
};

__int64 __fastcall final_enc(unsigned __int8 *flag_intern, __int64 a2)
{
  __int64 v2; // x9
  __int64 v3; // x9
  __int64 v4; // x9
  __int64 v5; // x9
  __int64 v6; // x9
  __int64 v7; // x9
  __int64 v8; // x9
  __int64 v9; // x9
  unsigned int v10; // w11
  int v11; // w13
  __int64 v12; // x9
  __int64 v13; // x9
  __int64 v14; // x9
  __int64 v15; // x9
  __int64 v16; // x9
  unsigned int v18; // w10
  int v19; // w13
  __int64 v20; // x9
  __int64 v21; // x9
  __int64 v22; // x9
  __int64 v23; // x9
  __int64 v24; // x9
  unsigned int v25; // w10
  int v26; // w13
  __int64 v27; // x9
  __int64 v28; // x9
  __int64 v29; // x9
  __int64 v30; // x9
  __int64 v31; // x9
  unsigned int v32; // w8
  int v33; // w11
  unsigned __int64 v34; // x20
  __int64 v35; // x21
  unsigned int v36; // w22
  int v37; // w8
  __int64 v38; // x10
  int v39; // w9
  unsigned __int64 v40; // x8
  __int64 v41; // x9
  unsigned __int64 v42; // x10
  unsigned __int64 v43; // x8
  int v44; // w12
  int v45; // w14
  int v46; // w3
  int v47; // w13
  int v48; // w2
  int v49; // w16
  int v50; // w15
  int v51; // w17
  int v52; // w6
  int v53; // w4
  int v54; // w12
  int *v55; // x13
  __int64 v57; // [xsp+8h] [xbp-48h] BYREF
  __int64 v58; // [xsp+10h] [xbp-40h]
  unsigned __int64 v59; // [xsp+18h] [xbp-38h]

  v57 = 0LL;
  v58 = 4LL;
  if ( a2 != 36 )
    return 0LL;
  v2 = arr1[*flag_intern];
  if ( (_DWORD)v2 == 36 )
    return 0LL;
  v3 = arr1[flag_intern[v2]] + v2;
  if ( v3 == 36 )
    return 0LL;
  v4 = v3 + arr1[flag_intern[v3]];
  if ( v4 == 36 )
    return 0LL;
  v5 = v4 + arr1[flag_intern[v4]];
  if ( v5 == 36 )
    return 0LL;
  v6 = v5 + arr1[flag_intern[v5]];
  if ( v6 == 36 )
    return 0LL;
  v7 = v6 + arr1[flag_intern[v6]];
  if ( v7 == 36 )
    return 0LL;
  v8 = v7 + arr1[flag_intern[v7]];
  if ( v8 == 36 )
    return 0LL;
  v9 = v8 + arr1[flag_intern[v8]];
  if ( v9 == 36 )
    return 0LL;
  v10 = flag_intern[v9];
  if ( (char)flag_intern[v9] < 0 )
  {
    if ( v10 < 0xE0 )
    {
      v10 = flag_intern[v9 + 1] & 0x3F | ((v10 & 0x1F) << 6);
    }
    else
    {
      v11 = flag_intern[v9 + 2] & 0x3F | ((flag_intern[v9 + 1] & 0x3F) << 6);
      if ( v10 < 0xF0 )
        v10 = v11 | ((v10 & 0x1F) << 12);
      else
        v10 = flag_intern[v9 + 3] & 0x3F | (v11 << 6) & 0xFFE3FFFF | ((v10 & 7) << 18);
    }
  }
  if ( v10 != 45 )
    return 0LL;
  v12 = v9 + arr1[flag_intern[v9]];
  if ( v12 == 36 )
    return 0LL;
  v13 = v12 + arr1[flag_intern[v12]];
  if ( v13 == 36 )
    return 0LL;
  v14 = v13 + arr1[flag_intern[v13]];
  if ( v14 == 36 )
    return 0LL;
  v15 = v14 + arr1[flag_intern[v14]];
  if ( v15 == 36 )
    return 0LL;
  v16 = v15 + arr1[flag_intern[v15]];
  if ( v16 == 36 )
    return 0LL;
  v18 = flag_intern[v16];
  if ( (char)flag_intern[v16] < 0 )
  {
    if ( v18 < 0xE0 )
    {
      v18 = flag_intern[v16 + 1] & 0x3F | ((v18 & 0x1F) << 6);
    }
    else
    {
      v19 = flag_intern[v16 + 2] & 0x3F | ((flag_intern[v16 + 1] & 0x3F) << 6);
      if ( v18 < 0xF0 )
        v18 = v19 | ((v18 & 0x1F) << 12);
      else
        v18 = flag_intern[v16 + 3] & 0x3F | (v19 << 6) & 0xFFE3FFFF | ((v18 & 7) << 18);
    }
  }
  if ( v18 != 45 )
    return 0LL;
  v20 = v16 + arr1[flag_intern[v16]];
  if ( v20 == 36 )
    return 0LL;
  v21 = v20 + arr1[flag_intern[v20]];
  if ( v21 == 36 )
    return 0LL;
  v22 = v21 + arr1[flag_intern[v21]];
  if ( v22 == 36 )
    return 0LL;
  v23 = v22 + arr1[flag_intern[v22]];
  if ( v23 == 36 )
    return 0LL;
  v24 = v23 + arr1[flag_intern[v23]];
  if ( v24 == 36 )
    return 0LL;
  v25 = flag_intern[v24];
  if ( (char)flag_intern[v24] < 0 )
  {
    if ( v25 < 0xE0 )
    {
      v25 = flag_intern[v24 + 1] & 0x3F | ((v25 & 0x1F) << 6);
    }
    else
    {
      v26 = flag_intern[v24 + 2] & 0x3F | ((flag_intern[v24 + 1] & 0x3F) << 6);
      if ( v25 < 0xF0 )
        v25 = v26 | ((v25 & 0x1F) << 12);
      else
        v25 = flag_intern[v24 + 3] & 0x3F | (v26 << 6) & 0xFFE3FFFF | ((v25 & 7) << 18);
    }
  }
  if ( v25 != 45 )
    return 0LL;
  v27 = v24 + arr1[flag_intern[v24]];
  if ( v27 == 36 )
    return 0LL;
  v28 = v27 + arr1[flag_intern[v27]];
  if ( v28 == 36 )
    return 0LL;
  v29 = v28 + arr1[flag_intern[v28]];
  if ( v29 == 36 )
    return 0LL;
  v30 = v29 + arr1[flag_intern[v29]];
  if ( v30 == 36 )
    return 0LL;
  v31 = v30 + arr1[flag_intern[v30]];
  if ( v31 == 36 )
    return 0LL;
  v32 = flag_intern[v31];
  if ( (char)flag_intern[v31] < 0 )
  {
    if ( v32 < 0xE0 )
    {
      v32 = flag_intern[v31 + 1] & 0x3F | ((v32 & 0x1F) << 6);
    }
    else
    {
      v33 = flag_intern[v31 + 2] & 0x3F | ((flag_intern[v31 + 1] & 0x3F) << 6);
      if ( v32 < 0xF0 )
        v32 = v33 | ((v32 & 0x1F) << 12);
      else
        v32 = flag_intern[v31 + 3] & 0x3F | (v33 << 6) & 0xFFE3FFFF | ((v32 & 7) << 18);
    }
  }
  if ( v32 != 45 )
    return 0LL;
  v34 = 0LL;
  v35 = 0LL;
  while ( 1 )
  {
    v36 = flag_intern[v35];
    if ( (char)flag_intern[v35] < 0 )
    {
      v37 = flag_intern[v35 + 1] & 0x3F;
      if ( v36 < 0xE0 )
      {
        v35 += 2LL;
        v36 = v37 & 0xFFFFF83F | ((v36 & 0x1F) << 6);
      }
      else
      {
        v38 = v35 + 3;
        v39 = flag_intern[v35 + 2] & 0x3F | (v37 << 6);
        if ( v36 < 0xF0 )
        {
          v36 = v39 | ((v36 & 0x1F) << 12);
          v35 += 3LL;
        }
        else
        {
          v35 += 4LL;
          v36 = flag_intern[v38] & 0x3F | (v39 << 6) & 0xFFE3FFFF | ((v36 & 7) << 18);
        }
      }
    }
    else
    {
      ++v35;
    }
    if ( v36 != 45 )
      break;
LABEL_55:
    if ( v35 == 36 )
      goto LABEL_67;
  }
  if ( v36 != 0x110000 )
  {
    if ( v34 == v57 )
      sub_3B688(&v57, a2);
    *(_DWORD *)(v58 + 4 * v34++) = v36;
    v59 = v34;
    goto LABEL_55;
  }
LABEL_67:
  v40 = v34 >> 3;
  v41 = 0LL;
  if ( (v34 & 7) != 0 )
    ++v40;
  v42 = v40 + 1;
  v43 = 8LL;
  while ( --v42 )
  {
    if ( v43 > v34 )
      goto LABEL_88;
    if ( v43 - 8 >= 0x19 )
    {
      v43 = 40LL;
      v34 = 32LL;
LABEL_88:
      panic(v43, v34, (__int64)&off_801E0);
    }
    v45 = *(_DWORD *)(v58 + v41 * 4 + 8);
    v44 = *(_DWORD *)(v58 + v41 * 4 + 12);
    v46 = *(_DWORD *)(v58 + v41 * 4);
    v47 = *(_DWORD *)(v58 + v41 * 4 + 4);
    v48 = *(_DWORD *)(v58 + v41 * 4 + 16);
    v49 = *(_DWORD *)(v58 + v41 * 4 + 20);
    v50 = *(_DWORD *)(v58 + v41 * 4 + 24);
    v51 = *(_DWORD *)(v58 + v41 * 4 + 28);
    if ( v51 + v47 * v44 * v49 - (v46 + v50 + v45 * v48) == enc[v41] )
    {
      v52 = v46 * v49;
      if ( v44 - v48 - v46 * v49 + v51 * v47 + v45 + v50 == enc[v41 + 1]
        && v52 - (v48 + v51 * v47) + v45 + v50 * v44 == enc[v41 + 2] )
      {
        v53 = v48 * v46;
        if ( v47 + v48 * v46 - (v51 + v45) + v50 * v49 * v44 == enc[v41 + 3]
          && v49 * v44 + v47 + v45 * v48 - (v50 + v51 * v46) == enc[v41 + 4]
          && v52 + v47 * v44 + v45 - (v50 + v48 * v51) == enc[v41 + 5]
          && v51 - v47 + v45 * v49 + v50 - v53 * v44 == enc[v41 + 6] )
        {
          v43 += 8LL;
          v54 = v44 - v51 - (v47 + v49);
          v55 = &enc[v41];
          v41 += 8LL;
          if ( v54 + v53 + v50 * v45 == v55[7] )
            continue;
        }
      }
    }
    if ( v57 )
      c_free(v58, 4 * v57, 4LL);
    return 0LL;
  }
  if ( v57 )
    c_free(v58, 4 * v57, 4LL);
  return 1LL;
}

通过观察,注意到前面的一堆是在检验是否是UUID,后面则是一个标准的z3题目。
exp

from regadgets import *
from z3 import *

enc = [
    0x0001EE59, 0x0000022A, 0x00001415, 0x00040714, 0x000013E0, 0x000008B8, 0xFFFDCEA0, 0x0000313B, 
    0x0003D798, 0xFFFFFE6B, 0x00000C4E, 0x00023884, 0x0000008D, 0x00001DB4, 0xFFFC1328, 0x00001EAC, 
    0x00043C64, 0x0000142B, 0xFFFFF622, 0x00023941, 0xFFFFEF6D, 0x0000120C, 0xFFFBD30F, 0x00001EBE, 
    0x00045158, 0xFFFFEF66, 0x00001D3F, 0x0004C46B, 0xFFFFF97A, 0x00001BFD, 0xFFFBA235, 0x00001ED2
]

for j in range(4):
    x = [BitVec(f"x{i+1}", 32) for i in range(8)]
    x1, x2, x3, x4, x5, x6, x7, x8 = tuple(i for i in x)
    s = Solver()
    for i in x:
        s.add(i > 0x20, i < 0x80)
    s.add(x8 + x2 * x4 * x6 - (x1 + x7 + x3 * x5) == enc[0+j*8])
    v52 = x1 * x6
    s.add(x4 - x5 - x1 * x6 + x8 * x2 + x3 + x7 == enc[1+j*8])
    s.add(v52 - (x5 + x8 * x2) + x3 + x7 * x4 == enc[2+j*8])
    v53 = x5 * x1
    s.add(x2 + x5 * x1 - (x8 + x3) + x7 * x6 * x4 == enc[3+j*8])
    s.add(x6 * x4 + x2 + x3 * x5 - (x7 + x8 * x1) == enc[4+j*8])
    s.add(v52 + x2 * x4 + x3 - (x7 + x5 * x8) == enc[5+j*8])
    s.add(x8 - x2 + x3 * x6 + x7 - v53 * x4 == enc[6+j*8])
    v54 = x4 - x8 - (x2 + x6)
    s.add(v54 + v53 + x7 * x3 == enc[7+j*8])
    
    if s.check() == sat:
        m = s.model()
        for i in x:
            l = m[i].as_long()
            print(l2b(l).decode(),end='')
            # 32e750c8fb214562af22973fb5176b9c
            # ByteCTF{32e750c8-fb21-4562-af22-973fb5176b9c}