京麒CTF2025 热身赛 RE

admin 2025年5月29日15:29:39评论16 views字数 17368阅读57分53秒阅读模式

扫码加圈子

获内部资料

京麒CTF2025 热身赛 RE
京麒CTF2025 热身赛 RE

网络安全领域各种资源,EDUSRC证书站挖掘、红蓝攻防、渗透测试等优质文章,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。加内部圈子,文末有彩蛋(知识星球优惠卷)。

文章作者:1484321987948696

文章来源:https://xz.aliyun.com/news/18040

京麒CTF2025 热身赛 RE
0x1 RE 1

rust逆向,符号表也被删了,但是没管那么多,直接动调跟,看看加密逻辑在哪

__int64 sub_7FF7F63A1100()
{
  unsigned __int64 flag_len; // rdx
  size_t n0x41_2; // r15
  void *Src_1; // r12
  char *Src_4; // rdx
  size_t Size; // rbx
  char *v5; // r9
  char *Src_2; // r8
  char v7; // r11
  char *Src_3; // rsi
  char *v9; // rax
  char n0xF0; // r8
  unsigned int n0x80; // r11d
  int v12; // r9d
  int v13; // edi
  int v14; // r11d
  unsigned int n48; // esi
  __int64 flag2; // r14
  char v17; // bl
  char *Src_6; // r11
  unsigned int n32; // esi
  char v20; // di
  char v21; // bl
  int v22; // ebx
  int v23; // edi
  unsigned int n22; // edi
  char *Src_5; // rdx
  char *Src_8; // rdi
  __int64 v27; // rsi
  __int64 Src_10; // rax
  unsigned __int16 v29; // di
  __int64 Buf1_1; // rax
  _BYTE *Buf1; // rsi
  __int64 i; // rax
  unsigned int v33; // et0
  unsigned __int8 v34; // cl
  unsigned __int8 v35; // cl
  _OWORD flag[4]; // [rsp+30h] [rbp-50h] BYREF
  __int64 v38; // [rsp+78h] [rbp-8h] BYREF
  char **_err_n_; // [rsp+80h] [rbp+0h] BYREF
  void *Src; // [rsp+88h] [rbp+8h]
  __int64 n8; // [rsp+90h] [rbp+10h]
  __int128 v42; // [rsp+98h] [rbp+18h]
  __int64 Src_7; // [rsp+B0h] [rbp+30h]
  size_t Size_1; // [rsp+B8h] [rbp+38h]
  int v45; // [rsp+C4h] [rbp+44h]
  __int64 v46; // [rsp+C8h] [rbp+48h]

  v46 = -2;
  memset(flag, 0, sizeof(flag));
  v38 = sub_7FF7F63A4DF0();
  if ( (sub_7FF7F63A4E20(&v38, flag, 64) & 1) != 0 )// scanf
  {
    _err_n_ = flag_len;
    sub_7FF7F63BB420(aCalledResultUn, 43, &_err_n_, &off_7FF7F63BC3A0, &off_7FF7F63BC428);// "src/main.rs"
  }
  if ( flag_len >= 65 )                         // 长度是64?
    sub_7FF7F63BB520(flag_len, 64, &off_7FF7F63BC440);// "src/main.rs"
  sub_7FF7F63B47D0(&_err_n_, flag, flag_len);
  n0x41_2 = _err_n_;
  Src_1 = Src;
  Src_4 = Src + n8;
  if ( n8 )
  {
    Size = 0;
    v5 = 0;
    Src_2 = Src;
    do
    {
      Src_3 = Src_2;
      v9 = v5;
      n0xF0 = *Src_2;
      n0x80 = n0xF0;
      if ( n0xF0 < 0 )
      {
        v12 = n0xF0 & 0x1F;
        v13 = Src_3[1] & 0x3F;
        if ( n0xF0 <= 0xDFu )
        {
          Src_2 = Src_3 + 2;
          n0x80 = v13 | (v12 << 6);
        }
        else
        {
          v14 = (v13 << 6) | Src_3[2] & 0x3F;
          if ( n0xF0 < 0xF0u )
          {
            Src_2 = Src_3 + 3;
            n0x80 = (v12 << 12) | v14;
          }
          else
          {
            Src_2 = Src_3 + 4;
            n0x80 = ((v12 & 7) << 18) | (v14 << 6) | Src_3[3] & 0x3F;
          }
        }
      }
      else
      {
        Src_2 = Src_3 + 1;
      }
      v5 = &v9[Src_2 - Src_3];
      if ( n0x80 - 9 >= 5 && n0x80 != 32 )
      {
        if ( n0x80 < 0x80 )
          goto LABEL_28;
        n48 = n0x80 >> 8;
        if ( n0x80 >> 8 > 0x1F )
        {
          if ( n48 == 32 )
          {
            v7 = *(off_7FF7F63C6078 + n0x80) >> 1;
          }
          else
          {
            if ( n48 != 48 )
              goto LABEL_28;
            v7 = n0x80 == 12288;
          }
        }
        else if ( n48 )
        {
          if ( n48 != 22 )
            goto LABEL_28;
          v7 = n0x80 == 5760;
        }
        else
        {
          v7 = *(off_7FF7F63C6078 + n0x80);
        }
        if ( (v7 & 1) == 0 )
          goto LABEL_28;
      }
    }
    while ( Src_2 != Src_4 );
    flag2 = 1;
    LOBYTE(v9) = 1;
    v45 = v9;
LABEL_60:
    Src_5 = Src;
    goto LABEL_61;
  }
  v9 = 0;
  v5 = 0;
  Src_2 = Src;
LABEL_28:
  if ( Src_2 == Src_4 )
  {
LABEL_53:
    if ( n8 )
    {
      Size = v5 - v9;
      if ( v5 - v9 < 0 )
        goto LABEL_74;
      goto LABEL_55;
    }
    flag2 = 1;
    LOBYTE(v9) = 1;
    v45 = v9;
    Size = 0;
    goto LABEL_60;
  }
  while ( 1 )
  {
    Src_6 = Src_4;
    n32 = *(Src_4 - 1);
    if ( (n32 & 0x80000000) != 0 )
      break;
    --Src_4;
    if ( n32 - 9 >= 5 )
      goto LABEL_43;
LABEL_32:
    if ( Src_2 == Src_4 )
      goto LABEL_53;
  }
  v20 = *(Src_4 - 2);
  if ( v20 >= -64 )
  {
    Src_4 -= 2;
    v23 = v20 & 0x1F;
  }
  else
  {
    v21 = *(Src_4 - 3);
    if ( v21 >= -64 )
    {
      Src_4 -= 3;
      v22 = v21 & 0xF;
    }
    else
    {
      Src_4 -= 4;
      v22 = ((*(Src_6 - 4) & 7) << 6) | v21 & 0x3F;
    }
    v23 = (v22 << 6) | v20 & 0x3F;
  }
  n32 = (v23 << 6) | n32 & 0x3F;
  if ( n32 - 9 < 5 )
    goto LABEL_32;
LABEL_43:
  if ( n32 == 32 )
    goto LABEL_32;
  if ( n32 < 0x80 )
    goto LABEL_73;
  n22 = n32 >> 8;
  if ( n32 >> 8 <= 0x1F )
  {
    if ( n22 )
    {
      if ( n22 != 22 )
        goto LABEL_73;
      v17 = n32 == 5760;
    }
    else
    {
      v17 = *(off_7FF7F63C6078 + n32);
    }
    goto LABEL_31;
  }
  if ( n22 == 32 )
  {
    v17 = *(off_7FF7F63C6078 + n32) >> 1;
    goto LABEL_31;
  }
  if ( n22 == 48 )
  {
    v17 = n32 == 12288;
LABEL_31:
    if ( (v17 & 1) == 0 )
      goto LABEL_73;
    goto LABEL_32;
  }
LABEL_73:
  v5 = &Src_6[v5 - Src_2];
  Size = v5 - v9;
  if ( v5 - v9 < 0 )
  {
LABEL_74:
    v27 = 0;
LABEL_75:
    Src_7 = Src_1;
    Size_1 = n0x41_2;
    sub_7FF7F63BAFA0(v27, Size);
  }
LABEL_55:
  Src_5 = &v9[Src];
  if ( v5 == v9 )
  {
    flag2 = 1;
    LOBYTE(v9) = 1;
    v45 = v9;
  }
  else
  {
    Src_8 = &v9[Src];
    v27 = 1;
    Src_10 = sub_7FF7F63A17D0(Size, 1);
    if ( !Src_10 )
      goto LABEL_75;
    flag2 = Src_10;
    v45 = 0;
    Src_5 = Src_8;
  }
LABEL_61:
  memcpy(flag2, Src_5, Size);
  if ( 2 * n0x41_2 )
    sub_7FF7F63A17E0(Src_1, n0x41_2, 1);
  Size_1 = Size;
  Src_7 = flag2;
  if ( Size == 1 )
    sub_7FF7F63BB200(1, 1, &off_7FF7F63BC410);  // "src/main.rs"
  if ( !Size )
    sub_7FF7F63BB200(0, 0, &off_7FF7F63BC3F8);  // "src/main.rs"
  v29 = *flag2;
  Buf1_1 = sub_7FF7F63A1800(Size, 1);
  if ( !Buf1_1 )
    sub_7FF7F63BAFA0(1, Size);
  Buf1 = Buf1_1;
  for ( i = 0; i != Size; ++i )
  {
    HIWORD(v33) = (v29 >> 2) ^ (v29 >> 3) ^ (v29 >> 1);
    LOWORD(v33) = v29;
    v29 = v33 >> 1;
    v34 = __ROL1__(v33, 4);
    v35 = (4 * (v34 & 0x33)) | (v34 >> 2) & 0x33;
    Buf1[i] = *(flag2 + i) ^ (i + ((2 * (v35 & 0x55)) | (v35 >> 1) & 0x55));
  }
  if ( Size == 42 && !memcmp(Buf1, &enc, 42u) )
  {
    sub_7FF7F63A17E0(Buf1, 42, 1);
    _err_n_ = &off_7FF7F63BC4A0;
    Src = 1;
    n8 = 8;
    v42 = 0;
    sub_7FF7F63A5750(&_err_n_);
  }
  else
  {
    sub_7FF7F63A17E0(Buf1, Size, 1);
    _err_n_ = &off_7FF7F63BC488;                // "errn"
    Src = 1;
    n8 = 8;
    v42 = 0;
    sub_7FF7F63A5750(&_err_n_);
  }
  return sub_7FF7F63A17E0(Src_7, Size_1, 1);
}

