某国赛CTF逆向题目Writeup:re2

目录

脱壳

010打开

UPX改了特征码,版本5.1.0

直接拖到ida分析不出来

这里我们选择动态调试脱壳,先找OEP,f9运行到入口

触发TLS回调函数断点,停在了ret上

64位UPX壳中不会用一个明显的跨段jmp指令跳向OEP,而是把OEP的地址压入堆栈,然后执行一条ret指令,这样CPU就会把堆栈顶部的OEP地址当作返回地址弹出来,并跳转过去

我们直接f8过去

此时进入了系统DLL地址,不用管,执行到用户代码

标准的UPX壳保存环境的方式,步过4次

此时当前的栈顶已经被修改了,内存中转到RSP,在栈顶位置下硬件断点,然后一直f9到触发硬件断点

此时正在恢复保存的寄存器状态,最后的jmp即是尾部跳转,直接f4过去,然后步过一下

可以看到已经解密完成,0x4014E0就是OEP

打开Scylla插件搜索IAT

找到两个IAT大小,因为这个exe只有几十kb,528字节的输入表正好,选择否,然后获取输入表

dump之后修复得到最终脱壳后的程序

第一层EXE

通过ida逆向

可以看到这里是获取了用户的输入并和Str2进行比较,Str 为 NGeQwv8eCRpINEcO

标准的windows可执行文件头

如果正确就会通过sub_401660函数进行base64解码并向缓存路径内写入一个随机名称的exe,直接执行原文件输入密码就能得到

第二层EXE

运行发现需要输入key

继续通过ida逆向这个文件,通过main函数找到处理逻辑(sub_401550函数)

分析可知,这里的逻辑也是接收用户输入的key,然后通过nullsub_3函数判断key是否正确

中间的do-while步骤是优化后的strlen函数实现,输入长度不超过240

现在我们的思路就是分析nullsub_3函数,不过nullsub在ida中代表为只有一个ret的空函数,说明针对反编译器做了修改,我们需要看看到底是不是真的空函数

跟进后发现是一个奇怪的hello节,应该就是题目提示了

这里我们选择通过x64dbg进行动态调试,首先加载后找到主逻辑函数地址

查找Enter the key关键字引用

ida中找到地址

输出Faild和Success中间的call就是,直接跳转然后下断点0x401623

然后一直f9运行到等待用户输入,返回窗口随便输入一串key:12345678

跟进

这里可以看到明显是有内容的,分析逻辑如下:

1、逐字节读取刚刚输入的key

2、计算离16字节的填充并补齐,经典的PKCS#7填充

3、为404CB0函数准备参数(rcx、r9、r8)

基本可以判断是AES或者SM4

运行到404F86(调用404CB0函数之前)拿到三个参数

复制代码
Key: C2 30 12 AB 39 10 18 33 F8 ED 4E 46 8D A1 5D 8D 8C FB F0 72 68 99 DC 7C 84 6E 7E CF 32 BB DA F8

IV: AE BA 0D BB CA 26 7F 99 06 ED 7C 70 E3 8D 8B 11

CipherText: 9B 5E 1E 8F D7 C3 43 62 A2 37 86 C0 CE 3D 3C F4 C3 B6 88 FF 3C 9C 13 D2 BB 6F 49 CE FF 59 A2 5C 36 E4 61 9E 60 61 C3 BB 3F 63 AF 00 3B 3D 8D A7

然而AES和SM4解密都失败了,说明题目对标准加密流程进行了魔改

跟进404CB0函数内部:

这里往栈上压入了一个参数E(十进制14),而加密轮数14的分组密码只有AES,我们上一步解密失败了,最大的可能就是魔改了标准的S盒

往下看,压栈后调用了404B60函数,直接跟进

这里就是AES的核心算法,先把内存地址 4071E0 装载到了 r14 寄存器里,然后把明文字节当做索引 rdx,去 r14 也就是 4071E0 这个地址里查表替换,这就是 AES 加密中最核心的字节替换操作,那么4071E0地址就是魔改S盒的地址

然而进去后发现就是标准的S盒

接下来就该排查行移位ShiftRows和列混淆MixColumns步骤有没有问题

这里调用了2个函数,分别跟进看一下

404070:

0, 5, 10, 15、13, 4, 9, 14、8, 13, 2, 7

正确对应行移位后的矩阵排列

404190:

0x1B 是 AES 有限域乘法的标准多项式常数,2 和 3 也是标准的乘法矩阵

