CTF show 数学不及格

拿到题目先查一下壳,看一下信息

发现是一个ELF文件,64位的

用IDA Pro 64 打开这个文件

然后点击F5进行伪代码转换

可以看到有五个if判断,第一个argc != 5这个判断并没有起太大作用,主要是下面四个if判断

根据题目,其实可以判断出来,这个就是一道数学题

现在开始逐步分析一下代码

赋值操作:

strtol函数,作用是把字符串转成长整型,具体解释看这个C 库函数 -- strtol() | 菜鸟教程

然后将v4传入f函数,剩下的v10,v11,v12和v4一样的操作,这里主要就是f函数

然后下面这几个if判断,可以理解为几个方程

假设x = v4,y = v9, a = v10,b = v11, c = v12,那么就有下面几个方程:

y - a = 151381742876(这里将十六进制转换成十进制了,下面的类同)

y - b = 117138004530

y - c = 155894355749

x + a + b + c = 1349446086540

很明显,五个未知数,四个方程解不出来的,这里还有一个点就是v9是通过v4得出的,也就是v9和v4还有关联,只要求出来一个,所有的都可以解出来了

现在查看一下f函数的内容

可以看到传进去的v4也就是这个局部变量的a1,是有限制的,即,1 < a1 <= 200

所以就知道了v4的取值范围也是(1-200]

那就根据这些条件,话不多说直接上脚本

cpp 复制代码
#include <stdio.h>
#include <windows.h>
#include <inttypes.h>

uint64_t y(int var_24);

int main() 
{
	uint64_t v10, v11, v12 ,argv = 0;
	uint64_t dbNumber = 0;
	for (int v4 = 1; v4 < 200; ++v4)
	{
		dbNumber = y(v4);
		v10 = dbNumber - 151381742876;
		v11 = dbNumber - 117138004530;
		v12 = dbNumber - 155894355749;
		argv = v4 + 25923;
		if (v4 + v12 + v11 + v10 == 1349446086540)
		{
			printf("v9=%" PRIX64 ",v10=%" PRIX64 ",v11=%" PRIX64 ",v12=%" PRIX64 ",v4=%X,argv=%" PRIX64 "\r\n", dbNumber, v10, v11, v12, v4,argv);
			printf("%" PRIX64 "%" PRIX64 "%" PRIX64 "%" PRIX64 "\r\n", v10, v11, v12, argv);
		}
	}
	system("pause");
}

uint64_t y(int var_24)
{
	uint64_t *ptr;
	uint64_t var_10;
	uint64_t var_14;
	if (var_24 <= 1)
	{
		return 0;
	}
	else if (var_24 <= 200)
	{
		ptr = (uint64_t *)malloc(var_24 * 8);
		ptr[0] = 1;
		ptr[1] = 1;
		var_10 = 0;
		for (var_14 = 2; var_14 < var_24; ++var_14)
		{
			ptr[var_14] = ptr[var_14 - 1] + ptr[var_14 - 2];
			var_10 = ptr[var_14];
		}
		free(ptr);
		return var_10;
	}
}

下面开始分析这个脚本的作用,以及如何写成的

首先,其中定义的变量都用了uint64_t,原因是,在我最开始写这个脚本的时候,用的long long型定义的,发现还是放不下其中的数值,数值貌似是5000亿,所以这里将所有的变量都改为uint64_t,不过用uint64_t的时候,需要包含头文件inttypes.h

类型 位数 是否有符号 等效传统类型 备注
uint64_t 64 无符号 unsigned long long 保证64位
unsigned long long 至少64 无符号 - 可能比64位更大(依赖平台)
size_t 平台相关 无符号 - 通常用于表示内存大小
uint32_t 32 无符号 unsigned int 32位无符号

然后接着往下分析,整体代码中除了主函数(main)还有一个y函数,这个等同于IDA中的f函数,也就是我将f函数的伪代码转换成C语言了,也就是用来计算IDA中v9的值

然后我main函数中,有一个for循环,这里我的思路是爆破v4的值,然后前面分析也知道了v4的值在(1,200]之间,所以利用一个for循环遍历v4,并将v4传入y函数中,用dbNumber接收返回值,这里dbNumber等同于IDA中的v9

然后v10、v11、v12的值都是通过dbNumber求出来的,也就是根据前面列的方程求

然后再加了一个if语句,是为了判断v4的值是否正确,也就是看看当前根据爆破出来的v4值求出来的v10、v11、v12是否正确,如果正确,那就全部输出出来

最后这个printf的用法,跟正常使用的时候不一样,这是为啥呢,因为我们定义的变量都是uint64_t,而printf 系列函数中,能正确格式化输出 uint64_t 类型的变量的就是PRIX64

  • 宏名称PRIx64

    • PRI:表示 "print"(格式化输出)

    • x:表示十六进制小写输出(X 表示大写十六进制)

    • 64:表示64位宽度

脚本的所有地方都解释清楚了,直接运行

可以看到,第一行是当时为了调试,看看所有变量有没有正确输出,第二行才是真正的flag,也就是将所有变量拼接起来,拼接顺序是v10、v11、v12、argv,为什么呢,因为在IDA中,赋值的地方就看到了v10是argv[1],v11是argv[2],v12是argv[3],v4是argv[4] - 25923,所以argv[4]=v4+25923,也就是我脚本中的argv变量

最后将得到的一串十六进制数复制到厨子中进行十六进制解密,就得到flag了


最后多说一句

如果师傅们还需要更多的资料,欢迎进入泷羽Sec资源库,里面不仅包含了各种面试题,还有各种网课资源,各种源码分享

泷羽Sec资料库,加入泷羽Sec帮会,享受各种IT资源,资源持续更新中

加入帮会-发现更有料的网安圈

相关推荐
柴郡猫^O^2 小时前
OSCP - Proving Grounds - DC - 1
安全·网络安全·安全性测试
泡泡以安2 小时前
JA3指纹在Web服务器或WAF中集成方案
服务器·安全·https·ja3指纹
Raners_3 小时前
【Linux】文件权限以及特殊权限(SUID、SGID)
linux·安全
格调UI成品5 小时前
预警系统安全体系构建:数据加密、权限分级与误报过滤方案
大数据·运维·网络·数据库·安全·预警
Wallace Zhang7 小时前
STM32F103_Bootloader程序开发11 - 实现 App 安全跳转至 Bootloader
stm32·嵌入式硬件·安全
CertiK8 小时前
IBW 2025: CertiK首席商务官出席,探讨AI与Web3融合带来的安全挑战
人工智能·安全·web3
m0_738120729 小时前
玄机——某学校系统中挖矿病毒应急排查
网络·安全·web安全
帽儿山的枪手10 小时前
为什么Linux需要3种NAT地址转换?一探究竟
linux·网络协议·安全
leagsoft_10039 天前
联软科技入选《新质·中国数字安全百强(2025)》专业领域榜单,斩获“领先者”称号
科技·安全
浩浩测试一下10 天前
渗透测试指南(CS&&MSF):Windows 与 Linux 系统中的日志与文件痕迹清理
linux·运维·windows·安全·web安全·网络安全·系统安全