加密逻辑如下

v29 = *flag2;
  Buf1_1 = sub_7FF7F63A1800(Size, 1);
  if ( !Buf1_1 )
    sub_7FF7F63BAFA0(1, Size);
  Buf1 = Buf1_1;
  for ( i = 0; i != Size; ++i )
  {
    HIWORD(v33) = (v29 >> 2) ^ (v29 >> 3) ^ (v29 >> 1);
    LOWORD(v33) = v29;
    v29 = v33 >> 1;
    v34 = __ROL1__(v33, 4);
    v35 = (4 * (v34 & 0x33)) | (v34 >> 2) & 0x33;
    Buf1[i] = *(flag2 + i) ^ (i + ((2 * (v35 & 0x55)) | (v35 >> 1) & 0x55));
  }

就对于密文来说就是一个异或而已,但是与之异或的密钥流是和输入的前两个明文有关,也就是上面的v29,然后就可以直接仿写此密钥流的生成逻辑,拿到异或的值,当然也可以动调跟,拿到v35的值,再进行最后一步的生成,最后异或解密也可以

脚本如下

enc = [ 0x00, 0xA1, 0xFB, 0x53, 0x1C, 0xFA, 0xF0, 0x1B, 0x06, 0x40, 0xD4, 0x8C, 0x16, 0xF4, 0x90, 0x27,
        0x42, 0xB9, 0x8B, 0x0F, 0x02, 0xD7, 0x31, 0xB7, 0x26, 0x12, 0x06, 0x7E, 0xAE, 0xDF, 0xDA, 0x68,
        0xAF, 0x35, 0xCC, 0xB7, 0xB0, 0xD0, 0x9A, 0x59, 0x2B, 0x0B]