到这里我们总结一下:

1、标准S盒

2、标准行移位

3、标准列混淆

还剩最后一个密钥扩展算法,这里我们想要重新分析算法就比较复杂了,但是最终是要生成好放进内存,直接在密钥生成器之后在内存中获取即可

重新运行跟进到404B60内部:

在字节替换操作之前,调用了一个404940函数,多半就是轮密钥的生成逻辑,运行到此处步过一次,而生成的轮密钥随后就被存放在了rbx寄存器指向的地址,内存窗口中找到

正好240字节

复制代码
C2 30 12 AB 39 10 18 33 F8 ED 4E 46 8D A1 5D 8D 8C FB F0 72 68 99 DC 7C 84 6E 7E CF 32 BB DA F8 B4 67 53 88 8D 77 4B BB 75 9A 05 FD F8 3B 58 70 CD 19 9A 23 A5 80 46 5F 21 EE 38 90 13 55 E2 68 58 FF 16 F5 D5 88 5D 4E A0 12 58 B3 58 29 00 C3 A7 BC F9 0D 02 3C BF 52 23 D2 87 C2 30 87 65 AA 5C B2 BA F1 89 3A E7 BF 29 28 BF 0C 71 01 BF CF 04 C0 F1 87 06 FC 4E D5 25 2E C9 17 15 A9 AC BD 9A 23 C0 A8 13 19 27 17 3A 31 98 1B 4B 30 27 D4 B7 C4 3D CF B1 38 73 1A 94 16 BA 0D 81 BF 16 B0 8B 64 27 A4 98 7D 00 B3 A2 4C 98 A8 E9 7C BF 7C A9 D4 35 DF 18 EC 46 C5 8C FA FC C8 0D 45 EA 78 E4 E3 9B 73 7C 9E 9B C0 DE D2 03 68 37 AE BC 14 33 30 50 25 2B DC 16 E0 A7 26 EA 28 AA 63 00 50 2E 80 C8 DF 52 1E 53 1F 8C CC 50 77 BB 62 EC 63

编写自定义轮密钥的解密脚本:

python 复制代码
# 自定义的轮密钥
expanded_key_hex = "C2 30 12 AB 39 10 18 33 F8 ED 4E 46 8D A1 5D 8D 8C FB F0 72 68 99 DC 7C 84 6E 7E CF 32 BB DA F8 B4 67 53 88 8D 77 4B BB 75 9A 05 FD F8 3B 58 70 CD 19 9A 23 A5 80 46 5F 21 EE 38 90 13 55 E2 68 58 FF 16 F5 D5 88 5D 4E A0 12 58 B3 58 29 00 C3 A7 BC F9 0D 02 3C BF 52 23 D2 87 C2 30 87 65 AA 5C B2 BA F1 89 3A E7 BF 29 28 BF 0C 71 01 BF CF 04 C0 F1 87 06 FC 4E D5 25 2E C9 17 15 A9 AC BD 9A 23 C0 A8 13 19 27 17 3A 31 98 1B 4B 30 27 D4 B7 C4 3D CF B1 38 73 1A 94 16 BA 0D 81 BF 16 B0 8B 64 27 A4 98 7D 00 B3 A2 4C 98 A8 E9 7C BF 7C A9 D4 35 DF 18 EC 46 C5 8C FA FC C8 0D 45 EA 78 E4 E3 9B 73 7C 9E 9B C0 DE D2 03 68 37 AE BC 14 33 30 50 25 2B DC 16 E0 A7 26 EA 28 AA 63 00 50 2E 80 C8 DF 52 1E 53 1F 8C CC 50 77 BB 62 EC 63".replace(" ", "")
iv = bytes.fromhex("AEBA0DBBCA267F9906ED7C70E38D8B11")
ct = bytes.fromhex("9B5E1E8FD7C34362A23786C0CE3D3CF4C3B688FF3C9C13D2BB6F49CEFF59A25C36E4619E6061C3BB3F63AF003B3D8DA7")

expanded_key = bytes.fromhex(expanded_key_hex)
round_keys = [list(expanded_key[i:i+16]) for i in range(0, 240, 16)]

# 标准S盒、逆S盒
SBOX = [
    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
    0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
    0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
    0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
    0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
    0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
    0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
    0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
    0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
    0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
    0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
    0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
    0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
]
INV_SBOX = [0] * 256
for i in range(256): INV_SBOX[SBOX[i]] = i

