shellcode 在作为单独的代码单独运行时调用不同的系统调用,而 运行 C++ 代码
shellcode calls different syscall while runing alone as individiual code and while running with C++ code
我有这样一个代码 运行 的 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
尽管如此,当我将其转换为二进制形式时:
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 不是 运行ning.
运行与
结合后
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 - 功能未实现
所以程序(又名 shell代码)可能是 运行 不正确的系统调用。
在您的 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 可执行文件中 hexdump 了 arg
的绝对地址,这就是您将 0x34
作为绝对地址的方式。
显然,如果要从随机堆栈地址 运行 没有重定位修复,那么在 shellcode 中嵌入绝对地址的整个方法都行不通。 dq arg, 0
与位置无关。
您至少需要使用指针自己构造 argv
数组(通常使用 push
)。您还可以使用 push imm32
来构造 arg
本身。例如push 'shsh'
/ lea rax, [rsp+2]
.
或者最常见的技巧是利用 Linux 特定的 "feature":您可以将 argv=NULL
(而不是指向 NULL 指针的指针)与 xor esi,esi
.
(使用 mov reg,0
完全违背了避免零字节的 jmp/call/pop 技巧的目的。如果允许零字节,您还不如使用普通的 RIP 相关 LEA。但如果不允许,您可以向前跳转数据,然后使用具有负位移的 RIP 相关 LEA。)
我有这样一个代码 运行 的 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
尽管如此,当我将其转换为二进制形式时:
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 不是 运行ning.
运行与
结合后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 - 功能未实现
所以程序(又名 shell代码)可能是 运行 不正确的系统调用。
在您的 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 可执行文件中 hexdump 了 arg
的绝对地址,这就是您将 0x34
作为绝对地址的方式。
显然,如果要从随机堆栈地址 运行 没有重定位修复,那么在 shellcode 中嵌入绝对地址的整个方法都行不通。 dq arg, 0
与位置无关。
您至少需要使用指针自己构造 argv
数组(通常使用 push
)。您还可以使用 push imm32
来构造 arg
本身。例如push 'shsh'
/ lea rax, [rsp+2]
.
或者最常见的技巧是利用 Linux 特定的 "feature":您可以将 argv=NULL
(而不是指向 NULL 指针的指针)与 xor esi,esi
.
(使用 mov reg,0
完全违背了避免零字节的 jmp/call/pop 技巧的目的。如果允许零字节,您还不如使用普通的 RIP 相关 LEA。但如果不允许,您可以向前跳转数据,然后使用具有负位移的 RIP 相关 LEA。)