WMCTF2025 VideoPlayer 逆向出题笔记
WMCTF2025 逆向出题笔记
这次我出的题预期难度定在了Hard,最后结束在三解,符合预期。这次的项目代码量很大,费了不少时间去构思,就是为了出不恶心人的难题,是合理的难,而且还不失乐趣。开源地址:VideoPlayer Source
[出题构思]
这次出题我是往现实场景逆向靠拢,也就是模仿的现实逆向商业VMP保护下的软件场景,想要考察的是选手的软件综合审计能力,以及动态调试、观察数据能力。所以弱化了(甚至没有)传统的加解密环节。仅仅需要静态分析配合动态调试即可得到Flag,但是需要很强的代码审计能力。
UI的框架选用Imgui,因为我平常开发UI都是用这个,所以用的比较顺手,(推荐一波我开源的OS-ImGui库->OS-ImGui,基于ImGui的二次封装库)。由于ImGui库的代码量较大,所以最终VMP保护后代码膨胀较大,造成脱壳后IDA加载分析较为吃力,后续上传了体积更小的版本便于IDA进行静态分析。
加上VMP是因为仿制现实逆向场景,而且我需要其中的函数保护功能,对其中三个核心函数进行虚拟化保护,考验选手从其他函数进行侧面逆向来综合分析,也就是在函数不完整的情况下去探索软件的流程,通过分析函数调用流,去寻找漏洞点。
[题目设计]
这题背景选在了加密视频播放器下,因为平常买的一些课程视频大多都用一些视频保护器进行保护,需要特定的加密视频播放器登入才能播放视频,所以就选了这么一个场景来当作题目背景。
[主流程]
- 需要账号密码来登入软件,然后不同账号播放加密视频的密钥是不一样的,是根据该账号绑定的机器信息生成的,也就是一机一密。
- 预留了后门超级用户,且该超级用户只校验输入的用户名,不校验密码,这边特意使用
strcmp
校验超级用户名,方便选手拦截到超级用户名。
- 模仿游戏反作弊机器码部分功能,获取目前机器的一些信息,如:硬盘序列号、Mac地址等机器信息,然后整合进行MD5,进行本地校验是否为超级用户的机器,校验通过则登入成功,并且该超级用户机器信息MD5直接作为密钥凭证进行使用。
- 选取
.mp0
后缀加密视频文件进行播放,但软件不具备视频播放能力,但是在软件中已经解密了,直接找到解密处断点步过执行提取解密后数据即可。
[题目writeup]:
反调试处理+VMP脱壳Dump
法1:TitanHide过反调试
这边反调试使用TitanHide驱动,Github可以搜索到相关项目,运行TitanHide.sys需要配置环境。
使用VKD工具的target64中的vminstall在虚拟机中运行安装,会多出来一个引导启动,重启电脑选择新的引导启动就可以,他会进入内核调试模式,禁止驱动强制签名以及关闭PG,也就可以让我们加载titanhide驱动。
运行titanhide.sys,将titanhide的dbg相关插件文件放入dbg的plugins文件夹中,运行dbg即可调试VMP程序。
法2:CheatEngine Veh debugger
软件运行后,使用CE附加,选用VEH Debugger即可进行断点调试。
寻找OEP
断点GetSystemTimeAsFileTime
,运行,第二次断下后,栈的第二个返回地址就是OEP,这也是exe程序VMP寻找OEP的通法。
OEP:
RIP修改到OEP,使用Scylla插件Dump程序。
主流程分析
IDA加载Dump的程序,可以通过字符串定位到Login页面代码,但是会发现Login按钮后调用的相关登入Check函数,被VMP虚拟保护了,所以无法正向从这边分析。
题目说存在后门账户,猜测会通过strcmp判断用户名或密码文本(这边通过断点用户名或密码的内存进行后续分析也可以)
随便输入用户名和密码后,断点strcmp,点击Login按钮断下,发现会判断一次用户名是否为WMAdmin_#6&JZZ%B
,证实确实存在后门用户名判断,但是目前还是登入失败。
字符串这边可以看到一些系统信息相关字符串。
定位到字符串相关调用函数,再查函数的交叉调用,发现这边会调用多个引用到系统字符串相关函数。
dbg定位到调用这些函数的主函数,断点,发现正常输入随机用户名和密码并不会触发,用户名输入后门用户名时会触发。
这个获取系统信息的函数会断下,执行到ret,发现是获取了多个系统信息字串,然后拼接到一起返回。
单步返回到这边,分析这部分代码,发现是将获取到的系统信息字串进行MD5,然后逐字节push_back到一个vector容器中。
执行到push_back之后的Call,让所有数据添加完毕,push_back的第一个参数rcx就是容器对象本身,也就是[rsp+80]
,对[rsp+80]
下8字节的硬件断点,运行,会断到下面这个函数。
单步运行到返回,来到如下函数,复制部分二进制48 63 44 24 24 48 89 44 24 50 48 8B 8C 24 D0 00 00 00 E8 E9 92 FF FF
,到IDA搜索字节找到对应函数进行反编译分析。
可以发现这边是将刚刚系统信息MD5的Vector数据传入进来然后逐个与v5数组比对,如果相等则返回1,不等则返回0。
结合题目信息,可以知道该软件登入后门用户的时候还会对机器进行校验,如果是指定机器才会登入成功。
尝试运行到ret,将函数返回值rax修改成1。
结果动调发现上面MD5数组在后续仍被访问,最后会到这边,v34就是上面的那个数组对象,第一个箭头函数是判断数组是否为空,如果为空则报错账号密码错误,如果不为空则把数据复制给a1参数的Vector数组。
Vector判断是否数据为空:
这边就可以该程序登入的大概流程:
输入账号、密码
判断是否为后门账户名
获取机器信息MD5,储存到Vector数组
返回Vector数组,如果账号密码错误则Vector数组为空,如果后门账号登入失败也同样为空。
将返回的MD5信息数组数据复制到登入函数的参数Vector容器中。
上面操作流程中我们将检验机器的那个函数返回值改成了1,也就是true,让他通过后门登入,所以返回了MD5数据数组。
IDA字符串可以找到”Open File”和”Play Video”相关字串,跳转到调用函数,就可以看到这个是登入后的页面相关函数。
“Open File”下面那个Call就是初始化结构,打开对话框的。
通过调试该页面函数,发现第一个箭头就是将对话框里面选中的文件目录绘制到窗口上,上面的(255,255,255,255)就是字体颜色,第二个箭头处判断选中文件目录是否为空,如果不为空则返回1,为空则弹对话框要求选中一个.mp0后缀的文件并返回0。
通过相关字符串查交叉引用定位到Player Page页面,这边也能看到打开视频文件的错误信息,复制函数部分代码字节48 89 54 24 10 48 89 4C 24 08 56 57 48 81 EC B8 01 00 00 B8 04 00 00 00
,到dbg搜索,对函数头下硬件断点。
选择video.mp0文件,然后点击Play按钮断下,发现该函数的参数一rcx是上面返回的MD5 Vector数据对象,进入两层就可以看到之前得到的MD5数据。
IDA这边可以看到判断是否通过a2文件路径的读入文件成功,然后传入文件数据以及a1参数(MD5数据)调用了一个函数,动调可以发现这边第三个参数就是返回的数据,那么这边就可以猜测就是视频数据解密函数。
通过题目可以知道该视频播放器并没有开发完成,在下面也看不到对该返回数据操作的相关代码,以及播放视频相关代码,所以就只能断在上面函数调用,单步执行,然后拿到第三个参数的数据,手动导出到文件。
题目可知该视频是由后门账户加密的,且解密的时候有传入系统信息MD5数据,所以要在MD5函数调用处将数据修改成上面校验的目标数据,也就是后门账号电脑系统信息MD5数据。
这样后续解密的时候就会传入后门账号电脑系统信息MD5数据,成功解密由后门账号加密的视频。
断在该处记下参数三r8的地址,单步执行后,跳转到r8地址,将md5数据修改成B4 EC ED FE 29 6E DE 7B 93 84 57 AF 61 9C 83 4B
。
复制IDA中的调用解密函数的汇编字节4C 8D 05 C1 9D 02 00 48 8B 94 24 D0 01 00 00 48 8D 0D 92 9D 02 00 E8 7D 35 F9 FF
,dbg搜索定位到解密函数调用,断下。
选择video.mp0文件,点击Play按钮断下,记下参数三r8地址,单步执行Call,然后等待解密完成,跳转到r8地址。
可以看到r8这边还是一个Vector结构,第一个是数据头指针,第二个是数据尾指针,相减就是数据大小,这边计算得到为11904
字节大小。
进入第一个地址,就可以看到成功解密出来的相关mp4文件数据。
使用Scylla插件,File->Dump memory,填入数据头指针地址,以及数据大小,进行Dump。
Dump得到mp4文件,打开就可以看到输入Flag的视频,拉到最后就是完整Flag。
[Flag]
WMCTF{63840490-84c5-4223-b5ee-63d4e51d0f05}