首页
社区
课程
招聘
[原创]2025 KCTF提交防守题目[设计思路+源码]
发表于: 2025-7-12 14:18 428

[原创]2025 KCTF提交防守题目[设计思路+源码]

2025-7-12 14:18
428

题目描述文档:

此题使用《规则:5.1.1  Windows方案一》,需要你输入一串序列号。

最终cm.exe文件的SHA1值为:db4b7754816c6ba61f8a9687feb2d50203941958

判胜条件:

打开cm.exe,输入你的序列号,然后回车,如果结果显示“success.”表示成功,其他为失败。
源码:

#include <iostream>

#pragma comment(linker, "/BASE:0x00700000")

const char keydict[]="0123456789abcdefghijklmnopqrstuvwxyz";

int maze_data[3][9][10] = {//不可分解为墙,可分解为路;
	{
		{11278, 4882,32580,19499,2406,7698,4707,6184,12705,21166},
		{7622, 16409, 15753,24909,6730,14884,21510,3681,1624,3404},
		{21539,18528,31112,8099,28763,17508,25583,16446,20683,5750},
		{20786,26805,19000,28661,6299, 5899, 9988,8429,18744,18422},
		{29970,20886,11132,19024,11860, 28606, 13618,31572,2749,10377},
		{22208,12859,13976, 12751, 17242, 30202, 2603, 8203, 17901, 5695},
		{9556,25275,13203, 10801,10582,12292,13956, 15854, 18348, 8807},
		{25246, 7827, 31043, 26139, 32692,9562,15671,6148,27105,12588},
		{14745,10559,12046,29754,279,25805,31862,23743,27312,1413}
	},
		// 第二层 
	{
		{27900,15590,28587,3969,20764,9444,1925,23275,10615,3067},
		{22008, 57, 10717, 18823, 4923, 24111, 32589, 25751, 25282, 14196},
		{6182,1932,8379, 21619, 26806,10235,29473, 6926, 25216, 21730},
		{20787, 25063, 12139, 23266, 6921, 25787, 1819, 25202, 17727, 8893},
		{3432, 21161, 12566,6573,10486,5150,27408,12972,9108,13629},
		{27200, 1687, 2062, 26755, 17047,5981,2874,17796,20408,21970},
		{9618,24473,22918,23172, 29091, 14709, 1317, 15893, 2083, 4823},
		{30959, 25451, 18202,21276,9208,14551,32356,6363,13390,22101},
		{7273,3816,572,9672,1668,30573,2917,30805,24621,10110}
	},
		// 第三层
	{
		{24748,6277,28224,16108,5980,3371,20270,1605,27760,17789},
		{23768,11026,284,31405,11870,14283,23800,23624, 25743, 14401},
		{25412,32611,1474,3042,29050,29463,28954,17220, 793, 30062},
		{12126,28402,9136,7893,23067,24575,29844,23628,32044, 11345},
		{19428,20887,9092,25821,25321,20505,17534,7585,28851, 23299},
		{6076,20180,15903,9976,10994,31588,17972,27097, 1882, 13387},
		{10192,3228,14430,9366, 22069, 22442,17576,25263, 26087, 12932},
		{31347,7375,9716,24013, 23774, 4251,30864,17871, 24559, 19131},
		{38, 1149, 24865, 14914, 21785, 12975,8342,6375, 16769, 8114}
	}
};

// 自定义绝对值函数 
inline double custom_abs(double val) {
	return (val < 0) ? -val : val;
}

// 自定义平方根函数 
double my_sqrt(double x) {
	// 处理边界值 
	if (x <= 0.0) return 0.0;
	if (x == 1.0) return 1.0;

	double guess = x / 2.0;     // 初始猜测值 
	double prev = 0.0;
	const double eps = 1e-7;    // 精度阈值 
	int max_iter = 50;          // 最大迭代次数 

	for (int i = 0; i < max_iter; ++i) {
		prev = guess;
		guess = 0.5 * (guess + x / guess); // 牛顿迭代公式 

		// 计算相对误差
		double diff = custom_abs(guess - prev);
		if (diff < eps * custom_abs(guess)) {
			break;
		}
	}
	return guess;
}