v29 = 0x66 + (0x6c << 8)
box = [0] * 42

for i in range(42):
    h = ((v29 >> 2) ^ (v29 >> 3) ^ (v29 >> 1)) & 0xffff
    l = v29 & 0xffff
    hl = (h << 16) | l
    v29 = (hl >> 1) & 0xffff
    v34 = (((hl & 0xFF) << 4) | ((hl & 0xFF) >> 4)) & 0xFF
    v35 = ((4 * (v34 & 0x33)) | (v34 >> 2) & 0x33) & 0xff
    box[i] = (i + ((2 * (v35 & 0x55)) | (v35 >> 1) & 0x55)) & 0xff

for i in range(42):
    print(chr((enc[i] ^ box[i]) & 0xff), end="")

flag{1c98572d-7f7b-4fbf-8750-4a2986c695ce}

京麒CTF2025 热身赛 RE
0x2 RE2

如题,就是SIMD

主函数如下

int __fastcall main(int argc, const char **argv, const char **envp)
{
  Stream *Stream; // rax
  size_t n0x40; // rax
  __int64 n32; // rax
  unsigned __int64 n0x20; // rax
  __m128i v8; // xmm2
  __m128i v9; // xmm1
  char Buffer[16]; // [rsp+20h] [rbp-58h] BYREF
  __int128 v11; // [rsp+30h] [rbp-48h]
  __int128 v12; // [rsp+40h] [rbp-38h]
  __int128 v13; // [rsp+50h] [rbp-28h]

  sub_140001010("Enter flag: ");
  Stream = _acrt_iob_func(0);
  fgets(Buffer, 64, Stream);
  n0x40 = strcspn(Buffer, "n");
  if ( n0x40 >= 0x40 )
    _report_rangecheckfailure();
  Buffer[n0x40] = 0;
  n32 = -1i64;
  do
    ++n32;
  while ( Buffer[n32] );
  if ( n32 == 32 )
  {
    n0x20 = 0i64;
    v8 = _mm_loadu_si128((const __m128i *)&xmmword_1400032F8);
    xmmword_140005630 = v11;
    xmmword_140005620 = *(_OWORD *)Buffer;
    xmmword_140005650 = v13;
    v9 = _mm_loadu_si128((const __m128i *)&xmmword_1400032E8);
    xmmword_140005640 = v12;
    do
    {
      *(__m128i *)&Buffer[n0x20] = _mm_add_epi8(
                                     _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)&Buffer[n0x20]), v8),
                                     v9);
      n0x20 += 16i64;
    }
    while ( n0x20 < 0x20 );
    if ( !memcmp(Buffer, "cge87k?9<>?@=pss393=>;8@:Cp@DAuH", 0x20ui64) )
      sub_140001010("Correct! The flag is flag{%s}n");
    else
      puts("Wrong!");
    return 0;
  }
  else
  {
    puts("Wrong!");
    return 1;
  }
}

