Read-only Speicherbereich
-
Ich benutze GCC 4.5.2 unter Windows 7, um C- und C++-Quelltext zu übersetzen.
Nun ist es so, dass der gesamte Speicherbereich eines hervorgehenden Programmes lesbar und ausführbar zu sein scheint - siehe Codebeispiel - der Teil aber, in dem die Funktionen bei Programmstart abgelegt werden, nicht beschreibbar ist.Lässt sich mit einem Parameter oder sonstiger Einstellmöglichkeit GCC dazu verleiten, Programme so zu übersetzen, dass diese keinen geschützten Speicher anfordern?
Gibt es einen Compiler für Windows, der es ermöglicht, dass solch schreibgeschützter Speicher nicht von einem Programm angefordert wird?#include <cstring> #include <iostream> using namespace std; char * fA() { return (char *)"Text."; } void dummy() {} int main() { cout << fA() << endl; unsigned int differenz = (unsigned int)&dummy - (unsigned int)&fA; unsigned char stack[differenz]; unsigned char * heap = new unsigned char[differenz]; unsigned char * funktion = (unsigned char *)&fA; for(unsigned int z = 0; z < differenz; z++) { stack[z] = funktion[z]; } memcpy((void *)heap, (void *)&fA, differenz); char * (* zeiger)() = (char * (*)())&stack; cout << (*zeiger)() << endl; zeiger == &fA ? cout << "Stack nicht gut" : cout << "Stack gut"; cout << endl; zeiger = (char * (*)())heap; cout << (*zeiger)() << endl; zeiger == &fA ? cout << "Heap nicht gut" : cout << "Heap gut"; cout << endl; }
-
Für VC++ siehe hier:
http://blogs.msdn.com/b/vcblog/archive/2009/05/21/dynamicbase-and-nxcompat.aspxBeachte aber auch, dass man dies in Windows systemweit deaktivieren kann, egal was Du in Deiner EXE definiert hast...
-
LordZsar1 schrieb:
// ... char * fA() {} int main() { //... unsigned char * funktion = (unsigned char *)&fA;
Es wär auch möglich, dass gcc das einfach komplett wegoptimiert. Einen Funktionszeiger nach unsigned char* zu casten ist nämlich undefined behavior in C und C++. Das heißt der Compiler muss hier nicht den Code generieren, den du erwartest. Das würde ich prüfen.
-
Ich habe mich wohl etwas unglücklich ausgedrückt - Code außerhalb des Codesegments auszuführen funktioniert gut, aber in dasselbe hineinzuschreiben, das ist das Problem:
char * fA() { cout << "Bla." << endl; } char * fB() { cout << "Mmh." << endl; } int main() { fA = fB; // lässt sich nicht einmal kompilieren - würd's funktionieren, // wär's aber trotzdem sinnlos; dient nur zur Veranschaulichung // oder unsigned int differenz = (unsigned int)&fB - (unsigned int)&fA; unsigned char * funktionA = (unsigned char *)&fA; unsigned char * funktionB = (unsigned char *)&fB; for(unsigned int z = 0; z < differenz; z++) { funktionA[z] = funktionB[z]; } // soll fB() aufrufen fA(); }
Stürzt ab. "APPCRASH". Unter Windows XP hieße das sicherlich "Zugriffsverletzung", weil auch sonst nichts in den betroffenen Speicherbereich geschrieben werden kann. Das kann aber nicht am Speicher selbst liegen, muss also künstlich bereitgestelltes "Feature" sein. Ich will's loswerden.
@Jochen:
Mmh, kann nicht ersehen, wie mir die beiden dortgenannten Techniken weiterhelfen sollen. Addressrandomisierung geschieht beim GCC standardmäßig (zumindest insofern, dass Verweise auf den Heap nicht fortlaufend angelegt werden, sondern große Lücken aufweisen - die Position pro Programmstart tendiert dazu, konstant zu bleiben oder zwischen zwei Werten zu wechseln) und Heap oder Stack für Ausführungen zu sperren sorgt sicherlich nicht dafür, dass der Codebereich plötzlich beschreibbar wird.
Vielleicht sehe ich auch nur den Wald vor lauter Bäumen nicht, könntest du bitte noch einmal detaillierter Verweisen?@Christoph:
Ich prüfe, ob die Zeiger, die ich ausführe, auf denselben Speicherbereich verweisen wie der Funktionszeiger, von dem ich kopiert habe - das ist nicht der Fall. ... Genügt das nicht, um sicher zu sein?
Und wie genau schreibe ich denn in C wohldefiniert "gib den Speicherbereich ab diesem Zeiger in 8-Bit-Blöcken aus"? Typecasting zu char * (und unsigned, damit beim casten zu int für cout kein Unfug geschieht) erschien mir als naheliegendste Lösung, aber wenn das keine gute Idee ist, will ich's natürlich besser machen...
-
LordZsar1 schrieb:
Ich prüfe, ob die Zeiger, die ich ausführe, auf denselben Speicherbereich verweisen wie der Funktionszeiger, von dem ich kopiert habe - das ist nicht der Fall. ... Genügt das nicht, um sicher zu sein?
Und wie genau schreibe ich denn in C wohldefiniert "gib den Speicherbereich ab diesem Zeiger in 8-Bit-Blöcken aus"?Soweit ich weiß ist es unmöglich ein wohldefiniertes C-Programm zu schreiben, dass das macht.
Das Problem ist, dass Funktionszeiger und Datenzeiger zwei völlig inkompatible Dinge sind in C und C++. Die kann man soweit ich weiß nicht wohldefiniert ineinander casten, egal wie man es auch versucht. Das schließt natürlich nicht aus, dass es mit gängigen Compilern trotzdem klappt.
Mit "das würde ich prüfen" meinte ich zum Beispiel, dass du mal g++ mit dem Schalter -S aufrufst und schaust, ob der Assembler-Code ungefähr das ist, was du erwartest.