华为杯第四届中国研究生网络安全创新大赛 Megrez RE WP
“华为杯”第四届中国研究生网络安全创新大赛 Megrez RE WP
本次研究生赛我们排名第6,解出8题
成功晋级
这是与我相关的一些题目
Reverse
YJS-GoEnc
IDA 打开,直接转到main函数看逻辑
main_main中的逻辑校验
如图,并且选择前16位作为key
main_encryptWithAES看出来为AES加密,并且为CBC模式
最后进行Base64编码
密文为base64的串交叉引用即可找到
key在这里做了变换
exp:
1 | import base64 |
flag{ca083c88-30b7-447c-8e6a-e7470862abe7}
YJS-bc
直接用llvm-dis bc.bc output.ll去反编译得到一个文件去分析
逆向这个这个代码拿flag
程序用 strtok(“_”) 将整行拆成三段:第一段长度要正好 5、且都是 [0-9a-f];第二段用于 Speck-128/128;第三段逐字节进 AES S- box,对比常量 1a04434de3099a51c79f8500 的前 11 字节(末字节是填零)。
第一段:暴力 SHA-256 第一段的 SHA256(part1) 必须等于常量 eb3404…2fcb。写脚本枚举所有 5 位十六进制串即可。 b1a37
第二段:还原 Speck 明文 密钥由第一段拼上其 SHA 摘要前 11 字节组成 16 字节(小端两段,先扩展 32 轮 round key)。程序随后把输入第二段当作 16 字节 块,用相同流程加密后要匹配常量密文 502a7c…f5b9。依 Speck-128/128 逆向解密该密文得到明文 3peckenc0def1n@l。
第三段:逆 S-box 常量数组是标准 AES S-box 的表。运行时把第三段每个字节过 S-box 并与常量前 11 个字节比较;数组末尾 0x00 仅是填充。把常量前 11 字节通过逆 S-box 即得第三段 C0deM@7p1ng。
exp
1 | #!/usr/bin/env python3 |
我解出来是flag{b1a37_3peckenc0def1n@l_C0deM@7p1ngR},提交错误
平台放了hint是41位长度,把最后一个R去掉即可
flag{b1a37_3peckenc0def1n@l_C0deM@7p1ng}
YJS-ooops 三种解法
魔改了Magic,修一下那个py解包脚本即可解包、
改成
1 | MAGIC = b'swI\014\013\012\013\016' # Magic number which identifies pyinstaller |
你问我怎么发现魔改的?附件直接丢给AI它能扫出来
这个opcode.pyc反编译失败,应该是做了些处理,我们尝试看一下它的字节码的反汇编代码
直接dis也会出现问题
用这个代码去解析
1 | # analyze_ooops.py |
出现了
1 | === decrypt_flag === |
的问题
Python 3.5 的 dis 模块在自定义或损坏的字节码时会崩溃,所以看到IndexError: tuple index out of range。说明出题人对这段字节码做过轻微改动,标准库的 dis 没办法继续下去
可以使用pycdc.exe
可以得到
上面还有很长一段,还是混淆过的,关键逻辑处还是没有
看到的大量 INVALID、CONTINUE_LOOP 等异常指令,是这个pyc做了反编译对抗,混淆,或是用不完全匹配的解释器,工具链反汇编导致的错位
anti_debug():Windows 上用 ctypes.windll.kernel32.IsDebuggerPresent() 反调试,若为真,sys.exit(1)。
还有正常的输入校验
还有一些data信息
没想到又问了一次GPT让它好好分析得到了
你贴出的那份 pycdas 输出本身是可信的整体结构,但「反汇编的指令」部分已经完全乱掉了——原因是 pycdas 仍然把 Python 3.5 的 wordcode 当成旧版 opcode 来读,从而把每一条指令拆成了 “真实 opcode + 0x00” 的组合,结果自然全是
、虚假的 SETUP_LOOP、BINARY_POWER、STORE_ATTR 59 等。要想真正还原函数,只能按 wordcode 重新读取(一次两个字节,第一字节 opcode,第二字节 0~255 参数,大于 255 通过 EXTENDED_ARG 累积)或者直接反编译出源码。 不过,光看 pycdas 列出的 meta 信息 就足够告诉我们 ooops.pyc 的结构,结合真正的反编译结果,逻辑非常清晰,关键点如下
1 | import sys, struct, zlib, ctypes |
但是还是不大对
解法一: 修复pyc
怎么扰动的AI尝试了多遍AI还是分析不明白
Python 3.5 引入了 wordcode 格式:每条指令固定 2 个字节(第一个字节 opcode,第二个字节 8 位参数;超过 255 靠 EXTENDED_ARG 累积)。老式 “一字节 opcode + 可能的两字节参数” 的反汇编器会把第二个字节当作新指令,于是出现大量
出题方还稍微动了 opcode,例如 COMPARE_OP 参数可能是 89 这种超出标准比较运算表的值。标准库 dis 在解析这类指令时会抛 IndexError,必须自己写 wordcode 解析或用能容错的反编译器,如 uncompyle6,才能看到真实指令流。
看来偷不了懒
_opcode.cpython-35m-x86_64-linux-gnu.so
可以分析下这个so文件
opcode>89会做这些操作
在libpython里面发现了opcode的表
把这些在喂给了ai
1 | #!/usr/bin/env python3 |
然后运行:
1 | import importlib.util, zlib, struct |
flag => b’flag{Reverse_This_Ultra_Hard_Challenge!!!}’
直接把flag打出来了
后来发现
uncompyle6 ooops-patch.pyc,它这个程序生成一个patch文件
1 | (py35) E:\Desktop\huaweiCTF\ooops>uncompyle6 ooops-patch.pyc |
解法二: LD_PRELOAD文件劫持
在发现zlib时,其实可以想到,可以去hook或者调试,只要绕过反调就能看到解压的flag
先来试一下文件劫持(优点是无需绕过反调试)
因为
1 | // hookzlib.c |
可以得到
010打开
直接看到flag
uncompresswrapper(legacy 接口)签名:
int uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)。这是 zlib 的一次性解压函数(非流式)。我们的 wrapper 做:
- 延迟解析真实函数地址
real_uncompress = dlsym(RTLD_NEXT, "uncompress")(第一次调用时解析)。- 调用
real_uncompress(dest, destLen, source, sourceLen)—— 让 zlib 把数据解压到dest。- 如果返回
Z_OK并且*destLen > 0,就把dest的*destLen字节写到输出文件(dump_bytes(dest, *destLen))。为什么先调用真实函数再写?因为只有在真实函数成功解压后,
dest才包含明文,直接写出我们需要的明文;若先写会只得到压缩态或未填充的缓冲。
flag{Reverse_This_Ultra_Hard_Challenge!!!}
解法三:frida_hook
注意到这个函数在(libpython3.5m.so.1.0)这个so文件中
有一个比较,可以hook一下可以直接出
1 | if user_input == decrypted: |
本题是有反调的,可能双进程都有,如何注入可以自行让ai写
1 | var mInterval = setInterval(function () { |
AI
YJS-指鹿为马
AI的题就该让AI做
\n




























