第八届强网·拟态防御国际精英挑战赛

  1. 第八届强网·拟态防御国际精英挑战赛
    1. MOBILE
      1. EZMiniAPP
      2. just
    2. REVERSE
      1. HyperJump
      2. Icall

第八届强网·拟态防御国际精英挑战赛

MOBILE

EZMiniAPP

看名字一眼就是一个小程序,直接用GitHub上的开源项目解包

biggerstar/wedecode: 全自动化,微信小程序 wxapkg 包 源代码还原工具, 线上代码安全审计,支持 Windows, Macos, Linux

image-20251025172957543

check逻辑就在chunk_0.appservice.js.map里

{
    "version": 3,
    "file": "",
    "sourceRoot": "",
    "sources": [
        "pages/index/index.js"
    ],
    "names": [
        "Page",
        "data",
        "inputValue",
        "animationData",
        "onLoad",
        "a",
        "wx",
        "createAnimation",
        "duration",
        "timingFunction",
        "opacity",
        "translateY",
        "step",
        "setData",
        "export",
        "onInputChange",
        "detail",
        "value",
        "enigmaticTransformation",
        "t",
        "e",
        "n",
        "r",
        "i",
        "Array",
        "from",
        "map",
        "charCodeAt",
        "s",
        "length",
        "c",
        "o",
        "u",
        "h",
        "push",
        "customEncrypt",
        "onCheck",
        "trim",
        "console",
        "log",
        "JSON",
        "stringify",
        "showToast",
        "title",
        "icon"
    ],
    "mappings": "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",
    "sourcesContent": [
        "\"use strict\";Page({data:{inputValue:\"\",animationData:{}},onLoad:function(){var a=wx.createAnimation({duration:1e3,timingFunction:\"ease\"});a.opacity(1).translateY(0).step(),this.setData({animationData:a.export()})},onInputChange:function(a){this.setData({inputValue:a.detail.value})},enigmaticTransformation:function(a,t){for(var e,n,r=[],i=Array.from(t).map((function(a){return a.charCodeAt(0)})),s=i.length,c=function(a){for(var t=0,e=0;e<a.length;e++)switch(e%4){case 0:t+=1*a[e];break;case 1:t+=a[e]+0;break;case 2:t+=0|a[e];break;case 3:t+=0^a[e]}return t}(i)%8,o=0;o<a.length;o++){var u=void 0;switch(o%3){case 0:u=a.charCodeAt(o)^i[o%s];break;case 1:u=i[o%s]^a.charCodeAt(o);break;case 2:e=a.charCodeAt(o),n=i[o%s],u=e^n}var h=void 0;switch(c){case 0:h=u;break;case 1:h=255&(u<<1|u>>7&1);break;case 2:h=255&(u<<2|u>>6&3);break;case 3:h=255&(u<<3|u>>5&7);break;case 4:h=255&(u<<4|u>>4&15);break;case 5:h=255&(u<<5|u>>3&31);break;case 6:h=255&(u<<6|u>>2&63);break;case 7:h=255&(u<<7|u>>1&127);break;default:h=255&(u<<c|u>>8-c)}switch(o%2){case 0:r[r.length]=h;break;case 1:r.push(h)}}return function(a){for(var t=[],e=0;e<a.length;e++)switch(e%2){case 0:case 1:t[e]=a[e]}return t}(r)},customEncrypt:function(a,t){return this.enigmaticTransformation(a,t)},onCheck:function(){var a=this.data.inputValue;if(\"\"!==a.trim()){var t=this.customEncrypt(a,\"newKey2025!\");console.log(t),JSON.stringify(t)===JSON.stringify([1,33,194,133,195,102,232,104,200,14,8,163,131,71,68,97,2,76,72,171,74,106,225,1,65])?wx.showToast({title:\"Right\",icon:\"success\",duration:2e3}):wx.showToast({title:\"Wrong\",icon:\"error\",duration:2e3})}else wx.showToast({title:\"请输入内容\",icon:\"none\",duration:2e3})}});"
    ]
}

