2557 字
13 分钟
第二届rdctf逆向Writeup
Hello_Rust
首先点开exe程序发现
Please input the flag:于是直接搜索对应字符找到对应主函数
__int64 __fastcall Hello_Rust::main::h72897955569e0b8d(__int64 a1, __int64 a2){ _QWORD *v2; // rax int v3; // r8d int v4; // r9d __int64 v5; // rdx __int64 v6; // rdx void *v7; // rax void *v8; // rdx int v9; // r9d int v10; // r8d int v11; // r9d _QWORD *v12; // rax void *v13; // rsi __int64 v14; // rdx __int64 *v15; // r13 char *v16; // rbp char *v17; // r9 __int64 v18; // r8 __int64 v19; // rcx char v20; // r9 __int64 (__fastcall **v21)(size_t *, void *, __int64, _QWORD); // rax __int64 v22; // r10 __int64 v23; // rdx __int64 v24; // rcx unsigned __int8 v25; // al _BYTE *v26; // rcx __int64 result; // rax __int64 v29; // [rsp+30h] [rbp-268h] BYREF __int64 v30; // [rsp+38h] [rbp-260h] __int64 v31; // [rsp+40h] [rbp-258h] const char *v32; // [rsp+48h] [rbp-250h] BYREF __int64 v33; // [rsp+50h] [rbp-248h] __int64 v34; // [rsp+58h] [rbp-240h] size_t Size; // [rsp+60h] [rbp-238h] BYREF const char *v36; // [rsp+68h] [rbp-230h] __int64 v37[2]; // [rsp+70h] [rbp-228h] BYREF __int64 (__fastcall **v38)(size_t *, void *, _QWORD, _QWORD); // [rsp+80h] [rbp-218h] __int64 v39; // [rsp+88h] [rbp-210h] __int64 v40; // [rsp+90h] [rbp-208h] __int64 v41; // [rsp+98h] [rbp-200h] __int64 v42; // [rsp+A0h] [rbp-1F8h] BYREF char v43; // [rsp+A8h] [rbp-1F0h] BYREF __int64 (__fastcall **v44)(size_t *, void *, __int64, _QWORD); // [rsp+B0h] [rbp-1E8h] BYREF __int64 (__fastcall **v45)(size_t *, void *, __int64, _QWORD); // [rsp+B8h] [rbp-1E0h] __int64 (__fastcall **v46)(size_t *, void *, __int64, _QWORD); // [rsp+C0h] [rbp-1D8h] __int64 (__fastcall **v47)(size_t *, void *, __int64, _QWORD); // [rsp+C8h] [rbp-1D0h] __int64 v48; // [rsp+D0h] [rbp-1C8h] __int64 v49; // [rsp+D8h] [rbp-1C0h] char v50; // [rsp+E0h] [rbp-1B8h] BYREF char v51; // [rsp+E8h] [rbp-1B0h] BYREF __int64 v52; // [rsp+F0h] [rbp-1A8h] char *v53; // [rsp+F8h] [rbp-1A0h] const char *v54; // [rsp+100h] [rbp-198h] __int64 v55; // [rsp+108h] [rbp-190h] __int64 v56; // [rsp+110h] [rbp-188h] __int64 *v57; // [rsp+118h] [rbp-180h] __int128 v58; // [rsp+120h] [rbp-178h] __int64 v59; // [rsp+130h] [rbp-168h] __int64 v60; // [rsp+138h] [rbp-160h] BYREF _QWORD *v61; // [rsp+140h] [rbp-158h] __int64 v62; // [rsp+148h] [rbp-150h] __int64 v63; // [rsp+150h] [rbp-148h] BYREF void *Src; // [rsp+158h] [rbp-140h] __int64 v65; // [rsp+160h] [rbp-138h] void *v66; // [rsp+168h] [rbp-130h] BYREF __int64 v67; // [rsp+170h] [rbp-128h] size_t p_Size; // [rsp+178h] [rbp-120h] __int64 v69; // [rsp+180h] [rbp-118h] __int64 v70; // [rsp+188h] [rbp-110h] __int64 v71; // [rsp+198h] [rbp-100h] __int64 v72[11]; // [rsp+240h] [rbp-58h] BYREF
v2 = (_QWORD *)RNvCs691rhTbG0Ee_7___rustc12___rust_alloc(a1, a2, 8LL, 128LL); if ( !v2 ) alloc::alloc::handle_alloc_error::h3c5fc2000323952a(a1, a2, 128LL, 8LL); *v2 = "Please inputtheflag:"; v2[1] = 6LL; v2[2] = " inputtheflag:"; v2[3] = 1LL; v2[4] = "inputtheflag:"; v2[5] = 5LL; v2[6] = " inputtheflag:"; v2[7] = 1LL; v2[8] = "theflag:"; v2[9] = 3LL; v2[10] = " inputtheflag:"; v2[11] = 1LL; v2[12] = "flag:"; v2[13] = 5LL; v2[14] = " inputtheflag:"; v2[15] = 1LL; v66 = v2; v67 = (__int64)v2; p_Size = 8LL; v69 = (__int64)(v2 + 16); v70 = 0LL; v71 = 0LL; _$LT$alloc..string..String$u20$as$u20$core..iter..traits..collect..FromIterator$LT$char$GT$$GT$::from_iter::hb4f7ccd8f8c26ef4( a1, (unsigned int)v72, (unsigned int)&v66, (unsigned int)v72, v3, v4); Size = (size_t)v72; v36 = (const char *)_$LT$alloc..string..String$u20$as$u20$core..fmt..Display$GT$::fmt::hfdc0cc6c70cf0f9f; v66 = &unk_1400A20E8; v67 = 1LL; v70 = 0LL; p_Size = (size_t)&Size; v69 = 1LL; std::io::stdio::_print::hb423c4c57e4fd8e1(a1, v72, v5, &v66); Size = std::io::stdio::stdout::h802387c29eff3b4c(); v7 = (void *)_$LT$std..io..stdio..Stdout$u20$as$u20$std..io..Write$GT$::flush::hcff5a89562c9cbf7(a1, v72, v6, &Size); if ( v7 ) { v66 = v7; core::result::unwrap_failed::hf125793ccbd5d840( a1, (unsigned int)v72, 22, (unsigned int)"Failed to flush stdoutsrc\\main.rs", (unsigned int)&v66, (unsigned int)&off_1400A2090); } v29 = 0LL; v30 = 1LL; v31 = 0LL; Size = std::io::stdio::stdin::hae0488c3f631d306(); if ( (std::io::stdio::Stdin::read_line::h51906b3109204308(a1, v72, &v29, &Size) & 1) != 0 ) { v66 = v8; core::result::unwrap_failed::hf125793ccbd5d840( a1, (unsigned int)v72, 19, (unsigned int)"Failed to read line", (unsigned int)&v66, (unsigned int)&off_1400A2090); } v66 = (void *)v30; v67 = v30 + v31; LOBYTE(p_Size) = 0; _$LT$alloc..vec..Vec$LT$T$GT$$u20$as$u20$alloc..vec..spec_from_iter..SpecFromIter$LT$T$C$I$GT$$GT$::from_iter::he73d87c5766bf13f( a1, v72, &v66, &Size, &off_1400A2078); v66 = (void *)v36; v67 = (__int64)v36; p_Size = Size; v69 = (__int64)&v36[4 * v37[0]]; LOBYTE(v70) = 0; _$LT$alloc..vec..Vec$LT$T$GT$$u20$as$u20$alloc..vec..spec_from_iter..SpecFromIter$LT$T$C$I$GT$$GT$::from_iter::h3e8df7d6d04c157d( (unsigned int)&Size, (unsigned int)v72, (unsigned int)&v66, (unsigned int)&Size, (unsigned int)&off_1400A2078, v9); v66 = (void *)v36; v67 = (__int64)v36; p_Size = Size; v69 = (__int64)&v36[4 * v37[0]]; _$LT$alloc..string..String$u20$as$u20$core..iter..traits..collect..FromIterator$LT$char$GT$$GT$::from_iter::hf11d0f788b4382c5( (unsigned int)&Size, (unsigned int)v72, (unsigned int)&v66, (unsigned int)&v63, v10, v11); v12 = (_QWORD *)RNvCs691rhTbG0Ee_7___rustc12___rust_alloc(&Size, v72, 8LL, 32LL); if ( !v12 ) alloc::alloc::handle_alloc_error::h3c5fc2000323952a(&Size, v72, 32LL, 8LL); *v12 = _$LT$Hello_Rust..XorOp$u20$as$u20$Hello_Rust..Operation$GT$::apply::h39ba18a00a5e6aef; v12[1] = _$LT$Hello_Rust..AddOp$u20$as$u20$Hello_Rust..Operation$GT$::apply::hb8ca4846c60bec8c; v12[2] = _$LT$Hello_Rust..SubOp$u20$as$u20$Hello_Rust..Operation$GT$::apply::hc33f064d2a20566c; v12[3] = _$LT$Hello_Rust..IdxXorOp$u20$as$u20$Hello_Rust..Operation$GT$::apply::h6761405cf33cc523; v60 = 4LL; v61 = v12; v62 = 4LL; v13 = Src; v66 = Src; v67 = (__int64)Src + v65; p_Size = 0LL; LOBYTE(v69) = 0; _$LT$alloc..vec..Vec$LT$T$GT$$u20$as$u20$alloc..vec..spec_from_iter..SpecFromIter$LT$T$C$I$GT$$GT$::from_iter::h78fb51c0aaaccf06( &Size, Src, &v66, &v32, &off_1400A2078); if ( v34 == 30 ) { v15 = &v42; v16 = &v43; v17 = (char *)v33; v18 = v33 + 30; v58 = 0LL; v59 = 0LL; Size = (size_t)&unk_1400A20B0; v36 = "Please inputtheflag:"; v37[0] = 0LL; v44 = 0LL; v52 = v33; v53 = (char *)v33; v54 = v32; v55 = v33 + 30; v56 = 0LL; v57 = &v60; v19 = v33; while ( v17 != (char *)v18 ) { v53 = v17 + 1; v20 = *v17; v14 = v56++; v21 = (__int64 (__fastcall **)(size_t *, void *, __int64, _QWORD))v57[1]; v22 = (__int64)&v21[v57[2]]; v37[0] = (__int64)v21; v37[1] = v22; v38 = v21; v39 = v22; v40 = v14 & 3; v41 = 1LL; v42 = v14; v43 = v20; v17 = v53; if ( v21 ) { v41 = 0LL; if ( (v14 & 3) != 0 ) { v40 = 0LL; v21 = (__int64 (__fastcall **)(size_t *, void *, __int64, _QWORD))core::iter::traits::iterator::Iterator::nth::h47ff4efa2abd51fb( &Size, v13, v14 & 3, v37, v18, v53); if ( v21 ) goto LABEL_26; v19 = v52; v37[0] = 0LL; if ( !v52 ) break; } else { if ( v21 != (__int64 (__fastcall **)(size_t *, void *, __int64, _QWORD))v22 ) { v38 = v21 + 1; goto LABEL_26; } v38 = v21; v39 = v22; v37[0] = 0LL; if ( !v19 ) break; } v17 = v53; v18 = v55; } } if ( !v44 || !v49 ) goto LABEL_36; --v49; v23 = v48; if ( v48 ) { v48 = 0LL; v21 = (__int64 (__fastcall **)(size_t *, void *, __int64, _QWORD))core::iter::traits::iterator::Iterator::nth::h47ff4efa2abd51fb( &Size, v13, v23, &v44, v18, v17); if ( !v21 ) goto LABEL_36; } else { v21 = v46; if ( v46 == v47 ) { v14 = (__int64)v45; v47 = v45; v21 = v44; if ( v44 == v45 ) goto LABEL_36; } v46 = v21 + 1; } v15 = (__int64 *)&v50; v16 = &v51;LABEL_26: v25 = (*v21)(&Size, v13, *v15, (unsigned __int8)*v16); v26 = (_BYTE *)Size; v14 = (__int64)v36; if ( (const char *)Size != v36 ) { ++Size; LOBYTE(v16) = *v26 == v25; memcpy(&Size, v13, (size_t)&Size); if ( (unsigned __int8)_$LT$core..iter..adapters..map..Map$LT$I$C$F$GT$$u20$as$u20$core..iter..traits..iterator..Iterator$GT$::fold::h3f0587534a09e2ec( &Size, v13, (unsigned int)v16, &v66) ) { v32 = "Congratulations! The flag is correct."; v33 = 37LL; Size = (size_t)&v32; v36 = (const char *)_$LT$$RF$T$u20$as$u20$core..fmt..Display$GT$::fmt::h31fa4d9b908ac590; v66 = &anon_ad88cb96a3fdcaf8c94331b0da292800_3_llvm_3440038967210771795; v67 = 2LL; v70 = 0LL; p_Size = (size_t)&Size; v69 = 1LL; result = std::io::stdio::_print::hb423c4c57e4fd8e1(&Size, v13, v14, &v66); goto LABEL_40; } goto LABEL_39; }LABEL_36: v24 = v52; if ( !v52 ) goto LABEL_39; v14 = (__int64)v54; if ( !v54 ) goto LABEL_39; goto LABEL_38; } v14 = (__int64)v32; if ( v32 ) { v24 = v33;LABEL_38: RNvCs691rhTbG0Ee_7___rustc14___rust_dealloc(&Size, v13, v14, v24, 1LL); }LABEL_39: v32 = (const char *)&unk_1400A2168; v33 = 24LL; Size = (size_t)&v32; v36 = (const char *)_$LT$$RF$T$u20$as$u20$core..fmt..Display$GT$::fmt::h31fa4d9b908ac590; v66 = &anon_ad88cb96a3fdcaf8c94331b0da292800_3_llvm_3440038967210771795; v67 = 2LL; v70 = 0LL; p_Size = (size_t)&Size; v69 = 1LL; result = std::io::stdio::_print::hb423c4c57e4fd8e1(&Size, v13, v14, &v66);LABEL_40: if ( v60 ) result = RNvCs691rhTbG0Ee_7___rustc14___rust_dealloc(&Size, v13, 8 * v60, v61, 8LL); if ( v63 ) result = RNvCs691rhTbG0Ee_7___rustc14___rust_dealloc(&Size, v13, v63, v13, 1LL); if ( v29 ) result = RNvCs691rhTbG0Ee_7___rustc14___rust_dealloc(&Size, v13, v29, v30, 1LL); if ( v72[0] ) return RNvCs691rhTbG0Ee_7___rustc14___rust_dealloc(&Size, v13, v72[0], v72[1], 1LL); return result;}
// XorOpchar __fastcall apply(__int64 a1, __int64 a2, __int64 a3, char a4) {return a4 ^ 0x37;}// AddOp__int64 __fastcall apply(__int64 a1, __int64 a2, __int64 a3, int a4) {return (unsigned int)(a4 + 20);}// SubOp__int64 __fastcall apply(__int64 a1, __int64 a2, __int64 a3, int a4) {return (unsigned int)(a4 - 5);}// IdxXorOpchar __fastcall apply(__int64 a1, __int64 a2, char a3, char a4) {return a4 ^ a3; // a3 是索引}之后直接看校验发现v34 == 30,加密后的flag是unk_1400A20B0处的数据
之后根据主函数逻辑写解密脚本
# 加密后的数据(30字节)encrypted = [0x65, 0x58, 0x3e, 0x57, 0x71, 0x8f, 0x4d, 0x72, 0x02, 0x88,0x5a, 0x3a, 0x43, 0x47, 0x6d, 0x3b, 0x43, 0x44, 0x6d, 0x60,0x68, 0x48, 0x6d, 0x24, 0x68, 0x57, 0x2b, 0x2b, 0x5b, 0x91]# 解密flag = []for i, c in enumerate(encrypted):op = i % 4if op == 0:flag.append(chr(c ^ 0x37)) # 逆XorOpelif op == 1:flag.append(chr((c - 20) & 0xFF)) # 逆AddOpelif op == 2:flag.append(chr((c + 5) & 0xFF)) # 逆SubOpelif op == 3:flag.append(chr(c ^ i)) # 逆IdxXorOpprint('Flag:', ''.join(flag))奶龙爱喝花茶
先拿exeinfo看一眼发现没加壳,之后直接搜字符串
Input flag:本打算定位到主函数,却发现里面充斥各种反反汇编代码混淆
0x411BE5: call loc_411BEB0x411BEB: add [esp], 0Ch ; 修改返回地址0x411BF0: retn ; 跳转到 0x411BF6这种技术通过修改栈上的返回地址来隐藏真正的代码流程,使 IDA 无法正确识别函数边界。 实际执行路径从 0x411BF6 开始:
0x411BF6: lea eax, [ebp-18h] ; 获取 key 地址0x411BF9: push eax0x411BFA: call 0x4119F0 ; 调用 key 初始化函数修复前
修复后

