Entiendo que una qword es un dato de tamaño de 64 bits, pero cuando quito qword el shellcode no funciona. ¿Por qué necesito agregar qword?
Porque estás moviendo una constante qword. Cuando escribes '//bin/sh'
estás expresando una matriz de 8 caracteres. Al hacer un prefijo con qword, le indica al ensamblador que trate el operando de la derecha como un entero de 64 bits en línea. De manera predeterminada, probablemente lo tratará como un puntero a esa cadena, o algún otro comportamiento.
Y después de que se enciendan las instrucciones, mis cadenas deben ser como '% 00 // bin / s' ¿no?
Los enteros
en los procesadores de arquitectura x86 se almacenan como little-endian . Si su compilador lo tomó literalmente, su cadena se codificaría en ASCII de la siguiente manera:
/ / b i n / s h
2f 2f 62 69 6e 2f 73 68
Lo que también puede mostrar en la siguiente instrucción:
mov rbx, 0x2f2f62696e2f7368
Esto se codifica de la siguiente manera:
48 BB 68 73 2F 6E 69 62 2F 2F
Podemos dividir esto en 3 secciones:
48 BB 68 73 2F 6E 69 62 2F 2F
| | |---------------------|
\ \ literal
64-bit B8 = MOV instr
prefix + reg operand
+ r/m operand
En este caso, el byte de instrucciones mov ( BB
) también contiene un campo de 7 bits que describe el registro al que nos estamos moviendo ( rbx
) y el tipo de operando de la derecha (literal).
Pero tenga en cuenta: el literal es no en el mismo orden que la representación ASCII que vimos anteriormente. En cambio, se invierte debido a la poca endiancia.
El problema aquí, por supuesto, es que esto no puede ser lo que ocurra en tu shellcode. Si el nombre se invirtió en los bytes, la función a la que llama vería hs/nib//
y fallaría. En su lugar, su ensamblador es lo suficientemente inteligente como para darse cuenta de que quiere esos caracteres ASCII en orden de bytes, lo que significa que realmente codifica la siguiente instrucción:
mov rbx, 0x68732f6e69622f2f
Esto produce la siguiente instrucción codificada:
48 BB 2F 2F 62 69 6E 2F 73 68
Como puede ver, el //
aparece primero ( 2F 2F
) en la representación de orden de bytes.
¿Por qué no usar shl en lugar de shr?
Usted hace ambas cosas. A su código le falta una instrucción, que se puede ver en el tutorial. Después de shl rbx, 0x8
, inmediatamente sigue un shr rbx, 0x8
.
Su explicación le hace justicia:
Después de desplazar 0x08 a la izquierda (piense en cada marcador de posición numérico en la cadena como un 4, de modo que mover 8 bytes más en realidad nos mueve dos espacios) empuja el 11 a la derecha del final. Ahora tenemos esto:
0x68732f6e69622f00
Luego lo devolvemos como antes, usando shr por el mismo valor, y obtenemos esto:
0x0068732f6e69622f
Esto nos da con éxito nuestra cadena terminada por un nullbyte sin generar un solo nullbyte en el código de la máquina.
La primera barra es simplemente un carácter de sacrificio que se descarta durante el shl
.
El motivo de este truco, en lugar de simplemente cargar el nulo en primer lugar o enmascarar el byte más a la derecha con cero, es que no requiere bytes nulos en la carga útil del exploit, lo cual es muy útil si la entrada se ignora después de un nulo ( por ejemplo, si una cadena se está copiando con strcpy).