Análisis del principio ROP (Programación Orientada a Retorno)

Primero mira un código:

#include <stdio.h>
#include <stdlib.h>

// 下面的dummy_libc_part1和dummy_libc_part2假设是GLIBC库里的任意两段函数
void dummy_libc_part1()
{
    
    
	// ... 这里可能会有别的指令
	__asm("mov 0(%rsp), %rdi");
	__asm("popq %r13");
	__asm("call *%r14");
	__asm("ret");
	// ... 这里可能会有别的指令
}

void dummy_libc_part2()
{
    
    
	// ... 这里可能会有别的指令
	__asm("popq %r14");
	__asm("ret");
	// ... 这里可能会有别的指令
}

int main(int argc, char **argv)
{
    
    
	__asm("pushq $0x400545");
	__asm("pushq $0x62");
	__asm("pushq $0x400521");
	__asm("pushq $0x400400");
	__asm("pushq $0x40052f");
	__asm("ret");
	printf(".");
}

¿Adivina cuál será el resultado? No intente compilar y ejecutar, puede que no sea igual en su máquina que la nuestra.

Suponemos que dummy_libc_part1 / dummy_libc_part2 son todas secuencias existentes, similares a las de GLIBC. Luego, utilizando estas secuencias de instrucciones, no es necesario escribir ninguna instrucción. Solo necesita apilar datos en la pila para lograr saltos arbitrarios en la lógica del programa. ¡Esto es ROP! Utilice la instrucción ret ya preparada para organizar cuidadosamente el contenido en la pila para lograr la inyección de código.

En el espacio de direcciones de un programa, el lugar más fácil de tocar es la pila. De esta manera, uno de los problemas principales de ROP es cómo organizar inteligentemente los datos en la pila.

GLIBC es un tesoro de secuencias de instrucciones, donde puede encontrar casi cualquier secuencia que pueda usar. Pero el problema radica en cómo se pueden unir algunas secuencias dispersas en una lógica basada en push / call y return / pop. Esta no es una tarea fácil, al igual que todas las palabras del poema de Li Bai están en el diccionario, pero por No se pueden escribir los poemas de Li Bai en un diccionario.

Echemos un vistazo a la lógica de apilamiento de la pila en el código original de este artículo:
Inserte la descripción de la imagen aquí

En la práctica de ROP, definitivamente no usará push para construir una pila como el código anterior, sino que usará el desbordamiento del búfer. Las más notorias con vulnerabilidades de desbordamiento de búfer son las funciones de cadena.

Sin embargo, en la arquitectura x86_64, la función de cadena finalmente se eliminó, porque el alto 16 bits del espacio de direcciones del proceso se fuerza a 0, lo que significa que cuando se usa la función de cadena truncada en 0 para desbordar, solo se puede sobrescribir una dirección. Similar al siguiente marco de pila, el uso de funciones de cadena es absolutamente imposible de construir:

0x00007fffffffe498
0x00007fffffffe3b0
0x00007fffffffdcb8
0x00007ffff7aa39aa

Por lo tanto, la escritura de ROP es un arte muy hábil, su tarea es encontrar la secuencia de instrucción disponible en GLIBC o LIB vinculada por el proceso inyectado. El principio es simplemente permitirle comprender las palabras, párrafos y capítulos, y lo real fuera de contexto es un arte.

Los sistemas actuales vienen con protección ASLR (asignación aleatoria de diseño de espacio de direcciones). Por lo tanto, incluso si piratea con éxito un programa, el código de pirateo no es reutilizable. Tienes que hacer el mismo trabajo artístico cada vez. Cuando este trabajo continúa Después de la repetición, ya no es un arte, y el implementador degenera inmediatamente en un trabajador de la línea de producción.

No el gerente.


Los zapatos de cuero en Wenzhou, Zhejiang están mojados, por lo que no engordan con la lluvia.

Supongo que te gusta

Origin blog.csdn.net/dog250/article/details/108633034
Recomendado
Clasificación