[NUAACTF-2017] [Reverse]快速解nuaactf

小五义 2026-07-12 09:21:36 5 0


JAR 文件逆向与多阶段爆破


一、环境准备与文件分析

拿到 `.jar` 文件,先用 `unzip` 解压查看结构,发现 `com/company` 包下共 11 个 class 文件,其中 `Main` 是入口类。使用 `jadx` 反编译,得到可读 Java 源码。

二、第一阶段:爆破 4 字符 Key


1、
关键代码分析

Main.java 核心逻辑:

String in = sc.nextLine();           // 读 4 字符输入
String t = new String(in.getBytes());
for (byte e : encode.digest(t.getBytes())) {  // SHA-1 哈希
    password = password + String.format("%02x", Byte.valueOf(e));
}
if (Check.checkPassword(password)) { ... }

Check.java 目标哈希:

private static String t = "caf4cbafdf72ce0f2f2eadc4309916e8c96f0de8";


2、验证 SHA-1 实现正确性

Nazo.java实现了完整 SHA-1,但存在填充差异。先用标准向量验证:

import hashlib
h = hashlib.sha1(b"abc").hexdigest()

确认目标哈希就是标准 SHA-1(Nazo 的自定义填充不影响短字符串结果)。

结果:a9993e364706816aba3e25717850c26c9cd0d89d

3、暴力破解

import hashlib, string

target = "caf4cbafdf72ce0f2f2eadc4309916e8c96f0de8"
chars = string.printable.strip()  # 95 个可打印字符

for c1 in chars:
    for c2 in chars:
        for c3 in chars:
            for c4 in chars:
                key = c1 + c2 + c3 + c4
                if hashlib.sha1(key.encode()).hexdigest() == target:
                    print(f"FOUND: {key}")  # 输出 mdzz

结果:Key = mdzz


三、第二阶段:ClassLoader 动态解密

1、ClassLoader 机制分析

a14b64a0683594003b4efe8a2285acd8.java继承ClassLoader,核心方法:

protected Class<?> findClass(String name) {
    String className = name.substring(name.lastIndexOf(".") + 1);
    // 文件名 = MD5(类名) + ".class"
    String fileName = "/" + name.replace(".", "/").substring(...) 
                   + md5(className) + ".class";
    InputStream in = getClass().getResourceAsStream(fileName);
    
    // 解密:AES/CBC/PKCS5Padding
    byte[] key = md5(this.code.getBytes());   // 密钥 = MD5("mdzz")
    IvParameterSpec iv = new IvParameterSpec("****************".getBytes());
    AES.init(2, new SecretKeySpec(key, "AES"), iv);
    byte[] decrypted = AES.doFinal(raw);       // 解密 class 字节
    return defineClass(name, decrypted, ...);
}


2、解密关键类

import hashlib
from Crypto.Cipher import AES

key = hashlib.md5(b'mdzz').digest()      # 密钥
iv = b'****************'                   # IV

for encrypted_file in ['64c6da24...', 'c42681a8...', 'f0cba5c1...', '8164f8f4...']:
    with open(f'resources/com/company/{encrypted_file}', 'rb') as f:
        encrypted = f.read()
    
    cipher = AES.new(key, AES.MODE_CBC, iv)
    decrypted = cipher.decrypt(encrypted)
    
    # 去除 PKCS5 填充
    pad_len = decrypted[-1]
    if 1 <= pad_len <= 16:
        decrypted = decrypted[:-pad_len]
    
    # 验证魔法数
    assert decrypted[:4] == b'\xca\xfe\xba\xbe'


 文件定位:`MD5(类名) + ".class"`,如 Stage → `64c6da2436465d11573858d46056b95d.class`

四、第三阶段:逆向变换链
1、调用链分析

Stage.Start() 
  → 读 flag 输入
  → 调用 Stage2.check(flag)
  
Stage2.check(flag)
  → 遍历 flag[i], i%3==0 时 flag[i] ^= 6
  → 调用 Stage5.check(flag)
  
Stage5.check(flag)
  → 遍历 flag[i], (i+2)%3==0 时 flag[i] ^= 51
  → 调用 Stage7.check(flag)
  
Stage7.check(flag)
  → 返回 flag 不变

2、目标值分析

Stage.java 中的 checkflag:

byte[] checkflag = {100, 106, 55, 53, 80, 48, 66, 0, 95, 81, 2, 55, 110, 108, 67, 54, 119, 51};

3、逆向变换

XOR 运算的逆运算仍是 XOR,直接逆序应用:

flag = bytearray(checkflag)

# 逆 Stage5:(i+2)%3==0 的索引 ^51
# 索引:1, 4, 7, 10, 13, 16
for i in range(17):
    if (i + 2) % 3 == 0:
        flag[i] ^= 51

# 逆 Stage2:i%3==0 的索引 ^6
# 索引:0, 3, 6, 9, 12, 15
for i in range(17):
    if i % 3 == 0:
        flag[i] ^= 6

4、逐步还原过程得出flag{bY73c0D3_W17h_C0D3}。


解题工具链:`unzip` → `jadx` → `Python hashlib/Crypto` → 逆向推导


注:此题可直接利用AI求解:

如果直接用GLM5.2大模型解此题现在不是难度,但难度是你无法在便携设备上得到这样的算力,所以我选用了Qwen3.6的35B,利用云侠AI配合直接完成此题目。


分类:Reverse
image
作者:小五义

1

提交

0

收入

相关WriteUP

  • 2018网鼎杯3-babyre

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • Reverse
    • 5年前
  • 2018-网鼎杯-advance

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • Reverse
    • 2年前
  • EasyReverse

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • Reverse
    • 2年前
  • [NUAACTF-2017] [Reverse]robots解法

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • Reverse
    • 2年前
  • EasyXor

    ***收费WriteUP请购买后查看,VIP用户可免费查看***

    • Reverse
    • 2年前
问题反馈