为什么我通过ptrace注入的sendmsg()调用不起作用?

时间:2012-06-26 18:50:32

标签: linux assembly arm file-descriptor ptrace

我正在尝试将命令注入到我拥有的进程中 - 具体来说,我正在尝试将ptraced进程的文件描述符传递给ptracing进程。我成功创建了一个unix域套接字并连接它(据我所见),如下所示:

#define stack_pointer(regs) regs.uregs[13]
#define jumpback_pointer(regs) regs.uregs[14]
#define ip(regs) regs.uregs[15]
#define arg0(regs) regs.uregs[0]
#define arg1(regs) regs.uregs[1]
#define arg2(regs) regs.uregs[2]
#define arg3(regs) regs.uregs[3]

#define ROUND_SIZE_TO_WORD(n) ((((n) - 1) / 4 + 1) * 4)

  // create a socket on the other side
  jumpback_pointer(regs) = 0; // please crash after completion
  ip(regs) = libc_offset + 0x0000cfd4/*socket*/;
  arg0(regs) = PF_UNIX;
  arg1(regs) = SOCK_STREAM;
  arg2(regs) = 0;
  ptrace(PTRACE_SETREGS, pid, NULL, &regs);
  assert(ptrace(PTRACE_CONT, pid, NULL, 0) == 0);
  int waitpid_result;
  assert(waitpid(pid, &waitpid_result, 0) == pid);
  assert(WIFSTOPPED(waitpid_result));
  printf("child was killed with signal %i, we expected it to be killed with SIGSEGV (%i)\n", WSTOPSIG(waitpid_result), SIGSEGV);
  struct pt_regs after_socket_regs;
  assert(ptrace(PTRACE_GETREGS, pid, NULL, &after_socket_regs) == 0);
  int remote_socket_id = arg0(after_socket_regs);
  printf("remote socket created, id=%i\n", remote_socket_id);

  // create a socket on our side and bind it
  int attacker_unix_socket = socket(PF_UNIX, SOCK_STREAM, 0);
  printf("local socket created, id=%i\n", attacker_unix_socket);
  int sockaddr_un_size_r = ROUND_SIZE_TO_WORD(sizeof(struct sockaddr_un)); // round up to words
  if ((sockaddr_un_size_r / 4) * 4 != sockaddr_un_size_r) {
    puts("struct sockaddr_un not properly sized");
    exit(1);
  }
  struct sockaddr_un *sockaddr = malloc(sockaddr_un_size_r);
  sockaddr->sun_family = AF_UNIX;
  strcpy(sockaddr->sun_path, "/tmp/ownage");
  unlink(sockaddr->sun_path);
  assert(bind(attacker_unix_socket, (struct sockaddr *) sockaddr, sizeof(struct sockaddr_un)) == 0);
  assert(listen(attacker_unix_socket, 5) == 0);

  // connect the victim socket
  int child_pid;
  if ((child_pid = fork())) {
    // the parent process takes care of running commands on the victim
    // side - the child isn't allowed to do this
    regs = orig_regs;
    stack_pointer(regs) -= sockaddr_un_size_r; // prepare stack push
    masspoke(pid, sockaddr, stack_pointer(regs), sockaddr_un_size_r); // stack push
    jumpback_pointer(regs) = 0;
    ip(regs) = libc_offset + 0x0000d030/*connect*/;
    arg0(regs) /*sockfd*/ = remote_socket_id;
    arg1(regs) /*addr*/ = stack_pointer(regs);
    arg2(regs) /*addr_len*/ = sizeof(struct sockaddr_un);
    assert(ptrace(PTRACE_SETREGS, pid, NULL, &regs) == 0);
    assert(ptrace(PTRACE_CONT, pid, NULL, 0) == 0);
    assert(waitpid(pid, NULL, 0) == pid);
    struct pt_regs after_connect_regs;
    assert(ptrace(PTRACE_GETREGS, pid, NULL, &after_connect_regs) == 0);
    if (arg0(after_connect_regs)) {
      printf("remote socket connect() failed with return value %li\n", arg0(after_connect_regs));
      exit(1);
    }
    printf("remote socket connected\n");

但是,实际上发送带有以下代码的文件描述符,这是一个有效的this形式,但是失败了:

  regs = orig_regs;

  int fd = 10;
  char byte = 0;
  struct iovec iov;
  struct msghdr msg;
  struct cmsghdr *cmsg;
  char buf[CMSG_SPACE (sizeof fd)];

  /* send at least one char */
  memset (&msg, 0, sizeof msg);
  iov.iov_base = &byte;
  iov.iov_len = 1;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_name = NULL;
  msg.msg_namelen = 0;

  msg.msg_control = buf;
  msg.msg_controllen = sizeof buf;
  cmsg = (struct cmsghdr *) buf;
  cmsg->cmsg_level = SOL_SOCKET;
  cmsg->cmsg_type = SCM_RIGHTS;
  cmsg->cmsg_len = CMSG_LEN (sizeof fd);
  /* Initialize the payload: */
  memcpy (CMSG_DATA (cmsg), &fd, sizeof fd);

  // copy structs over
  long r_buf = stack_pointer(regs) -= ROUND_SIZE_TO_WORD(sizeof(buf));
  masspoke(pid, buf, r_buf, sizeof(buf));
  msg.msg_control = (char **) r_buf;

  long r_byte = stack_pointer(regs) -= ROUND_SIZE_TO_WORD(sizeof(byte));
  masspoke(pid, &byte, r_byte, sizeof(byte));
  iov.iov_base = (char *) r_byte;

  long r_iov = stack_pointer(regs) -= ROUND_SIZE_TO_WORD(sizeof(iov));
  masspoke(pid, &iov, r_iov, sizeof(iov));
  msg.msg_iov = (struct iovec *) r_iov;

  long r_msg = stack_pointer(regs) -= ROUND_SIZE_TO_WORD(sizeof(msg));
  masspoke(pid, &msg, r_msg, sizeof(msg));

  // call it
  jumpback_pointer(regs) = 0;
  ip(regs) = libc_offset + 0x0000d188;
  arg0(regs) = remote_socket_id;
  arg1(regs) = r_msg;
  arg2(regs) = 0;
  ptrace(PTRACE_SETREGS, pid, NULL, &regs);
  assert(ptrace(PTRACE_CONT, pid, NULL, 0) == 0);
  assert(waitpid(pid, NULL, 0) == pid);
  struct pt_regs after_sendmsg_regs;
  assert(ptrace(PTRACE_GETREGS, pid, NULL, &after_sendmsg_regs) == 0);

  if (arg0(after_sendmsg_regs) != iov.iov_len) {
    printf("sendmsg() failed (returned %li)\n", arg0(after_sendmsg_regs));
    exit(1);
  }
  printf("successfully called sendmsg() on the other side!\n");

sendmsg()始终以-1失败。所以,我的问题是,有没有人知道为什么这不起作用或者至少能够告诉我如何找到错误以获得有关错误的线索?我试过这个来找到errno,但PTRACE_PEEKDATA总是失败:

void print_error(int pid, struct pt_regs regs, unsigned int libc_offset) {
  // call __errno to get a pointer to the errno
  jumpback_pointer(regs) = 0; // crash afterwards
  ip(regs) = libc_offset + LIBC_ERRNO;
  ptrace(PTRACE_SETREGS, pid, NULL, &regs);
  assert(ptrace(PTRACE_CONT, pid, NULL, 0) == 0);
  int waitpid_result;
  assert(waitpid(pid, &waitpid_result, 0) == pid);
  struct pt_regs after___errno_regs;
  assert(ptrace(PTRACE_GETREGS, pid, NULL, &after___errno_regs) == 0);
  long errno_fn_addr = arg0(after___errno_regs);
  long child_errno = ptrace(PTRACE_PEEKDATA, pid, errno_fn_addr, NULL);
  int peek_errno = errno;
  printf("ERROR IN PTRACED PROCESS: %s (errno@%lx=%li), peek errno: %s\n", strerror(child_errno), errno_fn_addr, child_errno, strerror(peek_errno));
}

0 个答案:

没有答案