就是两个指令_mm_add_epi8和_mm_shuffle_epi8,不是很熟悉,先去网上查了下,add没什么好说的,就是相加,shuffle,是以第二个参数的值作为索引,去第一个参数中取值,取到的值即为打乱后的密文,但是这里要注意,再传参的时候是小端序,不然就这题来说,打乱前和打乱后就没什么区别了

京麒CTF2025 热身赛 RE

解密脚本如下

encrypted_flag1 = "cge87k?9<>?@=pss"
encrypte_flag1 = encrypted_flag1[::-1]
enc1 = list(encrypte_flag1)
v9 = [0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]
v8 = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F]
for i in range(len(enc1)):
    print(chr(ord(enc1[i]) - v9[i % 16]), end="")

encrypted_flag2 = "393=>;8@:Cp@DAuH"
encrypte_flag2 = encrypted_flag2[::-1]
enc2 = list(encrypte_flag2)
v9 = [0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]
v8 = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F]
for i in range(len(enc2)):
    print(chr(ord(enc2[i]) - v9[i % 16]), end="")
京麒CTF2025 热身赛 RE

flag{cdb0444318e24beb8f374e9181599072}

京麒CTF2025 热身赛 RE
0x3 re!!!!!

iso逆向。

一开始想跟字符串,倒着跟踪到main函数,但是发现确实跟到了,但是有点抽象。于是乎,只能从头开始分析,也就是从初始化函数开始

