放进ida, 从程序入口开始分析: 看到它将函数sub_140115000添加为异常处理函数, 然后调用了sub_140001000;
sub_140001000中主要行为是创建窗口,以及创建了一个线程入口点为sub_140001FB0;
此后sub_140115000与sub_140001FB0点进去看都是一串mov. 此时对开始学习硬刚movfuscator还有一些犹豫, 先再整体观察一下这个程序;
观察到sub_140115000与sub_140001FB0分别在段.zx3与段.zx1, 打开Segments看到特殊段有.zx0 .zx1 .zx2 .zx3:
.zx0 看起来为数据段;
.zx1 是代码段, 点进去也是一串mov, 滑几下滚轮看到了一个常数:
搜索一下就知道是sha256的常数,而且题目给的序列号的长度与sha256结果长度相同(后来发现是偶然),可以认为验证过程一定用到了sha256函数,且.zx1段首地址1400A9000应该就是sha256函数的入口;
.zx2 是代码段, 点进去观察到段首代码引用了.zx0的地址, 再观察一下.zx0的数据看到一堆或整齐或错位的0到255;
.zx3 也是代码段但是段首有一溜.zx2的地址,疑似一些函数的入口点;同时用来查找库函数的代码也在.zx3.
之后考虑先动态调试寻找一下sha256的入参, 发现windbg不能用, 但是frida能用;
顺便在脚本同目录运行pnpm i @types/frida-gum可以在写脚本时具有类型提示.
观察到sha256入口处保存了rcx,rdx,r8,r9,
先写frida脚本如下: 运行 frida ./zx_mov_obfu.exe -l i.js -o i.log 填入示例用户名序列号, 输出为 发现sha256的参数为用户名;
但是之后也没法跟, 只能先看看movfuscator.
阅读 6feK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6^5L8%4u0W2j5i4S2W2j5i4S2W2j5i4S2Q4x3V1k6E0L8%4k6X3N6i4y4U0j5i4c8G2M7R3`.`. 了解到movfuscator的运算主要通过查表, 以异或为例: 所以之前观察到的.zx3存放.zx2函数地址, .zx2函数引用.zx0且.zx0中保存着各种运算表, .zx3存放的.zx2函数们应该负责各种运算;
如果所有运算操作都由这些函数完成, 则可以通过hook这些函数并输出完整的运算步骤来分析算法.
接下来的任务是判断这些函数的参数保存位置以及负责什么运算;
通过分析.zx0中的数据可以看出: 接下来分析函数功能有两种方式, 一是静态观察函数使用了什么运算表, 二是重点分析函数头尾的数据流, 找出入参与返回值的位置, 然后hook并输出它们来看是什么运算.
在分析过程中观察到在mov rax fs/gs:...前的代码: 有很多都将其后的代码地址存于1400061f0, 则该处很有可能保存着返回地址. 之后以sha256(59A35804E4F4C235)的结果60c5ef828d8be31ec6f2968436887a864e5020488dcf95e657fc9eb009870113为基点, 在输出文件i.log中查找, 能够找到:(其中step1输出是找到并分析后添加的) 这段代码再对sha256(name)的结果串做分解, 跳转到其中and的返回地址0x1400b8e97, 注意之前能分析出其返回值保存于0x140006098: and操作的返回值被存入[rsp+rcx*4+2F0h], hook此处并添加step1输出可以确认其逻辑: 同时在ida中搜索+2F0h可以找到:
hook并确认写入位置: 用python重写: 之后的运算: 在算(23333*114514)+1919810之后循环*114514+1919810...搜索0x1d4b42看到共乘加了28次最后得到了个常数0xd4abe55e. 既然是常数其实可以不管它, 在之后的运算它仍会以常数形式出现.
再之后的运算为: 看到在处理0x5e67f37f, 而0x5e67f37f是示例序列号7FF3675E036335E65E5D29D121FC149197556E6EA39F2E1A1A05437A9609EBC2的小端序第一个32位数字;
并看到其中有cmp: 0x0 0x1a0a false而0x1a0a正是step1中被添加到结果末尾的数字, 在log中搜索0x1400baa2a发现有多达1032个结果, 观察发现cmp的第一个参数正是按顺序出现的step1的结果, 每轮出现8次, 且只能为0 1 2 3 0x1a0a中的一种, 判断为0x1a0a应表示循环结束.
之后分别搜索cmp: 0x0 0x1a0a,cmp: 0x1 0x1a0a,cmp: 0x2 0x1a0a,cmp: 0x3 0x1a0a发现其后进行的运算由cmp的第一个参数决定: 0 1 2的对应运算比较好写, 3的对应运算还涉及到0x1400bd11a add: 0x1 0x1 0x2的结果值会作为其后移位运算的计算次数, 需要跟踪此处add的参数及其来源; 搜索0x1400bd11a可以发现 第二个参数总为1, 重点是第一个参数从哪来: 发现从[rsp+9Ch]中来, 在ida中搜索+9Ch可以找到: 这里静态不太好跟, 于是下个hook观察一下: 可以观察出该处的值为示例序列号作为小端序32位整数数组中被操作的值的下标.
于是可以写出step2: 再写出reverse_step2: 之后观察step2的结果, 发现是全0: 即可拼出完整的求用户KCTF序列号的算法: __int64 start()
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-288193.htm
[原创]KCTF 2025 第五题 智破幻阵wp
424 浏览
0 回复
暂无回复,快来抢沙发吧!