Problem: [GHCTF 2025]腐蚀
@TOC
main函数

ida打开,是rust,进入真正的main函数main_0。
看到明显读入了一个Input.png,但是不知道是在哪里读入的。
动调
随便放个Input.png直接开始动调,每个函数前后都停一下,直到找到读入的图片信息

图片存储地址在rax中,点进去下个硬件断点。

然后开始跑,第一个出发断点的位置:

加密
RC4
看伪代码,发现是RC4,但是最后异或了0x1F,我处理过的代码如下:
v45 = -2i64;
memset(sbox, 0, sizeof(sbox));
v32[0] = sub_7FF7DDAF3D10(0i64, 256i64);
v32[1] = v3;
while ( 1 )
{
v33 = sub_7FF7DDAF22C0(v32);
v34 = v4;
if ( !v33 )
break;
if ( v34 >= 0x100 )
sub_7FF7DDB0FDE4(v34, 256i64, &off_7FF7DDB12240);
*((_BYTE *)sbox + v34) = v34; // sbox生成0-255字节流
}
j = 0i64;
v36[0] = sub_7FF7DDAF3D10(0i64, 256i64);
v36[1] = v5;
while ( 1 )
{
v37 = sub_7FF7DDAF22C0(v36);
i_ = v6;
if ( !v37 )
break;
i__ = i_;
if ( i_ >= 0x100 )
sub_7FF7DDB0FDE4(i_, 256i64, &off_7FF7DDB121B0);
sbox_i = *((unsigned __int8 *)sbox + i_); // sbox_i = *((unsigned __int8 *)sbox + i_);
t_j = sbox_i + j;
if ( __CFADD__(sbox_i, j) )
sub_7FF7DDB100D0(&off_7FF7DDB121C8);
key_len = _except_get_jumpbuf_sp(key);
if ( !key_len )
sub_7FF7DDB10190(&off_7FF7DDB121E0);
key_i = *(unsigned __int8 *)sub_7FF7DDAF5210(key, i__ % key_len, &off_7FF7DDB121F8);// key_i = key[i % key_len]
next_j = key_i + t_j; // next_j = key_i + t_j;
if ( __CFADD__(key_i, t_j) )
sub_7FF7DDB100D0(&off_7FF7DDB12210);
j = next_j;
swap((unsigned int)sbox, 256, i__, next_j, (__int64)&off_7FF7DDB12228);
}
jumpbuf_sp = _except_get_jumpbuf_sp(a3);
sub_7FF7DDAF4DA0((__int64 *)&v39, jumpbuf_sp, (__int64)&off_7FF7DDB120D8);
i = 0i64;
j__ = 0i64;
v7 = sub_7FF7DDAF50B0(a3);
v9 = sub_7FF7DDAF4AC0(v7, v8);
v43[0] = sub_7FF7DDAF5090(v9, v10);
v43[1] = v11;
while ( 1 )
{
png_address = (char *)sub_7FF7DDAF5270(v43);
if ( !png_address )
break;
png_byte = *png_address; // png_byte = *png_address;
i_plus_1 = i + 1;
if ( i == -1i64 )
sub_7FF7DDB100D0(&off_7FF7DDB120F0);
i = i_plus_1; // i = i + 1
sbox_i_1 = *((unsigned __int8 *)sbox + i_plus_1);// sbox_i_1 = *((unsigned __int8 *)sbox + i_plus_1);
new_j = sbox_i_1 + j__; // new_j = sbox_i_1 + j__;
if ( __CFADD__(sbox_i_1, j__) )
sub_7FF7DDB100D0(&off_7FF7DDB12120);
j__ = new_j;
swap((unsigned int)sbox, 256, i, new_j, (__int64)&off_7FF7DDB12138);
if ( i >= 0x100 )
sub_7FF7DDB0FDE4(i, 256i64, &off_7FF7DDB12150);
sbox_i_n = *((unsigned __int8 *)sbox + i); // sbox_i_n = *((unsigned __int8 *)sbox + i);
if ( j__ >= 0x100 )
sub_7FF7DDB0FDE4(j__, 256i64, &off_7FF7DDB12168);
sbox_j_n = *((unsigned __int8 *)sbox + j__);// sbox_j_n = *((unsigned __int8 *)sbox + j__);
if ( __CFADD__(sbox_j_n, sbox_i_n) )
sub_7FF7DDB100D0(&off_7FF7DDB12180);
LOBYTE(v14) = *((_BYTE *)sbox + (unsigned __int8)(sbox_j_n + sbox_i_n)) ^ png_byte ^ 0x1F;// LOBYTE(v14) = *((_BYTE *)sbox + (unsigned __int8)(v15 + v21)) ^ png_byte ^ 0x1F;
sub_7FF7DDAF4F40(&v39, v14, &off_7FF7DDB12198);
}
*(_QWORD *)(a1 + 16) = v40;
*(_OWORD *)a1 = v39;
sub_7FF7DDAF2160(a3);
sub_7FF7DDAF2160(key);
return a1;
理论上把所有异或字节dump出来即可,但是图片太大了,我们就把key找出来

断点段在这,点进去key,地址在8字节处

跳转过去,dump下来:
6082AE424E4449451A0A0D0A4E478950

可以猜到RC4加密结果存储在v39中,断在如图所示处,进入rax,找到地址,打下断点,与刚刚操作类似。
换序
继续跑,断点被触发,查看逻辑

交换了i和a5-1-i的字节,疑似换序,发现a5还刚好是图片字节数的一半,稳辣
脚本
def reverse_file_bytes(input_filename, output_filename):
# 读取输入文件的二进制内容
with open(input_filename, 'rb') as file:
file_data = file.read()
# 将字节数据倒序
reversed_data = bytearray(reversed(file_data))
# 将倒序后的数据写入到输出文件
with open(output_filename, 'wb') as file:
file.write(reversed_data)
input_filename = "C:\\Users\\35202\\Downloads\\fs\\fs\\enc" # 输入文件路径
output_filename = 'C:\\Users\\35202\\Downloads\\fs\\fs\\out' # 输出文件路径
reverse_file_bytes(input_filename, output_filename)
def rc4_decrypt(ciphertext, key):
S = list(range(256))
j = 0
out = []
# KSA - Key Scheduling Algorithm
for i in range(256):
j = (j + S[i] + (key[i % len(key)])) % 256
S[i], S[j] = S[j], S[i]
i = j = 0
for char in ciphertext:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
out.append(chr(ord(char) ^ S[(S[i] + S[j]) % 256] ^ 0x1f))
return ''.join(out)
def decrypt_file(input_filename, output_filename, key):
# 读取输入文件的二进制内容
with open(input_filename, 'rb') as file:
file_data = file.read()
# 将字节数据转换为可处理的格式并解密
decrypted_data = rc4_decrypt(file_data.decode('latin1'), key).encode('latin1')
# 将解密后的数据写入到输出文件
with open(output_filename, 'wb') as file:
file.write(decrypted_data)
input_filename = 'C:\\Users\\35202\\Downloads\\fs\\fs\\out' # 加密文件路径
output_filename = 'C:\\Users\\35202\\Downloads\\fs\\fs\\flag.png' # 解密后文件保存路径
key = bytes.fromhex("6082AE424E4449451A0A0D0A4E478950") # RC4解密所需的密钥
decrypt_file(input_filename, output_filename, key)
美了美了

flag:NSSCTF{Y0u_ar3_ru5t_m@st3r}
