【问题标题】:shellcode calls different syscall while runing alone as individiual code and while running with C++ codeshellcode 在作为单独代码单独运行和与 C++ 代码一起运行时调用不同的系统调用
【发布时间】:2019-12-08 16:23:07
【问题描述】:

我有这样一个运行shell的代码:

BITS 64

global _start
_start:

  mov rax, 59

  jmp short file
  c1:
  pop rdi

  jmp short argv
  c2:
  pop rsi

  mov rdx, 0

  syscall

file:

  call c1
  db '/bin/sh',0

argv:

  call c2
  dq arg, 0

arg:

  db 'sh',0

以这种方式构建时它可以工作:

nasm -f elf64 shcode.asm
ld shcode.o -o shcode

Althougt,当我把它变成二进制形式时:

nasm -f bin shcode.asm

将其粘贴到以下 C++ 代码中:

int main(void)
{

  char kod[]="\xB8\x3B\x00\x00\x00\xEB\x0B\x5F\xEB\x15\x5E\xBA\x00\x00\x00\x00\x0F\x05\xE8\xF0\xFF\xFF\xFF\x2F\x62\x69\x6E\x2F\x73\x68\x00\xE8\xE6\xFF\xFF\xFF\x34\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x73\x68\x00";
  reinterpret_cast<void(*)()>(kod)();

  return 0;

}

使用 clang++ texp.cpp -o texp.e -Wl,-z,execstack 并执行,shell 没有运行。

运行后

strace ./texp.e

我看到了这样的东西(我用 ^C 停止了这个过程):

syscall_0xffffffffffffffda(0x7ffc23e0a297, 0x7ffc23e0a2a4, 0, 0x4a0, 0x7fe1ff3039b0, 0x7fe1ff69b960) = -1 ENOSYS (Nie zaimplementowana funkcja)
syscall_0xffffffffffffffda(0x7ffc23e0a297, 0x7ffc23e0a2a4, 0, 0x4a0, 0x7fe1ff3039b0, 0x7fe1ff69b960) = -1 ENOSYS (Nie zaimplementowana funkcja)
.
.
.
syscall_0xffffffffffffffda(0x7ffc23e0a297, 0x7ffc23e0a2a4, 0, 0x4a0, 0x7fe1ff3039b0, 0x7fe1ff69b960) = -1 ENOSYS (Nie zaimplementowana funkcja)
^Csyscall_0xffffffffffffffda(0x7ffc23e0a297, 0x7ffc23e0a2a4, 0, 0x4a0,  0x7fe1ff3039b0, 0x7fe1ff69b960strace: Process 2806 detached
 <detached ...>

Nie zaimplementowana funkcja - 功能未实现

所以程序(又名 shellcode)可能正在运行不正确的系统调用。

【问题讨论】:

  • 离题:你不需要单独的jmp/call/pop 序列:一旦你有了一个序列,你就可以用lea rsi, [rdi + argv - file_data] 得到另一个4 字节的lea。 (在文件中的db 以及call 上放置file_data 标签。)

标签: c++ linux assembly nasm system-calls


【解决方案1】:

在您的 C++ shellcode 调用程序中,strace 显示您的 execve 系统调用是

execve("/bin/sh", [0x34], NULL)         = -1 EFAULT (Bad address)

后面的syscall_0xffffffffffffffda(...) = -1 ENOSYS 来自无限循环,其中 RAX = -EFAULT 而不是 59,然后来自RAX=- ENOSYS(同样不是有效的电话号码)。此循环由您的 call/pop 创建。


大概是因为您从未链接的 .o 或 PIE 可执行文件中十六进制转储了 arg 的绝对地址,这就是您将 0x34 作为绝对地址的方式。

如果要从随机堆栈地址运行且没有重定位修复,那么在 shellcode 中嵌入绝对地址的整个方法显然不起作用。 dq arg, 0 与位置无关。

您至少需要使用指针自己构造argv 数组(通常使用push)。您也可以使用push imm32 来构造arg 本身。例如push 'shsh' / lea rax, [rsp+2].

或者最常见的技巧是利用特定于 Linux 的“功能”:您可以使用 xor esi,esi 传递 argv=NULL(而不是指向 NULL 指针的指针)。

(使用 mov reg,0 完全违背了 jmp/call/pop 技巧避免零字节的目的。如果允许零字节,你最好使用普通的 RIP-relative LEA。但如果不是,你可以跳转转发数据,然后使用带有负位移的 RIP 相对 LEA。)

【讨论】:

  • 当我使用 xor rsi, rsi 而不是将 argv 移动到 rsi 运行它时,第一个代码仍然有效。第二个代码现在什么都不显示并退出,但 shell 仍然没有运行。
  • @RomanKwaśniewski:所以用 GDB 和 strace 调试它。你确定它不运行shell吗?无论哪种方式,您都会收到 shell 提示,因此除非您使用 strace,否则工作退出与静默退出之间的区别并不明显。除非您添加了_exit 系统调用,否则我看不到这段代码如何返回。顺便说一句,xor esi,esi 的 RSI 为零;这不是我的回答错误。无需引入无用的 REX 前缀来使 64 位归零显式而不是隐式写入 32 位寄存器。
  • 如何做到这一点push 'shsh' / lea rax, [rsp+2] 技巧?我尝试在不同的变体中执行此操作,并且每次都收到 Illegal instruction (core dumped)Segmentation fault (core dumped)
  • @RomanKwaśniewski:您只需创建一个由 RAX 指向的以零结尾的 "sh" 字符串。然后你推一个归零的寄存器,然后是 RAX 在堆栈上创建argv[],然后mov rsi, rsp
猜你喜欢
  • 1970-01-01
  • 2021-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多