[solved] C: Maschinen-Code ausführen / Speicherzugriffskontrolle

Hallo,

ich beschäftige mich gerade mal wieder mit dem, was unter der Haube der modernen Rechentechnik steckt. Inspiriert, von dem etwas in die Jahre gekommen Artikel Erstellen eines Shellcode - HackerWiki, wollte ich Maschinencode ausführen.

So einfach wie wie im o.g. Artikel geht es leider nicht (mehr), dass man Maschinencode von einem Characterarray in ein Funktionspointer castet und abfeuert.

Ich habe schon herausgefunden, dass man einen seitenalignierten (page aligned klingt doch besser) Speicherbereich benötigt, in welchem man die Daten kopiert und ihn via mprotect ausführbar macht. Dieses minimale Szenario segfaultet bei mir und leider habe ich in diesem Bereich noch nicht so viel Ahnung, dass ich mir selbst zu helfen weiß. Deshalb kommt jetzt hier mein Setup und ich hoffe auf interessante Tipps. :)

% nasm -v
NASM version 2.09.10 compiled on Jul 19 2011

% gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-unknown-linux-gnu/4.6.2/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: /build/src/gcc-4.6-20111125/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --enable-gnu-unique-object --enable-linker-build-id --with-ppl --enable-cloog-backend=isl --enable-lto --enable-gold --enable-ld=default --enable-plugin --with-plugin-ld=ld.gold --enable-multilib --disable-libssp --disable-libstdcxx-pch --enable-checking=release --with-fpmath=sse
Thread model: posix
gcc version 4.6.2 20111125 (prerelease) (GCC)

% uname -vr
3.1.5-1-ARCH #1 SMP PREEMPT Sat Dec 10 14:43:09 CET 2011

Für die Vollständigkeit die Bedeutung meines Maschinencodes:
Code:
; hello.asm
    mov ecx,msg
    mov edx,msglen
    mov ebx,1 ; STDOUT
    mov eax,4 ; Funktionsnummer: Ausgabe
    int 80h
    mov ebx,0 ; EXITCODE
    mov eax,1 ; Funknr: Exit
    int 80h


msg: db "Hello ya encrypted world!"
msglen: equ $ - msg
Übersetzt mit nasm -f bin -o hello.bin hello.asm.

Mein C-Code:
Code:
// loader.c
#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>


int main(){
// der als hex codierte Inhalt der eben erstellten .bin Datei
        const char *hello = "\x66\xb9\x28\x00\x00\x00\x66\xba\x19\x00\x00\x00\x66\xbb\x01\x00"
                "\x00\x00\x66\xb8\x04\x00\x00\x00\xcd\x80\x66\xbb\x00\x00\x00\x00"
                "\x66\xb8\x01\x00\x00\x00\xcd\x80\x48\x65\x6c\x6c\x6f\x20\x79\x61"
                "\x20\x65\x6e\x63\x72\x79\x70\x74\x65\x64\x20\x77\x6f\x72\x6c\x64"
                "\x21";
        const int len = 65;

        size_t size = sizeof(char) * len;

        printf("pos-hello: %08x\n", (long) hello);

        void* mem = memalign(sysconf(_SC_PAGESIZE), size);
        if(mem == NULL) {
                perror(0);
                return 1;
        }

        printf("pos-mem: %08x\n", (long) mem);

        memcpy(mem, hello, size);
        
        if(mprotect(mem, size, PROT_READ|PROT_WRITE|PROT_EXEC) == -1){
                perror(0); 
                return 1;
        }

        int (*F)() = mem;
        printf("pos-F: %08x\n", (long) F);
        F();
}
Übersetzt mit gcc -o loader loader.c.

Ausgabe des Programmes (und meiner Shell):
% ./loader
pos-hello: 00400880
pos-mem: 00fb7000
pos-F: 00fb7000
[1] 27673 segmentation fault ./loader

Also meiner Meinung nach sollte es funktionieren, vielleicht habe ich auch einen trivialen Fehler gemacht und seh ihn nur nicht. Aber viele Augen sehen ja bekanntlich mehr als zwei.

Gruß, Thul
 
Zuletzt bearbeitet:
Hi,

