首页
社区
课程
招聘
[原创]kctf2025题目提交-FZREU考试系统登录器[设计思路]
发表于: 2025-7-2 20:45 2546

[原创]kctf2025题目提交-FZREU考试系统登录器[设计思路]

2025-7-2 20:45
2546

pediy_ctf_2025 设计说明

公开用户名:59A35804E4F4C235

公开序列号:7FF3675E036335E65E5D29D121FC149197556E6EA39F2E1A1A05437A9609EBC2

 

答案用户名:KCTF

答案序列号:AC1999741568E0A6B7CA93FD53FD900AE8EB599B8AE49E0797E91B106BE5DA53

 

题目名: FZREU考试系统登录器

错误提示:

正确提示:(在我的机器上运行1~2s即可输出结果)


混淆设计思路:

由于网上常见的mov混淆会使用4字节加法表,导致程序文件空间过大.所以我做了优化,使用2字节加法表,配合进位实现mov加法,类似的方法用于其他表,大大减少了表数据的体积,进而减少了程序大小.但是代码较为复杂,内联同样会文件大小巨大,所以与传统mov混淆的内联运算符不同,所以我使用了调用码方式执行.就是输入一个fsgs段的地址,跳转到对应运算符的mov指令实现.如图:


事先安装好veh,由于该地址的访问必定会产生异常,所以可以根据异常发生地址读取指令操作数头部的调用码(图中为0x12),进入分发器,分发到指定运算符的mov指令实现去执行.

分发器分为主分发器,api分发器和调用分发器.

主分发器中实现了5个调用,这几个调用的调用码(图中为0x3)紧跟着magic(0x66).如图:



00:一般调用分发器 01:winapi分发器 02:绝对跳转 03:虚拟栈push 04:虚拟栈pop

一般调用分发器:准备一个表,分发器查表跳转.

winapi分发器:iat地址提取,写入表,查表跳转.

虚拟栈push:将需要push的内容写入虚拟栈操作寄存器,然后移动栈指针写入栈顶.

虚拟栈pop:pop对应内容到虚拟栈操作寄存器.

调用号码表(0开始enum):

虚拟栈主要用于mov代码中寄存器的保存和一些绝对跳转.

跳转分为两种,一种为调用分发器中的跳转调用,使用跳转表中的跳转偏移id进行跳转,另一种在虚拟栈上,根据压栈的绝对地址跳转.条件跳转为第一种,mov函数调用的调用和返回为第二种.

不是所有的栈操作都在虚拟栈上.未被混淆函数和api的返回地址在原栈上,因为要响应未混淆的ret指令.

设置的虚拟寄存器如下:

reg0-reg16 通用寄存器

reg17,reg18 参数寄存器

reg19,reg20 返回值寄存器

21,22,23,24 OF,SF,ZF,CF (标志寄存器)

reg25-reg60 通用寄存器

reg61 虚拟栈顶寄存器

reg62 虚拟栈操作寄存器

reg63 循环计数器

为了省时间(技术差),标志寄存器没有全部实现,仅仅实现了常见的那4.通用寄存器用于mov调用,参数寄存器用于传递参数到mov调用,返回地址寄存器用于暂存mov调用的返回值.reg25-reg60用处繁多,用于选择器和一些寄存器的备份(比如当虚拟寄存器被用来存储程序基址的时候,需要先备份被使用的虚拟寄存器).

使用简单的几条中间指令,再由中间指令转化为mov指令.中间指令如下:

中间指令的使用详见mov_il.code,mov指令的转换详见Stockholm.h/.cpp,mov代码详见程序文件.

混淆是以函数为单位的,所以有部分函数没混淆(由于我水平太低,多线程支持没搞,带锁的函数没混淆.以后会慢慢搞),还有部分(其实是一大堆)指令没支持的,以及一些可优化的,残余的设定部分,以后再慢慢搞.


题目设计思路(源码见附件):

使(lan)用了winiocp模型,取其vm特性,以控制码为opcode.

输入用户名和序列号,序列号长度为64.转成数值后,分成8dword.

使用PostQueuedCompletionStatus发送完成数据包连接opcode,使用GetQueuedCompletionStatus阻塞接收opcode,根据每个opcode,对包中id对应的dword进行一次opcode对应的加密操作.

opcode 2333为按钮单击事件,opcode 6666为结束,0,1,2,3都为计算指令,每个指令均满足交换律并且可逆.

将用户名进行sha256 hash,将结果每2 bit转化为128位opcode,应用于8个dword.

最后,如果八个dword的结果均为0则正确,其他为错误.8个dword均可乱序执行.

输入64个大写hex字符,验证输入合法并转换分割后,将id乱序,起到迷惑性.

混淆了线程函数(单线程,无锁)和sha256的几个函数.

请各位大佬轻点打.#(滑稽)


题目概览:

ida打开,定位到start.

打开入口点,动态获取api,设置veh,分配内存.


[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 2025-8-25 13:07 被kanxue编辑 ,原因:
上传的附件:
收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 42
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
可以私聊吗
2025-9-4 12:41
1
雪    币: 1023
活跃值: (223)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

无混淆源码pediy2025_src_chal/loader.c中L98左右:用于交换位置的两层for循环,实际上只进行了一次交换。

将该循环编译执行,结果表明只执行了一次。

原因在于114514和1919810均为偶数,rand_num初始为奇数,第一次迭代后rand_num变为偶数,此后rand_num一直为偶数。

#include<stdio.h>
typedef unsigned long DWORD;

DWORD rand_num = 23333;
int main()
{
	for (DWORD i = 0; i < 7; i++)
	{
		for (DWORD j = 0; j < 7 - i; j++)
		{
			if (rand_num & 1)
			{
				/*DWORD temp = nums[j];
				nums[j] = nums[j + 1];
				nums[j + 1] = temp;
				temp = sort_result[j];
				sort_result[j] = sort_result[j + 1];
				sort_result[j + 1] = temp;*/
				printf("now there is executed...\n");
			}
			rand_num = rand_num * 114514 + 1919810;
		}
	}
}


2025-9-19 16:15
1
雪    币: 1375
活跃值: (2256)
能力值: ( LV12,RANK:226 )
在线值:
发帖
回帖
粉丝
4
NJyO 无混淆源码pediy2025_src_chal/loader.c中L98左右:用于交换位置的两层for循环,实际上只进行了一次交换。将该循环编译执行,结果表明只执行了一次。原因在于114514和191 ...
当时写的时候不是很熟悉多线程,所以用这种拙劣的打乱手法.有bug没事,顺序本来就不重要
2025-9-21 14:24
0
雪    币: 1375
活跃值: (2256)
能力值: ( LV12,RANK:226 )
在线值:
发帖
回帖
粉丝
5
原谅我算法学得烂.冒泡排序都背不下来.
2025-9-21 14:25
0
游客
登录 | 注册 方可回帖
返回