第八届强网·拟态防御国际精英挑战赛
MOBILE
EZMiniAPP
看名字一眼就是一个小程序,直接用GitHub上的开源项目解包
biggerstar/wedecode: 全自动化,微信小程序 wxapkg 包 源代码还原工具, 线上代码安全审计,支持 Windows, Macos, Linux

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 的时候报错,

然后发现so被加密了,于是就开始找解密的地方。最开始想着想在程序运行的时候dump下来,但是发现好像有Frida 检测,并且也dump不下来,那就只能去看其他的so了。
在libjust.so 中,通过可疑字符串定位到解密函数

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


尝试解密

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

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


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

丢给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 一把梭了

接下俩就是主要对 FlagChecker类进行分析了
然后根据偏移去找加密逻辑。一个魔改的tea

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

把 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);
}

可以根据这个值去 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 打开去找

写脚本解密
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的地方)

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

按道理来说应该可以把算法复现出来,然后写脚本爆破,但是奈何脚本搓不出来,就只能手动爆破了,回到最原始的方式(真是无敌了)。
就这样,试了几个小时,试出来了,然后题目hint中提到flag的md5值,说明可能是多解,但好在试出来的这个刚好就对上了。



flag{m4z3d_vm_jump5__42}
Icall
先静态分析了一下,首先看的是init,发现有反调试
然后直接该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;
}
接下来就开始动态调试。断点分别先打在上面的这三个函数,以下如下的一个地址,在这里会进行输入
然后直接F4就会到凯撒加密函数,按30次F4,就会跳转到rc4的初始化函数。通过不断调试尝试,可以发现会经过三次RC4,key的话是
然后按F4,跳转到RC4加密函数,虽然有key,但是这是RC4,直接去抓和明文异或的值更方便更省事,于是把断点下在异或的地方,每次异或都跟进去抓这个值

跟进查看v10

继续

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

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

然后编写脚本
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