-
-
[原创]KCTF2025 day9 wp
-
发表于: 2025-9-2 10:10 2779
-
DIE查出来一个Enigma Virtual Box壳, 直接放弃静态分析, 通过附加调试的方式进行动态分析
输入SN前在IDA挂起线程, 然后输入后步出到用户领空为止:

查看RAX可以看到刚刚输入的SN字符串, 同时可以观察出程序使用的库底层储存字符串的逻辑:

程序先对Name进行了简单的hash, 最终结果存放在v8:

当输入的是公开序列号对应的Name时v8为55, 输入KCTF时v8为27
第一次用到SN在main+12B, 观察处理完的结果, 看到了大量重复的9, 结合序列号的格式, 大概率是{digit}{count}l的格式, 只是最后的l被省略, 而这里处理完的字符串因为太长是用类似链表的方式储存的, 偏移8, 10h, 18h的成员分别代表当前段, 上一段, 当前段的长度:

下一次对SN的处理在main+276, 同时还用到了上面Name的hash, 它被以十进制字符串的形式储存起来了:

通过硬件断点跟踪数据流发现它和SN用空格拼接起来了:

步出至用户领空, 发现这个拼接的字符串上面还有一个路径字符串:


结合上面用空格拼接的字符串, 很有可能是当作命令行参数传给了这个释放出来的PE
找到这个PE, 发现是一个python打包程序, 解包反编译后得到以下py代码:
和上面的想法差不多, 通过命令行参数进行了一个除法, 得到的是十进制字符串, 但是有3个命令行参数, 找到最后拼接的作为精度的参数:

实际上到这一步就不用分析下面的程序的, 因为程序给出了能通过校验的55 / N1的N1, 对应到KCTF的27, 只需要算出27 / N2 = 27 / (N1 * 27 / 55) = 55 / N1的N2即为正确答案:
struct str{ void *nop; _DWORD len; _WORD s[];};struct str{ void *nop; _DWORD len; _WORD s[];};from decimal import Decimal, getcontextimport osimport sysdef reciprocal(m, n, prec): getcontext().prec = int(prec) result = Decimal(m) / Decimal(n) return resultdef main(arg1, arg2, arg3): sys.set_int_max_str_digits(int(arg3)) reciprocal_value = reciprocal(arg1, arg2, arg3) print(reciprocal_value)if __name__ == '__main__': arg1 = sys.argv[1] arg2 = sys.argv[2] arg3 = sys.argv[3] main(arg1, arg2, arg3)from decimal import Decimal, getcontextimport osimport sysdef reciprocal(m, n, prec): getcontext().prec = int(prec) result = Decimal(m) / Decimal(n) return resultdef main(arg1, arg2, arg3):[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
赞赏
- [原创]KCTF2025 day10 wp 310
- [原创]KCTF2025 day9 wp 2780
- [原创]KCTF2025 day8 wp 2870
- [原创]KCTF2025 day7 wp 2705
- [原创]KCTF2025 day6 wp 2782