Analyse du principe ROP (Return Oriented Programming)

Regardez d'abord un code:

#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(".");
}

Devinez quel sera le résultat? N'essayez pas de compiler et d'exécuter, il se peut que ce ne soit pas la même chose sur votre machine que la nôtre.

Nous supposons que dummy_libc_part1 / dummy_libc_part2 sont toutes des séquences existantes, similaires à celles de GLIBC. Ensuite, en utilisant ces séquences d'instructions, vous n'avez pas besoin d'écrire d'instructions. Il vous suffit d'empiler des données sur la pile pour réaliser des sauts arbitraires dans la logique du programme. C'est ROP! Utilisez l'instruction ret prête à l'emploi pour organiser soigneusement le contenu sur la pile pour réaliser l'injection de code.

Dans l'espace d'adressage d'un programme, l'endroit le plus facile pour nous de toucher est la pile. De cette façon, l'un des principaux problèmes de ROP est de savoir comment organiser intelligemment les données sur la pile.

GLIBC est un trésor de séquences d'instructions, où vous pouvez trouver presque toutes les séquences que vous pouvez utiliser. Mais le problème réside dans la façon dont vous pouvez assembler certaines séquences dispersées en une logique basée sur push / call et return / pop. Ce n’est pas une tâche facile, tout comme chaque mot du poème de Li Bai est dans le dictionnaire, mais pour Vous ne pouvez pas écrire les poèmes de Li Bai dans un dictionnaire.

Jetons un coup d'œil à la logique d'empilement de stack dans le code d'origine de cet article:
Insérez la description de l'image ici

Dans la pratique ROP, il n'utilisera certainement pas push pour construire la pile comme le code ci-dessus, mais utilisera un débordement de tampon. Les plus notoires avec des vulnérabilités de débordement de tampon sont, par exemple, les fonctions de chaîne.

Cependant, dans l'architecture x86_64, la fonction de chaîne a finalement été effacée, car les 16 bits élevés de l'espace d'adressage du processus sont forcés à 0, ce qui signifie que lors de l'utilisation de la fonction de chaîne tronquée par 0 pour déborder, une seule adresse peut être écrasée. Semblable au cadre de pile suivant, l'utilisation de fonctions de chaîne est absolument impossible à construire:

0x00007fffffffe498
0x00007fffffffe3b0
0x00007fffffffdcb8
0x00007ffff7aa39aa

Par conséquent, l'écriture de ROP est un métier très délicat: votre tâche est de trouver la séquence d'instructions disponible dans GLIBC ou le LIB lié par le processus injecté. Le principe est juste de vous faire comprendre les mots, les paragraphes et les chapitres, et le réel hors contexte est un art.

Les systèmes actuels sont dotés d'une protection ASLR (Address Space layout randomization). Par conséquent, même si vous réussissez à pirater un programme, le code de piratage n'est pas réutilisable. Vous devez faire le même travail artistique à chaque fois. Lorsque ce travail se poursuit Après la répétition, ce n'est plus un art, et l'implémenteur dégénère immédiatement en un ouvrier de la chaîne de production.

Pas le gérant.


Les chaussures en cuir à Wenzhou, Zhejiang sont mouillées, donc elles ne grossiront pas sous la pluie.

Je suppose que tu aimes

Origine blog.csdn.net/dog250/article/details/108633034
conseillé
Classement