题目:MoeCTF2025_find_it
日期:25-8-27
题目逻辑:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char file[40]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v6; // [rsp+28h] [rbp-8h]
v6 = __readfsqword(0x28u);
init(argc, argv, envp);
v3 = dup(1);
write(v3, "I've hidden the fd of stdout. Can you find it?\n", 0x2FuLL);
close(1);
__isoc99_scanf("%d", &fd1);
write(fd1, "You are right.What would you like to see?\n", 0x2AuLL);
__isoc99_scanf("%s%*c", file);
open(file, 0);
write(fd1, "What is its fd?\n", 0x10uLL);
__isoc99_scanf("%d", &fd2);
read(fd2, &buf, 0x50uLL);
write(fd1, &buf, 0x50uLL);
return 0;
}
要点解析:
- 由[rsp+0h]&[rbp-30h]可知栈底(rbp)与栈顶(rsp)相距30h(48字节),file的起始地址位于rsp,file占据rsp向下40字节。图示:
栈底(rbp)|----(8字节)----|----file(40字节)----|←栈顶(rsp) - int64定义了一个64位(8字节)整数v6,由[rbp-8h]可知v6起始位置位于rbp上方8字节
- 线程本地存储(TLS):操作系统和编译器协作,为进程中的每一个线程都分配一小块私有的内存区域。当一个变量被声明为thread_local时,编译器会确保每个线程访问这个变量时,实际上是在访问它自己那块私有内存区域中的副本。
- fs:一种段寄存器,存储当前线程的线程本地存储(TLS)区域在虚拟内存中的起始地址。
- qword:长度为8字节的一段数据
- v6 = __readfsqword(0x28u);:从fs向下0x28u(40字节)处开始读取8字节的一段数据存入v6。这里的v6是金丝雀。
- fd:文件描述符,如“0,1,2,3,…”,指向内核管理的一个复杂数据结构
- dup:系统调用,调用一个fd,系统会默认寻找最小的空闲fd。在这里因为程序启动已经调用了0,1,2三个fd,最小的空闲fd为3,所以dup(1)调用了3,并把1的权限复制给3,此时v3的值为3。
- write中的v3:v3位置为文件描述符,因为dup将1的权限复制给了3,所以这里相当于在v3的位置放了一个1(默认为:0,标准输入;1,标准输出;2,标准错误),使后面的字符串能正常显示。
- write中的0x2FuLL:0x2F规定了输入字符串的长度。write作为底层输出函数,不具备像print、printf那样自动计算数据长度的能力,而需要人为规定写入的数据长度。uLL 后缀表示"unsigned Long Long",确保这是一个64位无符号整数。
- write中的字符串:write会将2F长度的字符写入v3(即1),因为1代表标准输出,所以这个字符串最终会输出到屏幕上。
- close(1):关闭了文件描述符1,使其空闲。
- __isoc99_scanf(“%s%*c”, file);:%s:读取一个非空白字符串;%*c:c通常用于读取并丢弃输入流中残留的换行符(\n),*表示赋值抑制,匹配一个字符但不会赋值给任何量。file即程序开头设置的缓冲区,用来存储用户输入的文件名(flag)。
- open打开用户输入的文件名,如果该文件存在,就以只读模式(0)打开,并返回一个fd=1(因为1此时空闲且最小),且1并未被任何变量承接。但因fd被close后就失去了原本指向,所以这里的fd=1并不代表标准输出,1指向open打开的文件。
- read从fd2(即输入的1)指向的文件(flag)中读取50(80字节)长的数据并写入buf,write输出buf中的内容。
解题思路:
因为程序启动占用了0、1、2,所以dup只能将3赋给v3,因此第一次输入3并将3赋给fd1,使第二个问题能正常输出。第二个问题根据经验输入flag,open成功打开flag并返回一个fd,因为1被close,所以open返回fd=1,此时1指向flag。第二次输入,根据文字提示输入1,将1赋给fd2,read从flag中读取数据存入buf,write将其输出。
6万+

被折叠的 条评论
为什么被折叠?



