Introduction to x64
x64简介
x64,也称为x86-64,是一种主要用于台式机和服务器计算的64位处理器架构。起源于由英特尔生产的x86架构,后来被AMD采用并命名为AMD64,是今天个人计算机和服务器中普遍采用的架构。
寄存器
x64扩展了x86架构,具有16个通用寄存器,标记为rax
、rbx
、rcx
、rdx
、rbp
、rsp
、rsi
、rdi
,以及r8
到r15
。每个寄存器可以存储64位(8字节)的值。这些寄存器还具有32位、16位和8位的子寄存器,用于兼容性和特定任务。
rax
- 传统上用于从函数中返回值。rbx
- 经常用作内存操作的基址寄存器。rcx
- 通常用于循环计数器。rdx
- 用于包括扩展算术运算在内的各种角色。rbp
- 栈帧的基指针。rsp
- 栈指针,跟踪栈的顶部。rsi
和rdi
- 用于字符串/内存操作中的源和目的索引。r8
到r15
- x64中引入的额外通用寄存器。
调用约定
x64的调用约定在操作系统之间有所不同。例如:
Windows:前四个参数通过寄存器**
rcx
、rdx
、r8
和r9
传递。额外的参数被推送到栈上。返回值在rax
**中。System V(在类UNIX系统中常用):前六个整数或指针参数通过寄存器**
rdi
、rsi
、rdx
、rcx
、r8
和r9
传递。返回值也在rax
**中。
如果函数有超过六个输入,则其余参数将通过栈传递。RSP,即栈指针,必须是16字节对齐,这意味着在进行任何调用之前,它指向的地址必须能被16整除。这意味着通常我们需要确保我们的shellcode在进行函数调用之前RSP被正确对齐。然而,在实践中,即使不满足这一要求,系统调用也经常能够正常工作。
Swift中的调用约定
Swift有自己的调用约定,可以在https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#x86-64找到。
常见指令
x64指令具有丰富的集合,保持与早期x86指令的兼容性并引入新指令。
mov
:将一个值从一个寄存器或内存位置移动到另一个。示例:
mov rax, rbx
— 将rbx
中的值移动到rax
。push
和pop
:将值推送到/从栈中弹出。示例:
push rax
— 将rax
中的值推送到栈上。示例:
pop rax
— 将栈顶值弹出到rax
中。add
和sub
:加法和减法操作。示例:
add rax, rcx
— 将rax
和rcx
中的值相加,并将结果存储在rax
中。mul
和div
:乘法和除法操作。注意:这些操作对操作数的使用有特定行为。call
和ret
:用于调用和从函数返回。int
:用于触发软件中断。例如,在32位x86 Linux中,int 0x80
用于系统调用。cmp
:比较两个值并根据结果设置CPU的标志。示例:
cmp rax, rdx
— 比较rax
和rdx
。je
、jne
、jl
、jge
等:条件跳转指令,根据先前的cmp
或测试结果改变控制流。示例:在
cmp rax, rdx
指令之后,je label
— 如果rax
等于rdx
,则跳转到label
。syscall
:在一些x64系统(如现代Unix)中用于系统调用。sysenter
:在某些平台上优化的系统调用指令。
函数序言
推送旧的基指针:
push rbp
(保存调用者的基指针)将当前栈指针移动到基指针:
mov rbp, rsp
(为当前函数设置新的基指针)为本地变量在栈上分配空间:
sub rsp, <size>
(其中<size>
是所需字节数)
函数结语
将当前基指针移动到栈指针:
mov rsp, rbp
(释放本地变量)从栈中弹出旧的基指针:
pop rbp
(恢复调用者的基指针)返回:
ret
(将控制返回给调用者)
macOS
系统调用
有不同类别的系统调用,你可以在这里找到它们:
#define SYSCALL_CLASS_NONE 0 /* Invalid */
#define SYSCALL_CLASS_MACH 1 /* Mach */
#define SYSCALL_CLASS_UNIX 2 /* Unix/BSD */
#define SYSCALL_CLASS_MDEP 3 /* Machine-dependent */
#define SYSCALL_CLASS_DIAG 4 /* Diagnostics */
#define SYSCALL_CLASS_IPC 5 /* Mach IPC */
然后,您可以在此网址中找到每个系统调用号:
0 AUE_NULL ALL { int nosys(void); } { indirect syscall }
1 AUE_EXIT ALL { void exit(int rval); }
2 AUE_FORK ALL { int fork(void); }
3 AUE_NULL ALL { user_ssize_t read(int fd, user_addr_t cbuf, user_size_t nbyte); }
4 AUE_NULL ALL { user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); }
5 AUE_OPEN_RWTC ALL { int open(user_addr_t path, int flags, int mode); }
6 AUE_CLOSE ALL { int close(int fd); }
7 AUE_WAIT4 ALL { int wait4(int pid, user_addr_t status, int options, user_addr_t rusage); }
8 AUE_NULL ALL { int nosys(void); } { old creat }
9 AUE_LINK ALL { int link(user_addr_t path, user_addr_t link); }
10 AUE_UNLINK ALL { int unlink(user_addr_t path); }
11 AUE_NULL ALL { int nosys(void); } { old execv }
12 AUE_CHDIR ALL { int chdir(user_addr_t path); }
[...]
因此,为了从Unix/BSD类调用open
系统调用(5),您需要将其添加为:0x2000000
因此,调用open的系统调用号将是0x2000005
Shellcodes
要编译:
nasm -f macho64 shell.asm -o shell.o
ld -o shell shell.o -macosx_version_min 13.0 -lSystem -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib
提取字节:
# Code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/b729f716aaf24cbc8109e0d94681ccb84c0b0c9e/helper/extract.sh
for c in $(objdump -d "shell.o" | grep -E '[0-9a-f]+:' | cut -f 1 | cut -d : -f 2) ; do
echo -n '\\x'$c
done
# Another option
otool -t shell.o | grep 00 | cut -f2 -d$'\t' | sed 's/ /\\x/g' | sed 's/^/\\x/g' | sed 's/\\x$//g'
最后更新于