MOV RAX, 0x68732f6e69622f
CDQ
PUSH RAX
PUSH RSP
POP RDI
PUSH RDX
PUSH 0x632d
PUSH RSP
POP RSI
PUSH RDX
CALL FUN_0000001e
INSB RDI, DX
JNC FUN_0000001e
FUN_0000001e:
PUSH RSI
PUSH RDI
PUSH RSP
POP RSI
PUSH 0x3b
POP RAX
SYSCALL
这些是 msfvenom 有效负载的汇编指令:
msfvenom -p linux/x64/exec CMD='ls'
我了解 execve 系统调用是如何准备的,以及 /bin/sh -c 作为参数传递的。但是这段代码是如何执行 ls 命令的呢?
我尝试使用 c 函数指针执行此 shellcode,它按预期执行了 ls。
在调试器中单步执行并观察堆栈上的值变化。
0x68732f6e69622f
是 /bin/sh
push rsp
/pop rdi
是一种 2 字节的方式来实现 mov rdi, rsp
。
insb
/ jnc
实际上并没有运行,这些只是数据。 insb
的机器码就是0x6c
;这是一个“字符串”指令,隐式使用 [rdi]
后递增。我不知道这是什么 asm 语法,但它不是 NASM。为了让 NASM 组装它,我只是删除了操作数,所以它很简单 insb
。
如果你组装它并查看机器代码,你可以看到字节:
...
401017: e8 03 00 00 00 call 40101f <FUN_0000001e>
40101c: 6c ins BYTE PTR es:[rdi],dx
40101d: 73 00 jae 40101f <FUN_0000001e>
000000000040101f <FUN_0000001e>:
40101f: 56 push rsi
...
ASCII
63 73 00
是 "ls"
以零结尾。这就是 call
推送地址的字符串。
普通人会将其写为
db 0x6c, 0x73, 0
或 db "ls", 0
,而不是将这些字节“分解”为指令助记符。
请注意,此“shellcode”并非不受
0
字节的影响:前向 call
将在 rel32
的高 3 个字节中包含零(推送的“返回”地址是 ASCII 字节的地址)跟着它)。此外,push 0x632d
是符号扩展 imm32 的 qword 推送,其高 2 个字节为 0,因为没有 push word
大小覆盖。
并且 rel8=0 的
jae
当然有一个零字节。