ViewController.init

id __fastcall ViewController.init(nibName:bundle:)(__int64 a1, __int64 a2, void *obj_1)
{
  char *receiver; // x20
  char *v5; // x8
  char *v6; // x8
  NSString obj; // x21
  id v9; // x20
  objc_super v11; // [xsp+0h] [xbp-30h] BYREF

  v5 = &receiver[OBJC_IVAR____TtC9challenge14ViewController_secretKey];
  *(_QWORD *)v5 = 'akihuraH';
  *((_QWORD *)v5 + 1) = 0xEA00000000006567LL;
  v6 = &receiver[OBJC_IVAR____TtC9challenge14ViewController_flagHash];
  *(_QWORD *)v6 = 0xD000000000000126LL;
  *((_QWORD *)v6 + 1) = 0x8000000100003C20LL;
  *(_QWORD *)&receiver[OBJC_IVAR____TtC9challenge14ViewController_magicNumber] = 3735928559LL;
  *(_QWORD *)&receiver[OBJC_IVAR____TtC9challenge14ViewController_inputTextField] = 0;
  *(_QWORD *)&receiver[OBJC_IVAR____TtC9challenge14ViewController_outputTextField] = 0;
  *(_QWORD *)&receiver[OBJC_IVAR____TtC9challenge14ViewController_encryptButton] = 0;
  if ( a2 )
  {
    obj = String._bridgeToObjectiveC()();
    a1 = swift_bridgeObjectRelease(a2);
  }
  else
  {
    obj = 0;
  }
  v11.receiver = receiver;
  v11.super_class = (Class)type metadata accessor for ViewController(a1);
  v9 = objc_msgSendSuper2(&v11, "initWithNibName:bundle:", obj, obj_1);
  objc_release(obj);
  objc_release(obj_1);
  return v9;
}

这里有看到一个初始化的key:Haruhikage。(这里要注意,伪代码中的key不完整,还差两位...)

然后就开始分析ViewController.encryptButtonClicked()京麒CTF2025 热身赛 RE

这里就是check输出正确flag的地方

在String里找这个这个提示的时候还看到了一个可疑的字符串京麒CTF2025 热身赛 RE

c2..c3..。都是以这样的形式。

然后分析这个函数下来,主要逻辑就是获取到输入的flag,先检查是否为空,不为空的话就传到ViewController.complexEncrypt(_:)函数中进行加密,然后在ViewController.verifyFlag(_:)函数中进行check。

加密函数

__int64 __fastcall ViewController.complexEncrypt(_:)(__int64 a1, __int64 a2)
{
  __int64 v2; // x20
  __int64 v5; // x22
  __int64 v6; // x24
  char *v7; // x23
  __int64 v8; // x19
  __int64 v9; // x1
  __int64 v10; // x21
  __int64 v11; // x19
  unsigned __int64 v12; // x1
  unsigned __int64 v13; // x20
  __int64 _countAndFlagsBits; // x0
  void *_object; // x21
  Swift::String v16; // kr00_16
  __int64 v17; // x19
  __int64 v18; // x1
  __int64 v19; // x20
  __int64 v20; // x19
  __int64 v22; // [xsp+0h] [xbp-30h] BYREF

  v5 = type metadata accessor for String.Encoding(0);
  v6 = *(v5 - 8);
  v7 = &v22 - ((*(v6 + 64) + 15LL) & 0xFFFFFFFFFFFFFFF0LL);
  v8 = specialized ViewController.rc4Encrypt(_:key:)(// RC4
         a1,
         a2,
         *(v2 + OBJC_IVAR____TtC9challenge14ViewController_secretKey),
         *(v2 + OBJC_IVAR____TtC9challenge14ViewController_secretKey + 8));
  v10 = v9;
  static String.Encoding.utf8.getter();         // UTF8
  v11 = String.data(using:allowLossyConversion:)(v7, 0, v8, v10);
  v13 = v12;
  swift_bridgeObjectRelease(v10);
  (*(v6 + 8))(v7, v5);
  if ( v13 >> 60 == 15 )
  {
    _countAndFlagsBits = 0;
    _object = 0xE000000000000000LL;
  }
  else
  {
    v16 = Data.base64EncodedString(options:)(0);// base64
    _object = v16._object;
    outlined consume of Data?(v11, v13);
    _countAndFlagsBits = v16._countAndFlagsBits;
  }
  v17 = specialized ViewController.obfuscateString(_:)(_countAndFlagsBits, _object);// obfuscateString
  v19 = v18;
  swift_bridgeObjectRelease(_object);
  v20 = specialized ViewController.transformString(_:)(v17, v19);// transformString
  swift_bridgeObjectRelease(v19);
  return v20;
}

