blog · git · desktop · images · contact
2011-04-30
Noch nie wirklich ausprobiert. Jetzt nachgeholt!
/* basic io */
#include <stdio.h>
/* mmap */
#include <sys/mman.h>
/* bcopy */
#include <strings.h>
/*
* Diese Funktion wird später in einen Speicherbereich kopiert, der
* mittels mmap() angefordert wurde. Dabei kann man angeben, ob der
* Speicher ausführbar sein soll oder nicht.
*
* Ist die Funktion ausführbar, dann hat das NX-Bit nicht funktioniert.
*/
void test(unsigned char *where)
{
where[0] = 'J';
where[1] = 'A';
where[2] = '.';
}
/*
* Diese Funktion muss direkt nach test() kommen. Ich verlasse mich
* darauf, dass sie später im Speicher auch direkt hinter test()
* erscheint. Damit lässt sich die tatsächliche Code-Größe von test()
* messen.
*/
void dummy()
{
}
int main(int argc, char *argv[])
{
void *pTest, *pDummy;
int szTest;
void *testArea = NULL;
void (*fptr)(unsigned char *);
unsigned char writeHere[] = "000";
/* Pointer auf die zwei Funktionen und Größe messen. */
pTest = test;
pDummy = dummy;
szTest = pDummy - pTest;
printf("Adresse von test: %p\n", pTest);
printf("Adresse von dummy: %p\n", pDummy);
printf("Größe von test: %d\n", szTest);
/* Speicher anfordern, der *KEIN* PROT_EXEC hat. */
testArea = mmap(0, szTest, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (testArea == MAP_FAILED)
{
perror("mmap fehlgeschlagen");
return 1;
}
printf("Pointer auf Testbereich: %p\n", testArea);
/* Kopiere den Code der Funktion in den neuen Speicher. */
bcopy(pTest, testArea, szTest);
/* Belege den Funktionspointer mit der neuen Adresse und springe
* dann dorthin. */
printf("Inhalt: '%s'\nSpringe in Testbereich.\n", writeHere);
fptr = (void (*)(unsigned char *))testArea;
fptr(writeHere);
/* Hat es geklappt? Wie sieht der Speicher jetzt aus? */
printf("Zurück.\nInhalt: '%s'\n", writeHere);
return 0;
}
Schaut man sich das Speicherlayout zur Laufzeit an, dann hat der angeforderte Bereich keine Ausführungsrechte:
Pointer auf Testbereich: 0xb78dc000
...
$ grep '^b78dc' /proc/7370/maps
b78dc000-b78df000 rw-p 00000000 00:00 0
Trotzdem funktioniert ohne NX-Bit (also zum Beispiel unter i686 ohne
PAE-Kernel) der Sprung, er führt die Funktion aus und kehrt normal
zurück. Mit x86_64 hingegen funktioniert das Beispiel nur, wenn man
zusätzlich PROT_EXEC
angibt.