Soll ein Shellcode in ein Programm übergeben werden, so wird dies meist mit den C-Funktionen sprintf(), read() und strcpy() in dem Speicher der Applikation injiziert. Den meisten Programmieren ist bekannt, das diese Stringfunktionen der Sprache C davon ausgehen, das der zu verarbeitende String durch ein NULL-Byte abgeschlossen bzw. terminiert wird. Genau diesem Fakt liegt ein Problem zu Grunde, möchte ein Angreifer seinen Shellcode durch eine dieser Funktionen dem Opferprogramm übergeben. Der Shellcode darf keine NULL-Bytes beinhalten, da ansonsten der Rest des Shellcodes nicht ausgeführt wird. Das NULL-Byte im Programmcode wird als String-Termination erkannt. Der nachfolgende Code wird verworfen.
Natürlich gibt es zu diesem Problem eine Abhilfe. Der Programmierer vermeidet durch einen Trick, das sein Shellcode NULL-Bytes beinhaltet. Auf diese Weise kann der Code vollständig übergeben werden, ohne das es eine böse Überraschung durch beispielsweise UNIX-Systemaufrufe gibt. Soll der Shellcode beispielsweise einen Systemaufruf vollführen und dabei ein Parameter an das Programm übergeben, so muss dieser String NULL-terminiert sein. Jedoch darf die C-Funktion dieses Stück nicht als Abschluß der Eingabe interpretieren.
Hierzu ein kleines Beispiel. Möchte der Programmierer ein normales Programm in Assembler schreiben, nehmen wir das bekannte “Hello world!”, so könnte er folgendermaßen den String definieren:
“Hello world!”,0×00
Selbstverständlich haben wir nun einen Shellcode, der ein NULL-Byte enthält, das sollte vermieden werden. Um jetzt das Problem zu umgehen, läßt der Programmierer den String zur Laufzeit terminieren, indem er ein NULL-Bytean das Ende stellt. Hierzu ein Beispiel.
XOR EAX, EAX
MOV BYTE [EBX + 13], AL
Nun wird das Register EBX als Pointer auf den String “Hello world!” verwendet. Der Programmierer setzt nun den Inhalt von EAX gleich 0 bzw. NULL, indem er sich der XOR Operation bedient und diese mit dem Register selbst ausführt. Auf diese Weise ist egal, welcher Wert sich vorher dort befand, das Ergebnis ist dannach 0 bzw. NULL. Anschließend schreibt der Programmierer AL, das sind 8-Bit aus dem 32-Bit EAX Register, an den Offset 13 unseres Strings. Nachdem der Programmierer diese Anweisungen ausgeführt hat, ist der String “Hello world!” NULL-terminiert, ohne das ein NULL-Byte im Programmcode vorkommt.
Wählt der Programmierer die falschen Register oder Datentypen, so kann das Ergebnis des Shellcodes NULL-Bytes enthalten. Die ASM-Anweisung MOV EAX, 1 wird beispielsweise im Compiler in
MOV EAX, 0×00000001
umgewandelt. Der Programmierer hat explizit angewiesen, das 32-Bit Register EAX mit 1 zu belegen, der Compiler erzeugt eine entsprechendes Übersetzungsergebnis. Wenn der Programmierer statt EAX das 8-Bit Register AL verwendet, so steht keine NULL-Byte im Shellcode, ausgeliefert vom Compiler. Es wird von einem Register kopiert, das einen Wert, den der Programmierer haben möchte, beinhaltet. Durch die XOR-Operation wurde der Wert entsprechend gesetzt. Der Programmierer muss kein NULL-Byte übertragen, da die XOR Anweisung ein NULL-Byte als Ergebnis im Register herstellt, dieses nun im EAX Register zur Verfügung steht. Wenn nur 8 Bit davon gebraucht werden, so zieht sich der Programmierer diese 8-Bit mit dem AL Register, ein Teilstrück von EAX, heraus.