整体的加密逻辑就是RC4->base64->obfuscateString->transformString

transformString函数,有点抽象

void *__fastcall specialized ViewController.transformString(_:)(__int64 a1, unsigned __int64 a2)
{
  unsigned __int64 v2; // x8
  void *_countAndFlagsBits; // x0
  Swift::String_optional v4; // kr00_16
  void *_object; // x19
  __int64 index; // x21
  __int64 v7; // x20
  unsigned int n190; // w8
  unsigned int v9; // w8
  Swift::String v10; // x0
  void *_object_1; // x19
  Swift::String_optional v12; // kr10_16
  int v13; // w10
  int v14; // w9
  int v15; // w10
  int v16; // w13
  int v17; // w10
  _QWORD v18[2]; // [xsp+8h] [xbp-88h] BYREF
  unsigned __int64 v19; // [xsp+18h] [xbp-78h]
  __int64 v20; // [xsp+20h] [xbp-70h]
  unsigned __int64 v21; // [xsp+28h] [xbp-68h]
  void *_object_2; // [xsp+30h] [xbp-60h]
  unsigned __int64 v23; // [xsp+38h] [xbp-58h]

  _object_2 = 0;
  v23 = 0xE000000000000000LL;
  v2 = HIBYTE(a2) & 0xF;
  if ( (a2 & 0x2000000000000000LL) == 0 )
    v2 = a1 & 0xFFFFFFFFFFFFLL;
  v18[1] = a1;
  v19 = a2;
  v20 = 0;
  v21 = v2;
  swift_bridgeObjectRetain(a2);
  v4 = String.Iterator.next()();                // 获取字符串迭代器
  _countAndFlagsBits = v4.value._countAndFlagsBits;
  _object = v4.value._object;
  if ( v4.value._object )
  {
    index = 0;
    while ( !__OFADD__(index, 1) )              // 遍历每个字符
    {
      v7 = specialized Collection.first.getter(_countAndFlagsBits, _object);// 获取当前字符
      _countAndFlagsBits = swift_bridgeObjectRelease(_object);
      if ( (index & 1) != 0 )                   // 判断奇偶
                                                // 奇数异或 0xBE
                                                // 偶数异或 0xEF
      {
        n190 = v7 ^ 0xBE;
        if ( (v7 & &_mh_execute_header) != 0 )
          n190 = 190;
        if ( n190 >> 11 == 27 )
          goto LABEL_30;
        if ( n190 > 0x10FFFF )
          goto LABEL_27;
      }
      else
      {
        n190 = v7 ^ 0xEF;
        if ( (v7 & &_mh_execute_header) != 0 )
          n190 = 239;
        if ( n190 >> 11 == 27 )
          goto LABEL_29;
        if ( n190 > 0x10FFFF )
          goto LABEL_28;
      }
      if ( n190 > 0x7F )
      {
        v13 = (n190 & 0x3F) << 8;
        v14 = (n190 >> 6) + v13 + 33217;
        v15 = (v13 | (n190 >> 6) & 0x3F) << 8;
        v16 = (n190 >> 18) + ((v15 | (n190 >> 12) & 0x3F) << 8) - 2122219023;
        v17 = (n190 >> 12) + v15 + 8487393;
        if ( HIWORD(n190) )
          v17 = v16;
        if ( n190 >= 0x800 )
          v9 = v17;
        else
          v9 = v14;
      }
      else
      {
        v9 = n190 + 1;
      }
      v18[0] = (v9 + 0xFEFEFEFEFEFEFFLL) & ~(-1LL << (8 * (4 - (__clz(v9) >> 3))));
      v10._countAndFlagsBits = static String._uncheckedFromUTF8(_:)(v18);
      _object_1 = v10._object;
      String.append(_:)(v10);
      swift_bridgeObjectRelease(_object_1);
      v12 = String.Iterator.next()();           // 移动到下一个字符
      _countAndFlagsBits = v12.value._countAndFlagsBits;
      _object = v12.value._object;              // 检查是否结束
      ++index;
      if ( !v12.value._object )
      {
        _object = _object_2;
        goto LABEL_25;
      }
    }
    __break(1u);
LABEL_27:
    __break(1u);
LABEL_28:
    __break(1u);
LABEL_29:
    __break(1u);
LABEL_30:
    __break(1u);
  }
  else
  {
LABEL_25:
    swift_bridgeObjectRelease(v19);
    return _object;
  }
  return _countAndFlagsBits;
}

