【问题标题】:Why can't i exit from shellcode with a syscall?为什么我不能通过系统调用退出 shellcode?
【发布时间】:2019-05-11 17:32:28
【问题描述】:

我尝试制作一个程序,您可以在其中放入一些十六进制的组装程序并运行它。 使用像int3 这样的简单指令它可以工作,但是当我尝试使用系统调用退出程序时它不起作用。 我用rasm2组装它

mov eax, 1
mov ebx, 12
int 0x80

然后把它作为参数./Programm b801000000bb0c000000cd80 1 但我遇到了段错误。

这是我的代码:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

char *base16dec(char *b16str) {
  size_t stingrlength = strlen(b16str);
  char *decodedstr = malloc(stingrlength / 2);
  for (size_t i = 0; i < stingrlength; i += 2) {
    u_int8_t num = 0;
    char stringIn[3];
    stringIn[0] = b16str[i];
    stringIn[1] = b16str[i+1];
    stringIn[2] = 0;
    sscanf(stringIn, "%hhx", &num);
    decodedstr[i/2] = (char) num;
  }
  return decodedstr;
}

这会解码十六进制字符串

int main(int argc, char *argv[]) {
  char *dirstr = "XXXXXX";
  char dir[7];
  strcpy(dir, dirstr);
  int fd = mkstemp(dir);
  if (fd == -1) {
    dirstr = "/tmp/XXXXXX";
    char dir[12];
    strcpy(dir, dirstr);
    fd = mkstemp(dir);
  }
  unlink(dir);

这将创建存储程序集的 tmp 文件

  char *stringIn;
  if (argc == 2) {
    stringIn = malloc(strlen(argv[1]));
    strcpy(stringIn, argv[1]);
  } else if (argc == 3) {
    u_int8_t num = 0;
    sscanf(argv[2], "%hhu", &num);
    if (num == 1) {
      char *done = base16dec(argv[1]);
      stringIn = malloc(strlen(done));
      strcpy(stringIn, done);
    } else {
      stringIn = malloc(strlen(argv[1]));
      strcpy(stringIn, argv[1]);
    }
  } else {
    stringIn = malloc(1024);
    scanf("%s", stringIn);
    char *done = base16dec(stringIn);
    stringIn = malloc(strlen(done));
    strcpy(stringIn, done);
  }

这会解析输入并将其复制到stringIn

  ftruncate(fd, strlen(stringIn));
  u_int8_t *code = mmap(NULL, strlen(stringIn), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE , fd, 0);

这会扩展 tmp 文件并使其可执行并创建一个指向它的指针,名为 code

  for (int i = 0; i < 1024; i++) {
    code[i] = (u_int8_t) stringIn[i];
  }

这会将汇编字节复制到code

  #if __x86_64__
  __asm__(
    "mov %0, %%rbx\n"
    "jmp *%%rbx"
    :
    : "g" (code)
    : "memory"
  );
  #elif __i386__
  __asm__(
    "mov %0, %%ebx\n"
    "jmp *%%ebx"
    :
    : "r" (code)
  );
  #else
  #endif

这会跳转到程序集

  return 0;
}

编辑:

我无法使用 gdb 调试 shellcode

我使用 64 位 Linux Mint

我尝试使用strcpy复制0

【问题讨论】:

  • 您的代码段错误在哪条指令上?您所有的系统调用是否都返回成功?您是否使用strace 来确保所有系统调用都成功? edit 使其成为minimal reproducible example。 (顺便说一句,一个 tmp 文件似乎有点矫枉过正。只是 mmap(MAP_ANONYMOUS) 一个可执行 + 可写页面。此外,如果您在 Windows 上的 WSL 上使用 x86-64,32 位 int 0x80 系统调用不起作用,只有 64 位 syscall ABI。
  • “我无法使用 gdb 调试 shellcode” - 为什么不呢?无论如何使用"g" (code) 是一个非常糟糕的主意。不知道编译器将在那里使用什么。为什么不直接使用函数指针?
  • @MaxSilvester: 转换为(void*),因为在 C 中您可以将其分配给任何东西,或者使用(void (*)(void))code 转换为指向函数的指针。当然 char* 不能隐式转换为函数指针,编译器可以保护您免受这种情况的影响,就像将 float * 分配给 int* 变量而不进行强制转换一样。如果您希望编译器不碍事,请使用void*
  • 您仍然必须使用MAP_PRIVATEMAP_ANONYMOUS,即MAP_PRIVATE|MAP_ANONYMOUS。当您传递 just MAP_ANONYMOUS 时,mmap 返回 -1 并带有 errno=EINVAL 或类似的东西。 使用strace 查看 glibc malloc 和/或 CRT 启动代码的作用,并查看您自己的系统调用失败时出现的错误。在像这样的玩具实验中,strace 是编写检查错误的代码的快速而肮脏的替代品。做一个或另一个是必要的,以排除这种愚蠢的错误。
  • 使用x/10i 0xwhatever之类的东西来反汇编。

标签: c assembly x86 shellcode


【解决方案1】:

因为这是一个 shellcode,你不能有空字节。在您的代码中,您有 2 个带有立即数的 mov,它们被填充到 32 位

mov eax, 1
mov ebx, 12

编码为 B801000000BB0C000000,当 C 遇到空字节时,它认为字符串已经结束,所以它只最终复制部分指令,然后它执行垃圾。

相反,您需要使用:

xor eax, eax
inc eax
xor ebx, ebx
mov bl, 12

这将为您的系统调用提供您想要的值,并且不会编码为任何空字节。

【讨论】:

    猜你喜欢
    • 2012-12-24
    • 1970-01-01
    • 2013-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-21
    相关资源
    最近更新 更多