和前几年的相比还是太CTF了 第一天就出了flag 感觉有很多人都能做出来 不知道要达到什么要求才能拿满分数进决赛
R3层程序分析
flag前4位静态分析时就能看出是ACE_
全局构造函数中发现反调试函数 编写IDApython脚本跳过这个函数以便调试:
发现调试的时候在与驱动建立mini通讯连接时会直接卡死 现在只关注R3层程序对输入的处理 故也编写IDApython跳过这个函数先看看应用层对flag做了什么处理:
调试至中间发现一个像某种表的字符串:
紧接着就把这个表和输入的flag[4:]传入了一个函数进行编码
首先按照base58的第一步进行了分割:
但是看下面实际上编码用的是base64的表:
比较一下结果就能发现实际上就是换表base58用@
填充 + 反转:
下面是一个循环异或:
根据结果验证一下这一步的加密流程:
1 | enc = [ |
成功验证
R0层程序分析
接下来就进入到了驱动的校验函数 但是在驱动处理应用层发送的信息的回调函数里基本上都是花指令没法看 使用WinDBG进行辅助分析 关于WinDBG的远程调试配置参考我之前的博客 但是不推荐用IDA来调试 虽然IDA用的也是WinDBG作为调试器 但是查看内存还是没有WinDBG方便
先用DriverView辅助计算下断点的虚拟内存 断点就下在ProbeForRead
处 因为这个函数是驱动获取用户内存的API:
得到这两个函数都用于实现数据从用户层迁移到驱动层:
随后在读到驱动层的数据上下硬件断点跟踪数据流向 发现来到了如下的函数 参数就是用户层完成加密后的前两个字节以及b’ACE6’:
在IDA中找到对应函数进行静态分析:
发现就是一个XTEA 本来打算直接运行来验证其是否每次传入的都是相邻两个字节的用户层加密结果 但是断点没有被击中第二次 猜测应该是在加密完后直接就与正确密文进行了对比直接退出 这一次调试尝试使用步过来查看结果:
通过RSI计算是从哪里取出的与TEA结果对比的 计算偏移为0x4060:
也就是说这一块应该存放的就是最终的密文
手动修改zf寄存器值多观察几轮 r @zf=1
随后复制IDA伪代码验证是否就是TEA加密 发现结果和内核调试过程中得到的不一样 对TEA交叉引用发现有函数对其内容进行了修改:
在WinDBG中找到TEA函数对应的位置拷贝其内存到IDA中进行patch看看结果
发现TEA函数被改为了跳到某个地址 在这个地址下断看看做了什么:
大致可以看出来也是类似TEA的中间操作 指令条数实际上并不是很多 可以在IDA中找一块内存将其patch成TEA跳转到的这块指令然后再修改TEA+0x56处的逻辑让其跳到自己手写的指令上最后应该就能让IDA反汇编出实际执行的指令了(为了方便写入指令 将程序基址rebase成了0):
经过测试成功运算出了调试时得到的结果:
编写解密脚本
接下来要做的就是编写脚本解密了:
1 |
|
将得到的字符串反转并b58decode后拼接ACE_
:
1 | def b58decode(cipher_input : bytes): |
得到flag: ACE_We1C0me!T0Z0Z5GamESecur1t9*CTf
验证flag: