【个人笔记】题目:MoeCTF2025_find_it

题目: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将其输出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值