GNU/Linux >> Linux の 問題 >  >> Linux

Linux シェルコード Hello, World!

BSH が述べたように、シェルコードにはメッセージ バイトが含まれていません。 MESSAGE にジャンプ ラベルと GOBACK の呼び出し msg を定義する直前のルーチン msg のアドレスは ecx にポップできる戻りアドレスとしてスタックの一番上にあるため、byte は適切な動きでした。 、msg のアドレスが保存されます。

ただし、あなたのコードと BSH のコードの両方には、わずかな制限があります。NULL bytes ( \x00 ) が含まれています。 関数ポインターによって逆参照されると、文字列の末尾と見なされます。

これを回避する賢い方法があります。 eax, ebx and edx に格納する値 al, bl and dl にアクセスすることで、それぞれのレジスタの下位ニブルに一度に直接書き込むことができるほど小さい 上部のニブルにはジャンク値が含まれている可能性があるため、xored にすることができます。

b8 04 00 00 00 ------ mov $0x4,%eax


になる

b0 04          ------ mov $0x4,%al
31 c0          ------ xor    %eax,%eax


以前の命令セットとは異なり、新しい命令セットには NULL バイトが含まれていません。

したがって、最終的なプログラムは次のようになります:

global _start

section .text

_start:
jmp message

proc:
    xor eax, eax
    mov al, 0x04
    xor ebx, ebx
    mov bl, 0x01
    pop ecx
    xor edx, edx
    mov dl, 0x16
    int 0x80

    xor eax, eax
    mov al, 0x01
    xor ebx, ebx
    mov bl, 0x01   ; return 1
    int 0x80

message:
    call proc
    msg db " y0u sp34k 1337 ? "

section .data

組み立てとリンク:

$ nasm -f elf hello.asm -o hello.o
$ ld -s -m elf_i386 hello.o -o hello
$ ./hello
 y0u sp34k 1337 ? $ 

次に、hello バイナリからシェルコードを抽出します:

$ for i in `objdump -d hello | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\\x$i" ; done

出力:

\xeb\x19\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x59\x31\xd2\xb2\x12\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xb3\x01\xcd\x80\xe8\xe2\xff\xff\xff\x20\x79\x30\x75\x20\x73\x70\x33\x34\x6b\x20\x31\x33\x33\x37\x20\x3f\x20

これで、シェルコードを起動するドライバー プログラムを作成できます。

#include <stdio.h>

char shellcode[] = "\xeb\x19\x31\xc0\xb0\x04\x31\xdb"
                   "\xb3\x01\x59\x31\xd2\xb2\x12\xcd"
                   "\x80\x31\xc0\xb0\x01\x31\xdb\xb3"
                   "\x01\xcd\x80\xe8\xe2\xff\xff\xff"
                   "\x20\x79\x30\x75\x20\x73\x70\x33"
                   "\x34\x6b\x20\x31\x33\x33\x37\x20"
                   "\x3f\x20";


int main(int argc, char **argv) {
    (*(void(*)())shellcode)();
    return 0;
}

最新のコンパイラには、データ セグメントまたはスタックでのコードの実行を防止する NX 保護などの特定のセキュリティ機能があります。したがって、これらを無効にするコンパイラを明示的に指定する必要があります。

$ gcc -g -Wall -fno-stack-protector -z execstack launcher.c -o launcher

launcher シェルコードを起動するために呼び出すことができます。

$ ./launcher
 y0u sp34k 1337 ? $ 

より複雑なシェルコードの場合、別のハードルがあります。最新の Linux カーネルには ASLR または Address Space Layout Randomization があります シェルコードを挿入する前に、これを無効にする必要がある場合があります。特に、バッファ オーバーフローによる場合です。

[email protected]:~# echo 0 > /proc/sys/kernel/randomize_va_space 

このシェルコードを挿入すると、message に何があるかわかりません :

mov ecx, message

注入されたプロセスでは、それは何でもかまいませんが、"Hello world!\r\n" にはなりません テキストセクションのみをダンプしている間、データセクションにあるためです。シェルコードに "Hello world!\r\n" がないことがわかります :

"\xb8\x04\x00\x00\x00"
"\xbb\x01\x00\x00\x00"
"\xb9\x00\x00\x00\x00"
"\xba\x0f\x00\x00\x00"
"\xcd\x80\xb8\x01\x00"
"\x00\x00\xbb\x00\x00"
"\x00\x00\xcd\x80";

これは、シェルコード開発でよくある問題です。これを回避する方法は次のとおりです:

global _start

section .text

_start:
    jmp MESSAGE      ; 1) lets jump to MESSAGE

GOBACK:
    mov eax, 0x4
    mov ebx, 0x1
    pop ecx          ; 3) we are poping into `ecx`, now we have the
                     ; address of "Hello, World!\r\n" 
    mov edx, 0xF
    int 0x80

    mov eax, 0x1
    mov ebx, 0x0
    int 0x80

MESSAGE:
    call GOBACK       ; 2) we are going back, since we used `call`, that means
                      ; the return address, which is in this case the address 
                      ; of "Hello, World!\r\n", is pushed into the stack.
    db "Hello, World!", 0dh, 0ah

section .data

テキスト セクションをダンプします。

$ nasm -f elf shellcode.asm
$ ld shellcode.o -o shellcode
$ ./shellcode 
Hello, World!
$ objdump -d shellcode

shellcode:     file format elf32-i386


Disassembly of section .text:

08048060 <_start>:
 8048060:   e9 1e 00 00 00   jmp    8048083 <MESSAGE>

08048065 <GOBACK>:
 8048065:   b8 04 00 00 00   mov    $0x4,%eax
 804806a:   bb 01 00 00 00   mov    $0x1,%ebx
 804806f:   59               pop    %ecx
 8048070:   ba 0f 00 00 00   mov    $0xf,%edx
 8048075:   cd 80            int    $0x80
 8048077:   b8 01 00 00 00   mov    $0x1,%eax
 804807c:   bb 00 00 00 00   mov    $0x0,%ebx
 8048081:   cd 80            int    $0x80

08048083 <MESSAGE>:
 8048083:   e8 dd ff ff ff   call   8048065 <GOBACK>
 8048088:   48               dec    %eax                    <-+
 8048089:   65               gs                               |
 804808a:   6c               insb   (%dx),%es:(%edi)          |
 804808b:   6c               insb   (%dx),%es:(%edi)          |
 804808c:   6f               outsl  %ds:(%esi),(%dx)          |
 804808d:   2c 20            sub    $0x20,%al                 |
 804808f:   57               push   %edi                      |
 8048090:   6f               outsl  %ds:(%esi),(%dx)          |
 8048091:   72 6c            jb     80480ff <MESSAGE+0x7c>    |
 8048093:   64               fs                               |
 8048094:   21               .byte 0x21                       |
 8048095:   0d               .byte 0xd                        |
 8048096:   0a               .byte 0xa                      <-+

$

私がマークした行は "Hello, World!\r\n" です 文字列:

$ printf "\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21\x0d\x0a"
Hello, World!

$ 

したがって、C ラッパーは次のようになります。

char code[] = 

    "\xe9\x1e\x00\x00\x00"  //          jmp    (relative) <MESSAGE>
    "\xb8\x04\x00\x00\x00"  //          mov    $0x4,%eax
    "\xbb\x01\x00\x00\x00"  //          mov    $0x1,%ebx
    "\x59"                  //          pop    %ecx
    "\xba\x0f\x00\x00\x00"  //          mov    $0xf,%edx
    "\xcd\x80"              //          int    $0x80
    "\xb8\x01\x00\x00\x00"  //          mov    $0x1,%eax
    "\xbb\x00\x00\x00\x00"  //          mov    $0x0,%ebx
    "\xcd\x80"              //          int    $0x80
    "\xe8\xdd\xff\xff\xff"  //          call   (relative) <GOBACK>
    "Hello wolrd!\r\n";     // OR       "\x48\x65\x6c\x6c\x6f\x2c\x20\x57"
                            //          "\x6f\x72\x6c\x64\x21\x0d\x0a"


int main(int argc, char **argv)
{
    (*(void(*)())code)();

    return 0;
}

-z execstack を使ってテストしてみましょう read-implies-exec (名前に「スタック」が含まれていますが、プロセス全体) を有効にして、.data でコードを実行できるようにします。 または .rodata セクション:

$ gcc -m32 test.c -z execstack -o test
$ ./test 
Hello wolrd!

できます。 (-m32 64 ビット システムでも必要です。 int $0x80 32 ビット ABI は .rodata のような 64 ビット アドレスでは機能しません PIE 実行可能ファイルで。また、マシン コードは 32 ビット用にアセンブルされています。たまたま、同じバイト シーケンスが 64 ビット モードで同等の命令にデコードされることがありますが、常にそうとは限りません。)

最新の GNU ld puts .rodata .text とは別のセグメントで であるため、実行不可になる可能性があります。以前は const char code[] を使用するだけで十分でした 読み取り専用データのページに実行可能コードを配置します。少なくとも、自分自身を変更したくないシェルコードの場合.


Linux
  1. Linuxcatコマンドの使用を開始する

  2. Linux –ファイルパーミッションは実行のみですか?

  3. Linuxgrepコマンドを使用する

  1. Smalltalk Hello World の例:Linux OS で Smalltalk プログラムを作成して実行する方法

  2. Fortran Hello World の例:Linux OS で Fortran プログラムを作成して実行する方法

  3. XQuery Hello World の例:Linux OS で XQuery プログラムを作成して実行する方法

  1. Linux –モノマジカルはどうですか?

  2. Linux ターミナルで C# コードを実行する

  3. Linux で WD MyBook World ネットワーク ドライブをマウントするにはどうすればよいですか?