首页
社区
课程
招聘
[原创]2020 KCTF by lelfei
2020-10-29 18:25 8632

[原创]2020 KCTF by lelfei

2020-10-29 18:25
8632

注册码:10C7F30833B9C4563BF035C32D8C7709E040FCA64E211F34CD3FE773

说明:注册成功时输出“GOOD!”


设计说明:

0x00 这一题是这一段时间一些crackme想法的大集合,包含花指令、调试检查、内存校验、大数计算、溢出利用等等,如果能够理清各种干扰项,最后的算法其实比较简单。


0x01 自己写了一个大数计算类BigNum,数据位有0x20字节,实现了基本的加减乘除算法。有2个固定大数P=0x3FAFFA2B01B6BA9744C4B4E010010401和Q=0xFEA1BD9E6964129D8F5079E1,其中P共有4个DWORD组成,其中高位的3个DWORD分别是“INPUT:”、“GOOD!”、“ERROR!”的计算值,输出“INPUT:”等待用户输入。


0x02 用户输入注册码记为D,对D的前7位和后7位转换为数值记为sn1和sn2,对main()函数和调试检查函数分别计算内存校验,根据调试检查结果分别与这2个校验值进行异或。循环9次后,检查结果是否为P的第2、3个DWORD。


0x03 D转换为大数,取P的第一个DWORD记作E,计算D*E/0xE053D0F+P+Q记为F,计算F-P*Q记为m1,要求m1的数据位不超过0x10字节。


0x04 计算F*0x0E053D0F*25记为m2,再自加一次m2+m2会导致溢出覆盖掉P的长度位,导致P=1。


0x05 比较m1==P,即最终验证为D*E/0xE053D0F+P+Q-P*Q==1,正确则提示“GOOD!”。


0x06 程序源码中定义了4个花指令标志位:

#define JUNKCODE5()  asm volatile("mov $0x55555555,%eax")

#define JUNKCODE7()  asm volatile("mov $0x77777777,%eax;inc %eax;inc %eax;")

#define JUNKCODE9()  asm volatile("mov $0x99999999,%eax;inc %eax;inc %eax;inc %eax;inc %eax;")

#define JUNKCODE11() asm volatile("mov $0xAAAAAAAA,%eax;mov $0xBBBBBBBB,%eax;inc %eax;")

在源码的main()和调试检测函数中添加了很多引用占位,编译后使用replace_byte.py把标志位替换成随机花指令。


0x07 由于替换花指令导致内存校验值发生变化,程序中使用了2个全局变量作为salt,需要在花指令替换完成后,调试时修改程序流程计算出校验值,再由校验值计算出2个salt手动patch到程序的数据段。


0x08 调试检测函数中有一个是硬件断点检查,程序源码中并没有调用设置硬件断点的函数SetDrxBreakPoint(),而是在编译之后手动patch到入口附近,代码为:

0040CA3A    50                    push eax

0040CA3B    E8 06000000           call 33.0040CA46

0040CA40  - FF25 AC734C00         *jmp dword ptr ds:[<&msvcrt.__lconv_init>]

0040CA46    58                    pop eax

0040CA47    05 70900A00           add eax,0xA9070

0040CA4C    FF10                  call dword ptr ds:[eax]

0040CA4E    EB 03                 jmp short 33.0040CA53

0040CA50    31C0                  *xor eax,eax

0040CA52    C3                    *retn

0040CA53    58                    pop eax

0040CA54  ^ E9 77FFFFFF           jmp 33.0040C9D0

加*号的为原来数据不要动。目的是调用SetDrxBreakPoint()函数。其中有add eax,0xA9070让指针指向main()中保存SetDrxBreakPoint()的数据地址。这一步操作的主要目的是避免显示调用函数。


0x09 总结及破解建议:

1.“花指令+内存校验”让动态调试过程很恶心,估计很多人都会想到解决办法,就是把去花后的数据保存下来用于IDA分析。

2.“调试检测+内存校验”会让动态调试数据变得不可靠,其实分析完后面大数验证的代码可以发现,这一部分验证并没有起多大作用,通过大数验证就已经让结果唯一了。这一段验证的主要目的只有一个,就是防止在大数前面补0造成多解。

3.大数验证过程需要动态分析与静态分析相结合,静态分析很容易了解到验证过程就是D*E/0xE053D0F+P+Q-P*Q==P,但是却很难看到溢出漏洞改变了P值。

4.大数验证看似计算很乱,分析清楚之后其实很简单,根据这几个变量名也差不多能看出来,其实就是RSA算法中最基础的一步,根据d*e+k*Phi=1的解反求出Phi,再根据Phi=(P-1)*(Q-1)=P*Q-P-Q+1=N-P-Q+1来进行最终验证。


此crackme使用CodeBlocks设计,gcc编译,在win7x64下测试通过。



文件说明:

lelfei-KCTF2020.rar    参赛文件

src.rar                程序源码

replace_byte.py        把程序中预定义替换为随机花指令

readme.txt             本说明文件


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-12-3 12:02 被kanxue编辑 ,原因:
上传的附件:
收藏
点赞1
打赏
分享
最新回复 (1)
游客
登录 | 注册 方可回帖
返回