bool is_prime(int num) {
	if (num < 2) return false;
	for (int i = 2; i <= (int)my_sqrt((double)num); ++i) {
		if (num % i == 0) return false;
	}
	return true;
}

bool is_product_of_two_primes(int n, int* p1 = NULL, int* p2 = NULL) {
	if (n < 4) return false;
	for (int i = 2; i <= (int)my_sqrt((double)n); ++i) {
		if (n % i == 0) {
			int j = n / i;
			if (is_prime(i) && is_prime(j)) {
				if (p1) *p1 = i;
				if (p2) *p2 = j;
				return true;
			}
		}
	}
	return false;
}
bool checklic(const char*input)
{  
	if(input==NULL || strlen(input)==0) return false;
	
	int nLenInput=strlen(input);
	int nLenDict=strlen(keydict);
	int x,y,z,x0,y0,z0;
	x=y=z=0;
	x0=y0=z0=0;

	int step=0;
	for(int i=0;i<nLenInput;i++)
	{
		char a=input[i];
		int p=0;
		for (;p<nLenDict;p++)
		{
			if(keydict[p]==a) break;
		}
		if(p>=nLenDict)  break;
		if(p<12){
			z=0;
		}
		else if(p<24){
			z=1;
		}
		else{
			z=2;
		}
		if((i&1)==0)  
		{
			y=p%12;
		}
		else
		{
			x=p%12;
			step++;
			// 起点终点特殊检查 
			if(step==1 && maze_data[z][y][x]!=11278)  {
				return false;
			}

			if(i==nLenInput-1 && maze_data[z][y][x]!=8114)  {
				return false;
			}

			// 路径点有效性检查
			if(step>1 && i<nLenInput) {
				int p1, p2;
				bool decomposable = is_product_of_two_primes(maze_data[z][y][x], &p1, &p2);
				if (false == decomposable) {
					return false;
				}
				if(p1<2 || p2<2) return false;
				if(p1==p2) return false;
			}
			// 连续性检查
			if(step>1) {
				int dz = abs(z - z0);
				int dy = abs(y - y0);
				int dx = abs(x - x0);
				if(dz+dy+dx != 1) {
					return false;
				}
			}
			x0=x;
			y0=y;
			z0=z;
			//最后一步必须为8114
			if(i==nLenInput-1 && maze_data[z][y][x]==8114)  {
				return true;
			}
		}
	}
	return false;
}

int main() {
	printf( "input your serial:\n");
	char szInputStr[200]={0}; 

	std::cin.getline(szInputStr, sizeof(szInputStr));

	if ( strlen(szInputStr) <= 112 )             // 输入不能超过112个字符
	{
		bool ret=checklic(szInputStr);
		if(ret) {
			printf("\nsuccess.\n");
		} else {
			printf("\nfail!\n");
		}
	}
	else
	{
		printf("\nTry again...\n");
	}
	getchar();
	return 0;
}

writeup:


此题的解题思路:


此题是一个三维迷宫的题目,不过对迷宫的路和墙做了加噪干扰,不能直观的看出来,需要对迷宫每个坐标上的整数做质数分解来还原路和墙,同时也对主逻辑函数的一小段代码做了拆分,使程序在IDA7.0~IDA9.1版本不能直接还原出完整的函数代码(其他版本没试过),需要手工修改还原,大概的求解过程如下:


1.将主逻辑函数的代码进行还原,方便在IDA里分析过程(在x86dbg里查看也可以,就是只能看汇编代码);


2.利用程序里的质数分解函数将3X9X10的迷宫还原成路和墙(比如:00表示墙,11表示路),这样可以比较直观的解出迷宫的路线,即从{0,0,0}到{2,8,9}的通行路线坐标序列;


3.将路线坐标映射到字典字符,即得出要求解的序列号。


本题主要考查的是对三维数组映射到三维迷宫的空间想象力,还有一点就是对ret跳转的理解。




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

最后于 2025-8-18 14:33 被kanxue编辑 ,原因:
上传的附件:
收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 73675
活跃值: (23175)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
一楼附件解压密码是:kxdg.1234
2025-8-18 13:18
0
游客
登录 | 注册 方可回帖
返回