NepCTF2023-九龙拉棺wp
本文最后更新于:1 年前
综合难度: easy
考点:多线程,父子进程,共享内存,硬件断点检测,int3断点反调试,xtea加密算法.
出题思路/解题思路/杂记:
本题使用了8个线程+1个新的进程,实现了flag字符串的验证。
看似是 8 个线程,实则8个线程均通过一个变量控制,所以使用的是一个线程。只要跟随变量(nCurLevel)的变化,走到相应的线程,即可了解程序逻辑。
- 4个线程用来解密子进程exe,.分别使用 Rc4 -> Base32 -> Base58 -> Base64. 解密。因为使用的是线程,只能通过
- 1个线程用于内存写入exe文件,并执行子进程。
- 1个线程用于反调试
- 反调试动态读取.text(第一个段)并通过累加的方式,不断读取对比的方式,来实现反调试。如果程序一开始就存在断点,则该反调试无效,若在调试中突然增加int3断点,则该程序直接退出。
- 1个线程用于给输入的字符串加密,(部分字符, 使用了 xtea算法。若对比失败,则直接退出经常,不经过最后的输出验证结果。
- 1个线程用于获取最终的对比结果。用来判断是否验证成功。
检测硬件断点
大部分的线程中穿插着检查硬件断点的检测。
读取当前线程的Context, 查看Dr7 是否为一个正常值。若不为正常的值,则判断该线程设置了硬件断点。会尝试使用SetThreadContext 来设置Dr7 取消硬件断点。若无法取消,则直接退出进程。
Main 函数:
首先提权,需要一些权限。
然后启动8个线程。
紧接着接收用户输入字符串
将用户输入字符串放入 myShareMemory 中。
初始化当前应执行线程ID.使用Wait等待线程执行完毕。
用户输入字符的存储形式解释
myShareMemory 作为保存用户输入数据的地方,为了实现双进程间的数据交换,我直接使用的CreateFileMapping。并且通过 随机数来取一个随机偏移存放用户输入字符串。
并且初始化操作先于 main 函数执行。
导出子进程
了解完子进程的加密算法,可以找到原始数据导出,即可快速获得子进程
子进程解析
子进程使用纯汇编编写。
获取Kernel32dll的基地址
获取Kernel32dll的基本信息
遍历Kernel32.dll的导出函数名称表
找到最终的函数地址:
子进程验证部分
子进程主要 取MapViewOfFile, 获取父进程的共享内存。 Check2 函数去检查输入是否正确。
Check2 同样使用 XTEA算法进行加密后比较,注意长度,0x40. 部分长度与 Check1主进程的检查线程数据重复,需要筛选一下。(故意设置的)
如果验证成功,会遍历主进程的内存,将结果写回到主进程。(为了增加难度).
最后清理变量。
编写解密函数(XTEA):
取Check2的 0x40 + Check1 的 最后16位即为 最后的flag。
NepCTF{c9cdnwdi3iu41m0pv3x7kllzu8pdq6mt9n2nwjdp6kat8ent4dhn5r158iz2f0cmr0u7yxyq}
总结:
因为主进程使用的Release 模式,导致主进程代码被大量优化,可能对新手不太友好。不过我相信认真逆向下来的话,一定能学到不少东西。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!