# 逆向移位
def inv_shift_rows(s):
    return [
        s[0],  s[13], s[10], s[7],
        s[4],  s[1],  s[14], s[11],
        s[8],  s[5],  s[2],  s[15],
        s[12], s[9],  s[6],  s[3]
    ]

def gf_mult(a, b):
    p = 0
    for _ in range(8):
        if b & 1: p ^= a
        hi_bit_set = a & 0x80
        a = (a << 1) & 0xFF
        if hi_bit_set: a ^= 0x1b
        b >>= 1
    return p

def inv_mix_columns(s):
    out = [0] * 16
    for i in range(0, 16, 4):
        c = s[i:i+4]
        out[i]   = gf_mult(0x0e, c[0]) ^ gf_mult(0x0b, c[1]) ^ gf_mult(0x0d, c[2]) ^ gf_mult(0x09, c[3])
        out[i+1] = gf_mult(0x09, c[0]) ^ gf_mult(0x0e, c[1]) ^ gf_mult(0x0b, c[2]) ^ gf_mult(0x0d, c[3])
        out[i+2] = gf_mult(0x0d, c[0]) ^ gf_mult(0x09, c[1]) ^ gf_mult(0x0e, c[2]) ^ gf_mult(0x0b, c[3])
        out[i+3] = gf_mult(0x0b, c[0]) ^ gf_mult(0x0d, c[1]) ^ gf_mult(0x09, c[2]) ^ gf_mult(0x0e, c[3])
    return out

# AES解密
def decrypt_block(block, round_keys):
    state = list(block)
    # 第一步:异或最后一轮密钥 (Round 14)
    state = [a ^ b for a, b in zip(state, round_keys[14])]

    # 中间 13 轮逆向推导
    for round_idx in range(13, 0, -1):
        state = inv_shift_rows(state)
        state = [INV_SBOX[b] for b in state]
        state = [a ^ b for a, b in zip(state, round_keys[round_idx])]
        state = inv_mix_columns(state)

    # 最后一轮 (Round 0)
    state = inv_shift_rows(state)
    state = [INV_SBOX[b] for b in state]
    state = [a ^ b for a, b in zip(state, round_keys[0])]
    return state

# CBC 解密链
pt = bytearray()
prev = list(iv)
for i in range(0, len(ct), 16):
    block = ct[i:i+16]
    dec_block = decrypt_block(block, round_keys)
    pt_block = [a ^ b for a, b in zip(dec_block, prev)]
    pt.extend(pt_block)
    prev = block

# 去除PKCS#7填充
pad_len = pt[-1]
flag = pt[:-pad_len].decode('utf-8', errors='ignore')

print(flag)

FLAG

得到flag:dart{c3d4f5cc-8aab-46ce-a188-2fc453f3b288}

相关推荐
kali-Myon3 小时前
分享一个网络安全 AI 工具导航项目 SecSkills
安全·ai·github·ctf
晚风醉蝶13 小时前
用 Babel AST 把“天书“ JS 一键还原:七步流水线反混淆实战
ast·逆向·反混淆·代码还原·控制流平坦化
蒲公英eric1 天前
从旧接口泄露到 OAuth 保护:DVWA API 模块完整漏洞分析教程
web安全·ai·ctf·dvwa·ai安全·api模块
白猫不黑2 天前
CTF是什么?从零理解一场攻防竞赛
网络·web安全·计算机·网络安全·信息安全·ctf·红蓝对抗
玫幽倩3 天前
2026第二届湾区杯网络安全大赛决赛(AI专项赛道静态题wp)
pytorch·python·ai·agent·ctf·rag·湾区杯
落寞的魚丶5 天前
2026年浙江省信息通信业职业技能竞赛(信息安全测试员竞赛) 初赛Wireup
ctf·鱼影安全·2026网络安全通信行业·信息安全测试员竞赛·通信职业技能大赛
字节暗面9 天前
SO 加固强度自查 Checklist:静态、加载链、运行时看哪几项
安全·逆向
合天网安实验室10 天前
Modbus协议及其取证的学习笔记
ctf·modbus·通信协议·取证
努力的lpp10 天前
php反序列化一(大白话版)
开发语言·web安全·php·ctf·反序列化
蒲公英eric11 天前
从直接重定向到白名单验证:DVWA 开放重定向模块完整漏洞分析教程
web安全·ai·ctf·dvwa·ai安全·开放重定向模块