之后发现需要修复的太多了于是,只修复了部分关键函数,直接看加密逻辑
最后梳理后
输入 (24字节)↓分成 3 个 8 字节块↓每块转换为 2 个 DWORD (小端序)↓预处理↓TEA 加密↓存储结果解密脚本
import structdef tea_decrypt(v0, v1, key):"""TEA 解密"""delta = 0x9E3779B9sum_val = (delta * 32) & 0xFFFFFFFFfor i in range(32):v1 = (v1 - (((key[3] + (v0 >> 5)) ^ (sum_val + v0) ^ (key[2] + ((v0 << 4) & 0xFFFFFFFF))))) & 0xFFFFFFv0 = (v0 - (((key[1] + (v1 >> 5)) ^ (sum_val + v1) ^ (key[0] + ((v1 << 4) & 0xFFFFFFFF))))) & 0xFFFFFFsum_val = (sum_val - delta) & 0xFFFFFFFFreturn v0, v1def rol(val, r_bits, max_bits=32):return ((val << r_bits) | (val >> (max_bits - r_bits))) & ((1 << max_bits) - 1)def ror(val, r_bits, max_bits=32):return ((val >> r_bits) | (val << (max_bits - r_bits))) & ((1 << max_bits) - 1)# 正确的 TEA 密钥(动态计算得到)key = [0xFD56276F, 0x725BE59E, 0x03F240A5, 0xEA6E72F5]# sub_411E20 使用的数组v3 = [0x12345678, 0x9ABCDEF0, 0x0FEDCBA9, 0x87654321]# 密文ciphertext = [0x2E06E9DC, 0x11469DD0, 0x481028F0, 0x83FB8766, 0x2EBBAB8C, 0xBC51B3AF]# Step 1: 计算加密后的期望值encrypted_input = []for i in range(6):idx = i & 3shift = (5 * i + 3) & 0x1Fmask = rol(v3[idx], shift) ^ 0xDEADBEEFenc = mask ^ ciphertext[i]encrypted_input.append(enc)# Step 2: TEA 解密decrypted_blocks = []for i in range(3):v0 = encrypted_input[i*2]v1 = encrypted_input[i*2+1]p0, p1 = tea_decrypt(v0, v1, key)decrypted_blocks.append((p0, p1))# Step 3: 逆向预处理flag_bytes = b''for p0, p1 in decrypted_blocks:orig_v0 = p0 ^ 0xA5A5A5A5orig_v1 = ror(p1, 7) ^ 0x3C3C3C3Cflag_bytes += struct.pack('<I', orig_v0)flag_bytes += struct.pack('<I', orig_v1)print(f"Flag: RDCTF{{{flag_bytes.decode()}}}")UPX
首先通过exeinfo发现有upx魔改壳
[Tampared file] x64 UPX v3.9 - 5.0 - [4.24]Don't try: upx.exe -d option
发现UPX特征字段都被改成小写了
之后使用Python脚本修复:
with open("original.exe", "rb") as f:data = bytearray(f.read())# 修复签名和段名data[0x3E0:0x3E4] = b'UPX!' # upx! -> UPX!data[0x1F8:0x1FC] = b'UPX0' # upx0 -> UPX0data[0x220:0x224] = b'UPX1' # upx1 -> UPX1with open("fixed.exe", "wb") as f:f.write(data)修复后使用标准UPX工具成功脱壳
分析脱壳后程序发现是一个魔改AES加密(有额外的XOR)
解密脚本
SBOX = [0x63, 0x7c, 0x77, 0x7b, ...] # 标准AES S-BoxINV_SBOX = [0] * 256for i in range(256):INV_SBOX[SBOX[i]] = idef inv_mix_columns_modified(state):# 关键:先撤销XOR 0x7Cstate = bytes([b ^ 0x7c for b in state])# 然后执行标准InvMixColumns# ... 标准实现 ...return resultdef decrypt_block(ciphertext, expanded_key):state = ciphertextstate = add_round_key(state, expanded_key[160:176])for round in range(9, 0, -1):state = inv_shift_rows(state)state = inv_sub_bytes(state)state = add_round_key(state, expanded_key[round*16:(round+1)*16])state = inv_mix_columns_modified(state) # 使用修改版state = inv_shift_rows(state)state = inv_sub_bytes(state)state = add_round_key(state, expanded_key[0:16])return state# 解密key = b'rDctf_AES_key_16'ciphertext = bytes.fromhex('341359569651ffae3ed8d2b53be64b3452ba52e9a684b4ef4f39d5bcb1bb68b0')expanded_key = key_expansion(key)pt1 = decrypt_block(ciphertext[:16], expanded_key)pt2 = decrypt_block(ciphertext[16:], expanded_key)print("Flag:", (pt1 + pt2).decode())# Output: RDCTF{MixColXor7C_AES_Rev_2026!}ezbase
无壳且逻辑非常简单
- 读入输入串 Buffer
- sub_1400117D0 做前向异或: buf[i] ^= buf[i+1] ,最后一字节不变
- sub_140011890 用自定义 Base64 字表编码
- 与内置字符串 Str2 比较,相同则输出 Correct!
自定义 Base64 字表:
QWERTYUIOPASDFGHJKLZXCVBNMabcdefghijklmnopqrstuvwxyz0123456789+/内置 Str2 :
YgcBTj0DKQPLX11ebUgIby0VW1KKGKxzBlhTKgFEKQXRWgYeV2omLXXqaJXTaUhfV2wtJwdeB2g5CwXEYwQQBI0=解法思路:先用自定义字表把 Str2 解码为字节流,再从末尾逆推还原原串(因为正向是 buf[i] ^= buf[i+1] ,所以逆向是 orig[i] = t[i] ^ orig[i+1] )
解密脚本
import base64alphabet = b"QWERTYUIOPASDFGHJKLZXCVBNMabcdefghijklmnopqrstuvwxyz0123456789+/"std = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"trans = bytes.maketrans(alphabet, std)enc = b"YgcBTj0DKQPLX11ebUgIby0VW1KKGKxzBlhTKgFEKQXRWgYeV2omLXXqaJXTaUhfV2wtJwdeB2g5CwXEYwQQBI0="raw = base64.b64decode(enc.translate(trans))n = len(raw)orig = bytearray(n)orig[-1] = raw[-1]for i in range(n - 2, -1, -1):orig[i] = raw[i] ^ orig[i + 1]print(orig.decode())ezpy
有点懒得说了,直接pyinstxtractor.py 对 exe 进行解包,之后反编译pyc文件
发现是一个RC4加密
key = b"nailong"target =bytes.fromhex("2ef01fece0420e0e3d2179e97f26eeab020a32ef51e2a73b2ab6d550196858fa89cf284397df709afc")解密脚本
from binascii import unhexlifydef rc4(data: bytes, key: bytes) -> bytes:s = list(range(256))j = 0for i in range(256):j = (j + s[i] + key[i % len(key)]) & 0xFFs[i], s[j] = s[j], s[i]i = j = 0out = bytearray()for b in data:i = (i + 1) & 0xFFj = (j + s[i]) & 0xFFs[i], s[j] = s[j], s[i]k = s[(s[i] + s[j]) & 0xFF]out.append(b ^ k)return bytes(out)key = b"nailong"target = unhexlify("2ef01fece0420e0e3d2179e97f26eeab020a32ef51e2a73b2ab6d550196858fa89cf284397df709afc")print(rc4(target, key).decode()) 分享
如果这篇文章对你有帮助,欢迎分享给更多人!
第二届rdctf逆向Writeup
https://chaojixin.ren/posts/第二届rdctf逆向wp/ 部分信息可能已经过时