然后直接写脚本解密

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Decrypt script for the mini-program CTF check
Algorithm mirrors the app's enigmaticTransformation:
- u = input_byte ^ key_byte
- h = rol8(u, c) where c = sum(key_bytes) % 8
To decrypt: input_byte = ror8(h, c) ^ key_byte
"""

from typing import List


def rol8(x: int, k: int) -> int:
    k %= 8
    x &= 0xFF
    return ((x << k) & 0xFF) | (x >> (8 - k))


def ror8(x: int, k: int) -> int:
    k %= 8
    x &= 0xFF
    return ((x >> k) | ((x << (8 - k)) & 0xFF)) & 0xFF


def compute_shift(key_bytes: List[int]) -> int:
    return sum(key_bytes) % 8


def decrypt(arr: List[int], key: str) -> str:
    key_bytes = [ord(c) for c in key]
    s = len(key_bytes)
    c = compute_shift(key_bytes)
    out_chars = []
    for o, h in enumerate(arr):
        u = ror8(h, c)
        ch = u ^ key_bytes[o % s]
        out_chars.append(chr(ch))
    return ''.join(out_chars)


def encrypt(plain: str, key: str) -> List[int]:
    key_bytes = [ord(c) for c in key]
    s = len(key_bytes)
    c = compute_shift(key_bytes)
    out = []
    for o, ch in enumerate(plain):
        u = ord(ch) ^ key_bytes[o % s]
        h = rol8(u, c)
        out.append(h)
    return out


if __name__ == '__main__':
    target_arr = [
        1, 33, 194, 133, 195, 102, 232, 104, 200, 14,
        8, 163, 131, 71, 68, 97, 2, 76, 72, 171,
        74, 106, 225, 1, 65
    ]
    key = 'newKey2025!'

    plain = decrypt(target_arr, key)
    print(plain)

    reenc = encrypt(plain, key)
    assert reenc == target_arr, 'Re-encryption mismatch!'

flag{JustEasyMiniProgram}

just

​ 一开始用 Il2CppDumper 的时候报错,

image-20251028103040872

​ 然后发现so被加密了,于是就开始找解密的地方。最开始想着想在程序运行的时候dump下来,但是发现好像有Frida 检测,并且也dump不下来,那就只能去看其他的so了。

​ 在libjust.so 中,通过可疑字符串定位到解密函数

image-20251028103457455

可以看到应该是一个rc4,密钥是nihaounity

image-20251028103646709

image-20251028103842659

尝试解密

image-20251028103852791

​ 然后再次 Il2CppDumper 还是报错,那就是 global-metadata.dat 的问题了,010打开查看确实有问题(光看头确实没啥问题,但是往下多看看就发现有问题了)

image-20251028104029722

​ 还是通过字符串去解密出来的 libil2cpp.so 里搜

image-20251028104736964

image-20251028104816842

​ 跟进 sub_211D94 ,然后继续找解密逻辑,如下

image-20251028105035325

​ 丢给ai写了个脚本解密

import struct

def sub_21A2C8(src: bytes, a2: int) -> bytes:
    # 确保长度足够
    if len(src) < 1026 * 2:
        raise ValueError("src too short")

    # src 是 16-bit little endian 数组
    u16 = list(struct.unpack("<" + "H" * (len(src) // 2), src))
    v2 = u16[512]

    v4 = a2 - 4 * v2
    v5 = v4 - 1028

    dest_size = v4 - 4
    dest = bytearray(dest_size)

    # 拷贝前 0x400 字节(512 * 2)
    dest[:0x400] = src[:0x400]

    if v5 >= 1:
        for i in range(0, v5, 4):
            v8 = i if i >= 0 else i + 3
            v9 = i + i // v2

            base_offset = (v8 & ~3) + 1024
            src_data_off = (2 * v2 + 514) * 2 + (v8 & ~3)
            key_off = (2 * (v9 % v2) + 514) * 2

            if src_data_off + 4 <= len(src) and key_off + 4 <= len(src):
                data_val = struct.unpack_from("<I", src, src_data_off)[0]
                key_val = struct.unpack_from("<I", src, key_off)[0]
                res = data_val ^ key_val
                struct.pack_into("<I", dest, base_offset, res)

    return bytes(dest)

# 假设你从文件中读取了加密数据:
with open("global-metadata.dat", "rb") as f:
    src_data = f.read()

# a2 一般是文件总长度(比如 len(src_data))
result = sub_21A2C8(src_data, len(src_data))

with open("decrypted.bin", "wb") as f:
    f.write(result)

​ 然后就可以使用 Il2CppDumper 一把梭了

image-20251028105653428

​ 接下俩就是主要对 FlagChecker类进行分析了
image-20251028105920299

image-20251028111848917

​ 然后根据偏移去找加密逻辑。一个魔改的tea

image-20251028111902797

​ 然后继续找密文和密钥,可以去构造函数里找找

image-20251028112821074

​ 把 libil2Cpp.so 符号恢复一下,ida 依次导入脚本 ida_with_struct_py3.py -> script.json -> il2cpp.h,即可。

void __fastcall FlagChecker___cctor(const MethodInfo *method)
{
  unsigned __int64 v1; // d0
  unsigned __int64 v2; // d1
  int n9; // w8
  System_Array_o *array; // x0
  System_RuntimeFieldHandle_o fldHandle; // x1
  System_Array_o *Key; // x19
  struct FlagChecker_StaticFields *static_fields; // x0
  System_Array_o *array_1; // x0
  System_RuntimeFieldHandle_o fldHandle_1; // x1
  System_Array_o *ReallyCompare; // x19
  struct FlagChecker_StaticFields *static_fields_1; // x0

  if ( (byte_9D8040 + 7) / byte_9D8044 != byte_9D8048 )
  {
    LOBYTE(v1) = byte_9D804C;
    LOBYTE(v2) = byte_9D8050;
    if ( byte_9D8050 + 30 * byte_A2F928 == 3 )
    {
      if ( (byte_9D8040 + 7) / byte_9D8044 == byte_9D8048 )
        n9 = 0;
      else
        n9 = ((v1 - (v2 + v2)) * ((byte_A2F8C2 & 1) == 0) * ((byte_9D8040 + 7) / byte_9D8044 - byte_9D8048));
    }
    else
    {
      n9 = 9;
    }
    if ( ((byte_9D8040 + 7) / byte_9D8044 - byte_9D8048) * n9 )
    {
      sub_2744F4(&byte___TypeInfo);
      sub_2744F4(&FlagChecker_TypeInfo);
      sub_2744F4(&Field__PrivateImplementationDetails__29FC2CC7503351583297C73AC477A5D7AA78899F3C0D66E1F909139D4AA1FFB2);
      sub_2744F4(&Field__PrivateImplementationDetails__C8E4E9E3F34C25560172B0D40B6DF4823260AA87EC6866054AA4691711E5D7BF);
      sub_2744F4(&uint___TypeInfo);
      byte_A2F8C2 = 1;
    }
  }
  array = sub_274508(uint___TypeInfo, 4LL);
  fldHandle.fields.value = Field__PrivateImplementationDetails__C8E4E9E3F34C25560172B0D40B6DF4823260AA87EC6866054AA4691711E5D7BF;
  Key = array;
  System_Runtime_CompilerServices_RuntimeHelpers__InitializeArray_4166724(array, fldHandle, 0LL);
  static_fields = FlagChecker_TypeInfo->static_fields;
  static_fields->Key = Key;
  sub_274498(static_fields, Key);
  array_1 = sub_274508(byte___TypeInfo, 40LL);
  fldHandle_1.fields.value = Field__PrivateImplementationDetails__29FC2CC7503351583297C73AC477A5D7AA78899F3C0D66E1F909139D4AA1FFB2;
  ReallyCompare = array_1;
  System_Runtime_CompilerServices_RuntimeHelpers__InitializeArray_4166724(array_1, fldHandle_1, 0LL);
  static_fields_1 = FlagChecker_TypeInfo->static_fields;
  static_fields_1->ReallyCompare = ReallyCompare;
  sub_274498(&static_fields_1->ReallyCompare, ReallyCompare);
}

image-20251028114501728

​ 可以根据这个值去 dump.cs 中找到密文和key偏移

// Namespace: 
internal sealed class <PrivateImplementationDetails> // TypeDefIndex: 2223
{
	// Fields
	internal static readonly <PrivateImplementationDetails>.__StaticArrayInitTypeSize=40 29FC2CC7503351583297C73AC477A5D7AA78899F3C0D66E1F909139D4AA1FFB2 /*Metadata offset 0xF901D*/; // 0x0
	internal static readonly <PrivateImplementationDetails>.__StaticArrayInitTypeSize=16 C8E4E9E3F34C25560172B0D40B6DF4823260AA87EC6866054AA4691711E5D7BF /*Metadata offset 0xF9045*/; // 0x28
}

​ 010editor 打开去找

image-20251028115428102

写脚本解密

import struct


def perform_decryption_cycle(block_pair, key_schedule):
    val0 = block_pair[0]
    val1 = block_pair[1]
    constant_delta = 0x61C88647
    current_sum = (-16 * constant_delta) & 0xFFFFFFFF

    k0, k1, k2, k3 = key_schedule

    for _ in range(16):
        current_sum = (current_sum + constant_delta) & 0xFFFFFFFF

        term_shift_left_v0 = (val0 << 4) & 0xFFFFFFFF
        term_shift_right_v0 = (val0 >> 5) & 0xFFFFFFFF
        xor_operand_v1 = (term_shift_left_v0 + k2) ^ (val0 + current_sum) ^ (term_shift_right_v0 + k3)
        val1 = (val1 - xor_operand_v1) & 0xFFFFFFFF

        term_shift_left_v1 = (val1 << 4) & 0xFFFFFFFF
        term_shift_right_v1 = (val1 >> 5) & 0xFFFFFFFF
        xor_operand_v0 = (term_shift_left_v1 + k0) ^ (val1 + current_sum) ^ (term_shift_right_v1 + k1)
        val0 = (val0 - xor_operand_v0) & 0xFFFFFFFF

    block_pair[0] = val0
    block_pair[1] = val1
    return block_pair


cipher_bytes_data = bytes([
    0xaf, 0x58, 0x64, 0x40, 0x9d, 0xb9, 0x21, 0x67,
    0xae, 0xb5, 0x29, 0x04, 0x9e, 0x86, 0xc5, 0x43,
    0x23, 0x0f, 0xbf, 0xa6, 0xb2, 0xae, 0x4a, 0xb5,
    0xc5, 0x69, 0xb7, 0xa8, 0x03, 0xd1, 0xae, 0xcf,
    0xc6, 0x2c, 0x5b, 0x7f, 0xa2, 0x86, 0x1e, 0x1a,
])

encryption_key = [0x12345678, 0x09101112, 0x13141516, 0x15161718]

mutable_cipher_bytes = list(cipher_bytes_data)

for byte_offset_l in range(32, 0, -8):
    # 1️⃣ XOR 前 8 个字节
    for i in range(8):
        mutable_cipher_bytes[byte_offset_l + i] ^= mutable_cipher_bytes[i]

    # 2️⃣ 对前 8 字节执行解密
    segment_bytes_to_decrypt = bytes(mutable_cipher_bytes[0:8])
    block_data_pair = list(struct.unpack("<2I", segment_bytes_to_decrypt))
    perform_decryption_cycle(block_data_pair, encryption_key)
    updated_segment_bytes = struct.pack("<2I", *block_data_pair)

    # 更新前 8 个字节
    for i in range(8):
        mutable_cipher_bytes[i] = updated_segment_bytes[i]

# 最后再解一次(有些算法会双解或多轮)
segment_bytes_to_decrypt = bytes(mutable_cipher_bytes[0:8])
block_data_pair = list(struct.unpack("<2I", segment_bytes_to_decrypt))
perform_decryption_cycle(block_data_pair, encryption_key)
updated_segment_bytes = struct.pack("<2I", *block_data_pair)
for i in range(8):
    mutable_cipher_bytes[i] = updated_segment_bytes[i]

final_decrypted_bytes = bytes(mutable_cipher_bytes)

print(final_decrypted_bytes.decode("utf-8", errors="ignore"))

flag{unitygame_I5S0ooFunny_Isnotit?????}

REVERSE

HyperJump

IDA 打开分析,刚开始想尝试去弄明白逻辑,但是发现太复杂了,想手搓复现根本不可能,AI吧,又没钱充 ChatGPT5,然后开始上手动调看看逻辑,刚开始动调的时候发现刚挂上就闪退了,发现忘记把两处反调试nop了,直接 nop 掉即可,就可以正常动态调试了(标红出来的这两处就是anti_debug的地方)

image-20251025182355320

然后开始动调,首先是检测flag长度为24,而去掉flag{}这六位,就剩18位,并且check的时候也会check flag{},这样带来的好处就是有利于动调爆破,后来在看汇编的时候发现有个cmp,对每个字符进行加密check之后都会进行比对,只要有一个错误就直接退出

f52da144a7bf6ea7088ba4df13bd072b

按道理来说应该可以把算法复现出来,然后写脚本爆破,但是奈何脚本搓不出来,就只能手动爆破了,回到最原始的方式(真是无敌了)。

就这样,试了几个小时,试出来了,然后题目hint中提到flag的md5值,说明可能是多解,但好在试出来的这个刚好就对上了。

image-20251025182846804

image-20251025182908186

image-20251025182932408

flag{m4z3d_vm_jump5__42}

Icall

先静态分析了一下,首先看的是init,发现有反调试
image-20251026074710526

然后直接该if的判断,从jz改成jnz就可以直接跳过,不进入到if语句内部执行逻辑即可

然后看main函数的话,发现混淆很严重,但是从函数列表中可以发现函数的数量并不多,于是就挨个去看了下,发现了三个可疑函数

第一个是很像凯撒加密的函数(后面就称其为凯撒加密函数)

__int64 __fastcall kaisa(char a1)
{
  int n1668413646; // edx
  int n1321055944; // edi
  int n1668413646_3; // edi
  int n1668413646_2; // esi
  int n1668413646_1; // [rsp+10h] [rbp-30h]
  int v7; // [rsp+14h] [rbp-2Ch]
  char v9; // [rsp+2Fh] [rbp-11h]
  int v10; // [rsp+3Ch] [rbp-4h]

  v10 = *(*((*(qword_406198 + 1518159036) - 66708104LL))() + 2LL * a1) & 0x100;
  n1668413646_1 = -1576811487;
  while ( 1 )
  {
    while ( 1 )
    {
      while ( n1668413646_1 < -630065159 )
      {
        if ( n1668413646_1 < -1013558147 )
        {
          if ( n1668413646_1 < -1266885767 )
          {
            n1668413646 = 1668413646;
            if ( v10 )
              n1668413646 = -1013558147;
            n1668413646_1 = n1668413646;
          }
          else if ( n1668413646_1 < -1152562549 )
          {
            v7 = (7 * (a1 - '0') + 11) % 10;
            n1668413646_2 = -1152562549;
            if ( v7 < 0 )
              n1668413646_2 = -764946794;
            n1668413646_1 = n1668413646_2;
          }
          else
          {
            v9 = v7 + 48;
            n1668413646_1 = 1433409905;
          }
        }
        else if ( n1668413646_1 < -788615130 )
        {
          v9 = (7 * (a1 - 'A') + 11) % 26 + 65;
          n1668413646_1 = 1433409905;
        }
        else if ( n1668413646_1 < -764946794 )
        {
          v9 = (7 * (a1 - 'a') + 11) % 26 + 97;
          n1668413646_1 = 1433409905;
        }
        else
        {
          LOBYTE(v7) = v7 + 10;
          n1668413646_1 = -1152562549;
        }
      }
      if ( n1668413646_1 >= 1433409905 )
        break;
      if ( n1668413646_1 < -624870238 )
      {
        v9 = a1;
        n1668413646_1 = 1433409905;
      }
      else if ( n1668413646_1 >= 1321055944 )
      {
        n1668413646_3 = -624870238;
        if ( (*(*((*(qword_406198 + 1518159036) - 66708104LL))() + 2LL * a1) & 0x800) != 0 )
          n1668413646_3 = -1266885767;
        n1668413646_1 = n1668413646_3;
      }
      else
      {
        n1668413646_1 = 2084301942;
      }
    }
    if ( n1668413646_1 < 1668413646 )
      break;
    if ( n1668413646_1 < 2084301942 )
    {
      n1321055944 = 1321055944;
      if ( (*(*((*(qword_406198 + 1518159036) - 66708104LL))() + 2LL * a1) & 0x200) != 0 )
        n1321055944 = -788615130;
      n1668413646_1 = n1321055944;
    }
    else
    {
      n1668413646_1 = -630065159;
    }
  }
  return v9;
}

后面两个就分别是rc4的初始化函数和加密函数了

__int64 __fastcall rc4_init(__int64 a1, __int64 a2, int a3)
{
  __int64 n2031415971_1; // rax
  int n2031415971_2; // edx
  int n2031415971_3; // edx
  int n2031415971; // [rsp+1Ch] [rbp-134h]
  char s[256]; // [rsp+20h] [rbp-130h] BYREF
  int v8; // [rsp+120h] [rbp-30h]
  int v9; // [rsp+124h] [rbp-2Ch]
  int n256; // [rsp+128h] [rbp-28h]
  int v11; // [rsp+12Ch] [rbp-24h]
  __int64 key; // [rsp+130h] [rbp-20h]
  __int64 v13; // [rsp+138h] [rbp-18h]
  int n953312896; // [rsp+148h] [rbp-8h]
  int n953312896_1; // [rsp+14Ch] [rbp-4h]

  n953312896 = 953312896;
  n953312896_1 = 953312896;
  v13 = a1;
  key = a2;
  v11 = a3;
  memset(s, 0, sizeof(s));
  n256 = 0;
LABEL_25:
  n2031415971_2 = n953312896_1 + 1078103075;
  if ( n256 < 256 )
    n2031415971_2 = n953312896_1 + 1421397418;
  n2031415971 = n2031415971_2;
  do
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( n2031415971 < -776628777 )
        {
          if ( n2031415971 < -1126335090 )
          {
            if ( n2031415971 < -1342440884 )
            {
              if ( n2031415971 == -1920256982 )
              {
                *(v13 + n256) = n256;
                s[n256] = *(key + n256 % v11);
                n2031415971 = n953312896_1 - 1738164735;
              }
            }
            else if ( n2031415971 == -1342440884 )
            {
              v8 = (s[n256] + *(v13 + n256) + v8) % 256;
              v9 = (s[n256] + *(v13 + v8) + v8) % 256;
              ((*(qword_406180 + 2067789068) - 675034692LL))(v9 + v13, v8 + v13);
              n2031415971 = n953312896_1 - 1729941673;
            }
          }
          else if ( n2031415971 < -784851839 )
          {
            if ( n2031415971 == -1126335090 )
              goto LABEL_25;
          }
          else if ( n2031415971 == -784851839 )
          {
            ++n256;
            n2031415971 = n953312896_1 - 2079647986;
          }
        }
        if ( n2031415971 >= -628551728 )
          break;
        if ( n2031415971 < -741556009 )
        {
          if ( n2031415971 == -776628777 )
          {
            ++n256;
            n2031415971 = n953312896_1 - 1694868905;
          }
        }
        else if ( n2031415971 == -741556009 )
        {
          n2031415971_3 = n953312896_1 - 1581864624;
          if ( n256 < 256 )
            n2031415971_3 = n953312896_1 + 1999213516;
          n2031415971 = n2031415971_3;
        }
      }
      if ( n2031415971 < 2031415971 )
        break;
      if ( n2031415971 == 2031415971 )
      {
        v8 = 0;
        v9 = 0;
        n256 = 0;
        n2031415971 = n953312896_1 - 1694868905;
      }
    }
    n2031415971_1 = n2031415971;
  }
  while ( n2031415971 != -628551728 );
  return n2031415971_1;
}

加密函数

__int64 __fastcall rc4(char *a1, __int64 a2, int a3, int a4)
{
  int n16635716; // esi
  int n841780568_1; // esi
  int n841780568; // [rsp+1Ch] [rbp-44h]
  int v8; // [rsp+20h] [rbp-40h]
  char v9; // [rsp+26h] [rbp-3Ah]
  char v10; // [rsp+27h] [rbp-39h]
  int index; // [rsp+28h] [rbp-38h]
  int v12; // [rsp+30h] [rbp-30h]
  int v13; // [rsp+34h] [rbp-2Ch]

  v9 = *a1;
  v12 = 0;
  v13 = 0;
  index = 0;
  for ( n841780568 = -1352487153; ; n841780568 = 841780568 )
  {
    while ( 1 )
    {
      while ( n841780568 < -86367832 )
      {
        if ( n841780568 < -498778613 )
        {
          if ( n841780568 < -547120796 )
          {
            n16635716 = -86367832;
            if ( index < a3 )
              n16635716 = 16635716;
            n841780568 = n16635716;
          }
          else
          {
            *(a2 + index) ^= v9 ^ v10;
            v9 = *(a2 + index);
            n841780568 = 1776665005;
          }
        }
        else if ( n841780568 < -134357446 )
        {
          v13 = (v13 + 1) % 256;
          v12 = (a1[v13] + v12) % 256;
          ((*(qword_406188 + 2048138584) + 1461912248LL))(&a1[v13], &a1[v12]);
          v10 = a1[(a1[v12] + a1[v13]) % 256];
          n841780568 = -134357446;
        }
        else
        {
          ++v8;
          n841780568 = 841780568;
        }
      }
      if ( n841780568 < 841780568 )
        break;
      if ( n841780568 < 1776665005 )
      {
        n841780568_1 = -547120796;
        if ( v8 < a4 )
          n841780568_1 = -498778613;
        n841780568 = n841780568_1;
      }
      else
      {
        ++index;
        n841780568 = -1352487153;
      }
    }
    if ( n841780568 < 16635716 )
      break;
    v8 = 0;
  }
  return n841780568;
}

接下来就开始动态调试。断点分别先打在上面的这三个函数,以下如下的一个地址,在这里会进行输入
image-20251026075702200

然后直接F4就会到凯撒加密函数,按30次F4,就会跳转到rc4的初始化函数。通过不断调试尝试,可以发现会经过三次RC4,key的话是image-20251026075908312

然后按F4,跳转到RC4加密函数,虽然有key,但是这是RC4,直接去抓和明文异或的值更方便更省事,于是把断点下在异或的地方,每次异或都跟进去抓这个值

image-20251026080125456

跟进查看v10

image-20251026080147170

继续

image-20251026080218105

重复上述操作,拿到三次异或的值,但是这里还有一个点就是每次明文异或之后,还有一个流异或,后一个异或前一个值,然后第一个也进行了异或,每次都是异或一个固定值0xCD

image-20251026080850377

然后继续调试,最后可以追到密文
main函数
image-20251028101104699

image-20251028101119643

然后编写脚本

def decrypt_layer1():
    print("=== 第一层解密:多层异或解密 ===")

    # 加密后的结果
    encrypted = [
        0xF7, 0x88, 0xC3, 0x29, 0x36, 0x64, 0x63, 0x29,
        0xC7, 0x7F, 0x1C, 0xAB, 0x71, 0xE0, 0x03, 0x49,
        0x73, 0xCB, 0x0A, 0xAF, 0x0C, 0x87, 0x84, 0x8E,
        0x5A, 0x64, 0xC7, 0xAC, 0x2A, 0x67
    ]

    xor1 = [
        0x5b, 0x55, 0x44, 0xc6, 0x74, 0x03, 0x6f, 0xa3, 0xb2, 0x00,
        0x30, 0xe5, 0x71, 0x7e, 0x25, 0x32, 0x5a, 0x46, 0x31, 0xed,
        0x6a, 0x1f, 0xd2, 0xd5, 0x04, 0x4a, 0xb2, 0x74, 0xdf, 0xad
    ]

    xor2 = [
        0x97, 0x74, 0x20, 0x00, 0xeb, 0x25, 0x3e, 0xed, 0x10, 0x04,
        0x84, 0xad, 0xba, 0xf5, 0x7f, 0xbf, 0x96, 0x94, 0x1a, 0x84,
        0x92, 0x07, 0xce, 0x46, 0x09, 0xd9, 0x36, 0xe8, 0x29, 0x36
    ]

    xor3 = [
        0x83, 0x6f, 0x8e, 0x7e, 0x3e, 0x6a, 0x63, 0x74, 0x9c, 0xf5,
        0xfe, 0x77, 0x5b, 0x59, 0x92, 0x3c, 0x4d, 0xff, 0x31, 0xe8,
        0x41, 0x02, 0x83, 0xa4, 0x27, 0x72, 0x60, 0xbd, 0x7a, 0x54
    ]

    qwq = [0] * 30

    # 复制加密结果
    for i in range(30):
        qwq[i] = encrypted[i]

    # 第4层解密:逆向 CBC -> 逆向 0xCD -> 逆向 xor3
    for i in range(29, 0, -1):
        qwq[i] = qwq[i] ^ qwq[i-1]
    qwq[0] ^= 0xcd
    for i in range(30):
        qwq[i] = qwq[i] ^ xor3[i]

    # 第3层解密:逆向 CBC -> 逆向 0xCD -> 逆向 xor2
    for i in range(29, 0, -1):
        qwq[i] = qwq[i] ^ qwq[i-1]
    qwq[0] ^= 0xcd
    for i in range(30):
        qwq[i] = qwq[i] ^ xor2[i]

    # 第2层解密:逆向 CBC -> 逆向 0xCD -> 逆向 xor1
    for i in range(29, 0, -1):
        qwq[i] = qwq[i] ^ qwq[i-1]
    qwq[0] ^= 0xcd
    for i in range(30):
        qwq[i] = qwq[i] ^ xor1[i]

    # 输出结果
    print("解密后的mio: ", end="")
    for i in range(30):
        print(chr(qwq[i]), end="")
    print()

    print("十六进制: ", end="")
    for i in range(30):
        print(f"{qwq[i]:02x} ", end="")
    print("\n")


def affine_decrypt(c):
    """仿射密码解密函数"""
    if c.isupper():
        # 大写字母解密: D(x) = 15*(x - 11) mod 26
        x = ord(c) - ord('A')
        decrypted = (15 * (x - 11 + 26)) % 26
        if decrypted < 0:
            decrypted += 26
        return chr(ord('A') + decrypted)
    elif c.islower():
        # 小写字母解密: D(x) = 15*(x - 11) mod 26
        x = ord(c) - ord('a')
        decrypted = (15 * (x - 11 + 26)) % 26
        if decrypted < 0:
            decrypted += 26
        return chr(ord('a') + decrypted)
    elif c.isdigit():
        # 数字解密: D(x) = 3*(x - 11) mod 10
        x = ord(c) - ord('0')
        decrypted = (3 * (x - 11 + 10)) % 10
        if decrypted < 0:
            decrypted += 10
        return chr(ord('0') + decrypted)
    else:
        # 其他字符不变
        return c


def decrypt_layer2():
    print("=== 第二层解密:仿射密码解密 ===")

    encrypted = "uklb{a1vYg_Az9_Luu8ynNyz8xm0!}"
    decrypted = ""

    print(f"加密文本: {encrypted}")

    # 解密每个字符
    for char in encrypted:
        decrypted += affine_decrypt(char)

    print(f"解密结果: {decrypted}\n")


def affine_encrypt(c):
    """仿射密码加密函数(用于验证)"""
    if c.isupper():
        # 大写字母加密: E(x) = (7*x + 11) mod 26
        x = ord(c) - ord('A')
        encrypted = (7 * x + 11) % 26
        return chr(ord('A') + encrypted)
    elif c.islower():
        # 小写字母加密: E(x) = (7*x + 11) mod 26
        x = ord(c) - ord('a')
        encrypted = (7 * x + 11) % 26
        return chr(ord('a') + encrypted)
    elif c.isdigit():
        # 数字加密: E(x) = (7*x + 11) mod 10
        x = ord(c) - ord('0')
        encrypted = (7 * x + 11) % 10
        if encrypted < 0:
            encrypted += 10
        return chr(ord('0') + encrypted)
    else:
        return c


def main():
    # 执行第一层解密(多层异或)
    decrypt_layer1()

    # 执行第二层解密(仿射密码)
    decrypt_layer2()


if __name__ == "__main__":
    main()

flag{r0uNd_Rc4_Aff1neEnc1yp7!}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1621925986@qq.com

💰

×

Help us with donation