总的来说就是一个异或,但是在异或完,有个奇怪的逻辑京麒CTF2025 热身赛 RE

可以按照这个逻辑来仿写一个看看输出是什么。这里还要查下__clz()有什么用京麒CTF2025 热身赛 RE

同构的脚本:

def __clz(x):
    return 64 - x.bit_length() if x != 0 else 64

for n190 in range(0x00, 0x100):  # 0x00到0xFF
    # 处理非ASCII字符(>0x7F)
    if n190 > 0x7F:
        v13 = (n190 & 0x3F) << 8
        v14 = (n190 >> 6) + v13 + 33217
        v15 = (v13 | ((n190 >> 6) & 0x3F)) << 8
        v16 = (n190 >> 18) + ((v15 | ((n190 >> 12) & 0x3F)) << 8) - 2122219023
        v17 = (n190 >> 12) + v15 + 8487393

        if n190 > 0xFFFF:
            v17 = v16
        v9 = v17 if n190 >= 0x800 else v14
    else:
        v9 = n190 + 1

    # 安全计算移位量(确保非负)
    shift = 8 * (4 - (__clz(v9) >> 3))
    shift = max(0, min(shift, 64))  # 限制在0-64范围内

    # 计算掩码
    mask = ~(-1 << shift) if shift > 0 else 0xFFFFFFFFFFFFFFFF
    v9 = (v9 + 0xFEFEFEFEFEFEFF) & mask

    print(f"{hex(n190)} -> {hex(v9)}")
京麒CTF2025 热身赛 RE
京麒CTF2025 热身赛 RE

这样一来就知道了那串可疑字符串中c2,c3的来处了。以及如果是c3开头的,在解密的时候要-0x40.

obfuscateString函数