Nach einem kurzen Blick darauf fallen mir zwei Sachen auf:
1.) Die vielen 0x66 Prefixes in deinem Shellcode deuten darauf hin, dass du BITS 32 in der hello.asm vergessen hast der Assembler 16-Bit Code generiert.
2.) Shellcode sollte immer positionsunabhängig sein. Dein mov ecx,msg führt dazu, dass ecx dann bei der Ausführung im C-Programm nicht mehr auf die richtige Adresse zeigt. Du kannst das für den Anfang z.b. so lösen

Code:
call over_txt
msg db "Hello SC-World!",0
over_txt:
pop ecx

Bei einem call wird die Rücksprungadresse, in diesem Fall die Adresse von MSG, auf dem Stack abgelegt und dann mit pop ecx wieder ins gewünschte ECX Register geladen.

Ich hoffe, du kommst dem Ziel damit ein Stück näher.

Mfg
 
\x66\xb9\x28\x00\x00\x00
66:16/32 Bit Prefix
b9 28: mov ecx, 0x28 <-- String befindet sich also an Adresse 0x28 ?
int 80h <-- 32-bit Interface. Bei 64-bit sollte SYSCALL verwendet werden
Sprich: der Code sollte bei einem 64-bit System auch selbst 64-bittig sein ;)

linuxsyscalls
bzw. in "kurz":
Low-Level Assembler unter Linux
Code:
[B]Syscallnummern SYSCALL[/B]

 [COLOR=#666666][I]; FILE syscall_64_syscall.inc[/I][/COLOR]

[COLOR=#339933]%[/COLOR]define [COLOR=#00007f][B]_syscall[/B][/COLOR]    [COLOR=#000000][B]syscall[/B][/COLOR]
[COLOR=#339933]%[/COLOR]define [COLOR=#00007f]_nr[/COLOR]         [COLOR=#00007f]rax[/COLOR]
[COLOR=#339933]%[/COLOR]define [COLOR=#00007f]_arg1[/COLOR]       [COLOR=#00007f]rdi[/COLOR]
[COLOR=#339933]%[/COLOR]define [COLOR=#00007f]_arg2[/COLOR]       [COLOR=#00007f]rsi[/COLOR]
[COLOR=#339933]%[/COLOR]define [COLOR=#00007f]_arg3[/COLOR]       [COLOR=#00007f]rdx[/COLOR]
[COLOR=#339933]%[/COLOR]define [COLOR=#00007f]_arg4[/COLOR]       [COLOR=#00007f]rcx[/COLOR]
[COLOR=#339933]%[/COLOR]define [COLOR=#00007f]_arg5[/COLOR]       [COLOR=#00007f]r8[/COLOR]
[COLOR=#339933]%[/COLOR]define [COLOR=#00007f]_arg6[/COLOR]       [COLOR=#00007f]r9[/COLOR]

[COLOR=#339933]%[/COLOR]define _sys_read 0
[COLOR=#339933]%[/COLOR]define _sys_write 1
...
[COLOR=#339933]%[/COLOR]define _sys_exit 60
Das ganze sollte in diese Richtung gehen (64-Bit Assemby nie ernsthaft betrieben):
Code:
jmp my_code
msg: db "Hello ya encrypted world!"
msglen equ $ - msg
my_code:
    mov rax,1 
    mov rdi,1 ;stdout
    lea rsi,[rel msg]
    mov rdx, msglen
    syscall

    mov rdi,0
    mov rax,60h ; Funknr: Exit
    syscall
    ret
 
Danke Ihr beiden,

ss5s bemerkte Auffälligkeiten haben abhilfe geschaffen. Tatsächlich bräuchte ich die 64 Bit variante, aber die 32 Bit variante tut auch und irgendwie war es doch ein offensichtlicher Fehler den Code nicht frei von Positionierung zu halten.

Jedenfalls funktioniert es jetzt und ich freue mich. :) Werd mir dann mal die 64 Bit variante zu Gemüte führen.

Thul
 
Also dein Code funktioniert einwandfrei, wobei das return tatsächlich wichtig ist, da es sonst zu nem segfault kommt, sollte der syscall an exit nicht schon das Programm beenden?
 
Zurück
Oben