void *__fastcall specialized ViewController.obfuscateString(_:)(__int64 _countAndFlagsBits, unsigned __int64 _object)
{
  unsigned __int64 v2; // x8
  void *_countAndFlagsBits_1; // x0
  Swift::String_optional v4; // kr00_16
  void *_object_2; // x19
  __int64 v6; // x21
  __int64 v7; // x20
  __int64 v8; // x8
  bool v9; // vf
  __int64 v10; // x8
  __int64 v11; // x8
  bool v12; // nf
  unsigned int v13; // w8
  Swift::String v14; // x0
  void *_object_3; // x19
  Swift::String_optional v16; // kr10_16
  _QWORD v17[2]; // [xsp+8h] [xbp-78h] BYREF
  unsigned __int64 _object_1; // [xsp+18h] [xbp-68h]
  __int64 v19; // [xsp+20h] [xbp-60h]
  unsigned __int64 v20; // [xsp+28h] [xbp-58h]
  void *_object_4; // [xsp+30h] [xbp-50h]
  unsigned __int64 v22; // [xsp+38h] [xbp-48h]

  _object_4 = 0;
  v22 = 0xE000000000000000LL;
  v2 = HIBYTE(_object) & 0xF;
  if ( (_object & 0x2000000000000000LL) == 0 )
    v2 = _countAndFlagsBits & 0xFFFFFFFFFFFFLL;
  v17[1] = _countAndFlagsBits;
  _object_1 = _object;
  v19 = 0;
  v20 = v2;
  swift_bridgeObjectRetain(_object);
  v4 = String.Iterator.next()();
  _countAndFlagsBits_1 = v4.value._countAndFlagsBits;
  _object_2 = v4.value._object;
  if ( v4.value._object )
  {
    v6 = 0;
    while ( !__OFADD__(v6, 1) )
    {
      v7 = specialized Collection.first.getter(_countAndFlagsBits_1, _object_2);
      _countAndFlagsBits_1 = swift_bridgeObjectRelease(_object_2);
      v8 = v7;
      if ( (v7 & &_mh_execute_header) != 0 )
        v8 = 0;
      v9 = __OFADD__(v8, v6);
      v10 = v8 + v6;
      if ( v9 )
        goto LABEL_20;
      v9 = __OFADD__(v10, 3735928559LL);
      v11 = v10 + 3735928559LL;
      if ( v9 )
        goto LABEL_21;
      v12 = -v11 < 0;
      v11 = v11;
      if ( !v12 )
        v11 = --v11;
      if ( v11 < 0 )
        goto LABEL_22;
      if ( (v11 & 0xFFFFFF80) != 0 )
        v13 = (((v11 & 0x3F) << 8) | (v11 >> 6)) + 33217;
      else
        v13 = v11 + 1;
      v17[0] = (v13 + 0xFEFEFEFEFEFEFFLL) & ~(-1LL << (8 * (4 - (__clz(v13) >> 3))));
      v14._countAndFlagsBits = static String._uncheckedFromUTF8(_:)(v17);
      _object_3 = v14._object;
      String.append(_:)(v14);
      swift_bridgeObjectRelease(_object_3);
      v16 = String.Iterator.next()();
      _countAndFlagsBits_1 = v16.value._countAndFlagsBits;
      _object_2 = v16.value._object;
      ++v6;
      if ( !v16.value._object )
      {
        _object_2 = _object_4;
        goto LABEL_18;
      }
    }
    __break(1u);
LABEL_20:
    __break(1u);
LABEL_21:
    __break(1u);
LABEL_22:
    __break(1u);
  }
  else
  {
LABEL_18:
    swift_bridgeObjectRelease(_object_1);
    return _object_2;
  }
  return _countAndFlagsBits_1;
}

加密逻辑:+索引index和常数0xDEADBEEF,然后&0xFF

那么就可以解密了

hex_enc = "c2a7c3b9c2acc3a5c2a2c3b6c391c295c2aac38cc28bc38ac2a6c3aec28bc28fc2a1c3aac287c382c2bfc3b6c282c38ec2b9c3a2c2a13cc28ac3adc2b1c280c2b2c384c28dc3bbc283c396c2b03dc28a3bc2b12cc287c3b0c2852bc282c39ac28432c29320c29d21c29ac392c291c3a1c296c3a06d1866396c256310c299c3946931c291c3917a2e470b632a7811730f65c385"

def de_utf(hex_str):
    res = bytearray()
    i = 0
    while i < len(hex_str):
        prefix = hex_str[i:i+2]
        if prefix == "c2":
            val = int(hex_str[i+2:i+4], 16)
            res.append(val)
            i += 4
        elif prefix == "c3":
            val = int(hex_str[i+2:i+4], 16) + 0x40
            res.append(val)
            i += 4
        else:
            val = int(prefix, 16)
            res.append(val)
            i += 2
    return res

step1 = de_utf(hex_enc)

for i in range(len(step1)):
    step1[i] ^= 0xBE if i % 2 else 0xEF

step2 = de_utf(step1.hex())

for i in range(len(step2)):
    step2[i] = (step2[i] - i - 0xDEADBEEF) & 0xFF

print(step2.decode(errors="ignore"))

解得

YWRiZTI5NzkzNTg3OTgzMDhjOTExZGQ0NjQ3YTJmNmExM2MwNDJjYzMyNDU5N2UxZWRiYzA4OWE5ZTkwMTVmYmE5

京麒CTF2025 热身赛 RE

flag{N4nd3_H4ruhik4g3_Y4tt4n0?!!}

京麒CTF2025 热身赛 RE
0x4

原文始发于微信公众号(神农Sec):京麒CTF2025 热身赛 RE

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月29日15:29:39
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   京麒CTF2025 热身赛 REhttps://cn-sec.com/archives/4111340.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息