Gibt es CPUs, die den alten Speicherinhalt, auf den der Stapelzeiger
(Stackpointer) zeigte, selbstständig per Hardware löschen?
Der Stack wächst bei den meisten CPUs von oben nach unten im RAM und ein
Register, der Stapelzeiger, zeigt auf die letzte Stelle. In dem Fall
also die unterste Stelle des Stacks im RAM.
Wenn dann ein POP oder RET oder vergleichbarer Befehl kommt, wird der
Stapelzeiger entsprechend verändert. Zeigt also in dem Fall ein paar
Stellen weiter oben an die letzte Stelle des Stacks.
Das Problem dabei ist nur, die alten Daten werden bei vielen CPUs nicht
gelöscht, sondern bleiben erhalten.
Hier wäre es doch aus Sicherheitsgründen naheliegend, wenn der
Maschinenbefehl wie POR, RET usw. nicht nur den Stackpointer
aktualisieren würde, sondern auch gleich noch die alten Daten, auf die
der Stack nun nicht mehr zeigt, gleich noch nullt bevor er verändert
wird.
Gibt es CPUs bzw. µC die das automatisch per Hardware machen?
Georg schrieb:> Nano schrieb:>> die alten Daten werden bei vielen CPUs nicht>> gelöscht, sondern bleiben erhalten>> Ich wette eine Tüte Haribo: bei allen.>> Georg
Wetten tue ich nicht, aber was spräche denn dagegen eine CPU zu
entwerfen, die aus Sicherheitsgründen den Stack automatisch aufräumt?
Das wäre ja durchaus nebenläufig bzw. gleichzeitig während die CPU den
Stapelzeiger oder andere Befehle ausführt, solange sie nicht selbst auf
das RAM zugreift, möglich.
Nur dann, wenn sie auf das RAM zugreift, müsste das RAM für diese
blockiert sein, bis diese nebenläufige Aufräumarbeit erledigt ist.
Ja, das kostet dann etwas Geschwindigkeit, aber erhöht dafür die
Sicherheit.
Sollte das RAM des Stacks wie ein Cache auf dem Prozessor Die enthalten
sein, wäre aber auch das optimierbar. Mit dem Haken, dass der Stack dann
eine maximale Größe hätte, die nicht überschritten werden könnte.
Bei WASM gibt es einen Funktionsstack, der nicht direkt mit den anderen
Instruktionen zugreifgar ist, also aus dessen Sicht nicht im Speicher
des Programms sichtbar ist. Das ist aus gründen der JS Interoperation.
Beim Laden des Programms stehen die Einsprungspunkte fest. Man hat dann
quasi handle, statt Adressen, die man anspringt (der ist einfach ein
Index in eine Tabelle, glaub ich). Aber direkt kommt man an den nicht
ran. Er existiert aus Programmsicht quasi gar nicht.
DPA schrieb:> Bei WASM gibt es einen Funktionsstack,
Gibt's das nicht auch bei PIC10/PIC12? Hardware-Stack nur für die
Funktions-Rücksprungsadressen, mit ganz wenig erlaubter
Verschachtelungstiefe?
"Rekursiv geht schief"?
Nano schrieb:> Nur dann, wenn sie auf das RAM zugreift, müsste das RAM für diese> blockiert sein, bis diese nebenläufige Aufräumarbeit erledigt ist.
das ist eigentlich dann immer der fall, da kann man sich die
Nebenläufigkeit auch sparen.
1
voidtuwas(){
2
uint32_tvielRam[10000000];
3
...
4
return;
5
}
6
7
voidmain(){
8
tuwas();
9
tuwas();
10
}
Der zweite "tuwas"-Aufruf müsste dann blockiert werden, bis der
Speicher-Aufräumer fertig ist.
Εrnst B. schrieb:> Der zweite "tuwas"-Aufruf müsste dann blockiert werden, bis der> Speicher-Aufräumer fertig ist.
Ja, bei solchen großen lokalen Arrays, die auf den Stack kommen wäre das
dann so. Aber es gibt ja auch Funktionen, wo man nur ein paar wenige
Werte auf dem Stack sichern muss und die nächsten Befehle nach dem
Rücksprung ins Hauptprogramm arbeitet vielleicht erstmal nur mit den
Registern, damit hätte man also Zeit für den nebenläufigen
Stackaufräumer, die dieser dann nutzen kann.
DPA schrieb:> Bei WASM gibt es einen Funktionsstack, der nicht direkt mit den> anderen> Instruktionen zugreifgar ist, also aus dessen Sicht nicht im Speicher> des Programms sichtbar ist. Das ist aus gründen der JS Interoperation.> Beim Laden des Programms stehen die Einsprungspunkte fest. Man hat dann> quasi handle, statt Adressen, die man anspringt (der ist einfach ein> Index in eine Tabelle, glaub ich). Aber direkt kommt man an den nicht> ran. Er existiert aus Programmsicht quasi gar nicht.
Gut, du meinst mit WASM wohl WebAssembly. Ja, das ist dann eine
Softwaregeschichte. Da kann man so etwas leicht umsetzen und macht bei
Browsern auch Sinn.
Meine Frage bezieht sich aber auf echte Hardware.
Nano schrieb:> damit hätte man also Zeit für den nebenläufigen> Stackaufräumer, die dieser dann nutzen kann.
Out Of Order CPUs wickeln das auch mit normalen Stores nebenläufig ab,
so lange der Store Buffer nicht absäuft.
Nano schrieb:> Ja, das kostet dann etwas Geschwindigkeit, aber erhöht dafür die> Sicherheit.
Für was erhöht das die Sicherheit? Nur für die 0,01% des Codes der sich
mit Security beschäftigt.
Genauso gut kann man in dem Fall als sicher geltende Open Source
Methoden benutzen, die die benutzten kritischen Daten selbst löschen.
Besonders interessant wäre das allerdings mit entsprechend Tags im
Speicher, so dass es beim Zugriff auf ungültigen oder falsch genutzen
Speicher scheppert, statt 0 zu liefern. Sowas in der Art gab es vor >50
Jahren als "Typenkennung" bei der TR440.
Nano schrieb:> PUs bzw. µC die das automatisch per Hardware machen?
Was jetzt "Spielzeug µC" oder "große Applikation CPU"?
Bei ausgewachsenen CPU gibt es sowas wie am ende des RAM gar nicht mehr.
Da regelt das unter anderem die MMU für jeden Prozess einzeln. Da hat
jeder für sich ein eigenes virtuelles "Ende des Speichers" und keiner
weiß wo das im physikalischen RAM tatsächlich liegt usw. Noch nichtmal
ob es überhaupt RAM ist oder gar irgendwo ausgelagert ist. Da wird schon
einiges unternommen um zu verhindern das fremde Prozesse auf andere
Daten zugreifen können...
- z.B.: https://en.wikipedia.org/wiki/Address_space_layout_randomization
In der Vergangenheit gab es aber auch schon Fehler bei dem sich das
ganze umgehen lassen hat. Z.B.:
- https://de.wikipedia.org/wiki/Meltdown_(Sicherheitsl%C3%BCcke)
- https://de.wikipedia.org/wiki/Spectre_(Sicherheitsl%C3%BCcke)
Nano schrieb:> Das Problem dabei ist nur, die alten Daten werden bei vielen CPUs nicht> gelöscht, sondern bleiben erhalten.
Aber nur wenn der Stackpointer, bzw. sein Ersatz viel inkrementiert. Das
macht normalerweise keiner, sondern die Programmiersprachen übertreiben
es da ein wenig.
Parameterübergabe oder Zählhilfen kann man anderes organisieren, und
braucht auch keinen Stack, sondern nur Speicher an sich.
Hardwarebeschleunigte Parameterübergabe, oder eben effektiveres
Funktionenhandling bei funktionalen Sprachen wäre nicht so verkehrt.
Habe allerdings bei diesen Haskell-Tutorials noch nicht so viel zum
Thema Alignement finden können. Dass normale Zahlen, und Hexzahlen
durchaus unterschiedlich sind, verstehen auch nur wenige.
Aktuell müsste man mal die RISC-Experten fragen, was es diesbezüglich an
neueren Entwicklungen auf der Hardware-Developer-Ebene gibt.
Früher, in den 60er Jahren bzw. etwas später gab es schon "Lisp"
Maschinen.
https://de.wikipedia.org/wiki/Lisp-Maschine.
Angebahnt hatte sich aber schon ein Kompatibilitätsproblem, obwohl
später auf vielen Kisten Basic verfügbar war - und Basic so eine eigene
Kompatibilitätsebene hatte.
Εrnst B. schrieb:> DPA schrieb:>> Bei WASM gibt es einen Funktionsstack,>> Gibt's das nicht auch bei PIC10/PIC12? Hardware-Stack nur für die> Funktions-Rücksprungsadressen, mit ganz wenig erlaubter> Verschachtelungstiefe?
Jo, der olle 16F84 hatte nur zwei, die 10er haben min. acht. Da aber eh
kaum Ressourcen vorhanden, stört das nich.
Nano schrieb:> Gut, du meinst mit WASM wohl WebAssembly. Ja, das ist dann eine> Softwaregeschichte. Da kann man so etwas leicht umsetzen und macht bei> Browsern auch Sinn.> Meine Frage bezieht sich aber auf echte Hardware.
Tomato potato: https://github.com/piranna/wasmachine
rbx schrieb:> Hardwarebeschleunigte Parameterübergabe, oder eben effektiveres> Funktionenhandling bei funktionalen Sprachen wäre nicht so verkehrt.
Auf eine ähnliche Idee kam DEC bei der VAX. Der entsprechend
umständliche "General Call" Befehl war so langsam, dass kluge
Programmierer ihn mieden und Aufrufe über den einfacheren "Branch
Subroutine" Befehl abwickelten. Diese Erkenntnis führte mit zur
Entwicklung von RISC.
rbx schrieb:> Nano schrieb:>> Das Problem dabei ist nur, die alten Daten werden bei vielen CPUs nicht>> gelöscht, sondern bleiben erhalten.>> Aber nur wenn der Stackpointer, bzw. sein Ersatz viel inkrementiert. Das> macht normalerweise keiner, sondern die Programmiersprachen übertreiben> es da ein wenig.
Es gibt ja noch die Adressen von Unterprogrammen die auf dem Stack
landen.
Die könnte man erneut wieder aufrufen, wenn der Stack nicht aufgeräumt
wird.
Irgend W. schrieb:> Was jetzt "Spielzeug µC" oder "große Applikation CPU"?>> Bei ausgewachsenen CPU gibt es sowas wie am ende des RAM gar nicht mehr.
Ich wollte die Frage jetzt nicht eingrenzen und das einfach mal offen
stehen lassen.
Vor ein paar Jahren wäre der 8086 ne ausgewachsene CPU gewesen und der
legt den Stack auf das RAM ohne MMU.
Nano schrieb:> Gibt es CPUs, die den alten Speicherinhalt, auf den der Stapelzeiger> (Stackpointer) zeigte, selbstständig per Hardware löschen?
Nein.
Bei CPU die nur PUSH/POP/CALL/RET können um auf den Stack zuzugreifen,
gibt es kein Problem (PIC etc.). Die leiden nur, wenn man PUSH 5 und
dann RET macht.
Wenn man aber Stack reservieren kann durch ADD SP,n oder modifizieren
kann durch LD SP,Rx, dann gibt es ein Problem:
Woher weiss ich, daß der übersprungene Speicherbereich wirklich Stack
sein soll ? Es kann auch einfach zur Neu-Einrichtung eines zweiten
Stacks (weiterer Prozess) gemacht werden und man weiss gar nicht, wo der
Anfang des Stacks ist.
Aber Hochsprachen können leere Speicherbereiche löschen, z.B.
1
voidfunc(void)
2
{
3
charbuffer[128];
4
}
was normalerweise in ADD SP,128 kompiliert wird, kann diese 128 bytes
auf 0 setzen, der ARM64 enthält dafür sogar Instruktionen wie STP und
STR.
Denn der Compiler weiss, ob er den Stack überschreiben darf.
Zweite Methode: Segmentierte Prozessoren können Speicherzugriffe
unterhalb von top of stack als trap abbrechen, damit wird zwar nicht der
Speicher gelöscht, aber Zugriffe auf ehemalige buffer werden
unterbunden. So lange das korrekte Programm also nur mit
PUSH/POP/CALL/RET arbeitet liegen auf dem Stack nie alte Daten.
Diese Prozessoren wissen auch, wo der Stack beginnt, könnten also auch
mit 0 auffüllen wenn der stack pointer modifiziert wird (tun es aber
nicht, weil der Performanceverlust dann wieder dumme Kritiker auf den
Plan ruft).
Bei segmentierten CPU ist das Anlegen eines neuen Stacks für einen
weiteren Prozess auch eine für Anwenderprogramme verbotene Operation,
darf also nur das Betriebssystem machen.
Aber es gibt ja einige Verfechter, die sagen, segmentierte Prozessoren
wären doof.
Michael B. schrieb:> Nano schrieb:>> Gibt es CPUs, die den alten Speicherinhalt, auf den der Stapelzeiger>> (Stackpointer) zeigte, selbstständig per Hardware löschen?>> Nein.>> ...> Denn der Compiler weiss, ob er den Stack überschreiben darf.>> Zweite Methode: Segmentierte Prozessoren ...
Danke für deine Antwort. Die genannten Punkte machen Sinn. Du hast
recht, ich habe das bisher nur unter dem Gesichtspunkt einer einfachen
CPU, die halt nur PUSH/POP/CALL/RET nutzt/kann, betrachtet.
Nano schrieb:> Hier wäre es doch aus Sicherheitsgründen naheliegend, wenn der> Maschinenbefehl wie POR, RET usw. nicht nur den Stackpointer> aktualisieren würde, sondern auch gleich noch die alten Daten, auf die> der Stack nun nicht mehr zeigt, gleich noch nullt bevor er verändert> wird.
Das wäre überhaupt nicht naheliegend, und ich sehe auch keinen
Sicherheitsvorteil. Bei Buffer Overflow mit Manipulation der
Rücksprungadresse wird das ja vor dem RET gemacht und nicht etwa danach.
Die angesprungene Funktion könnte die für sie bestimmten Daten auf dem
Stack selbst löschen bevor sie den Return ausführt. Dann bleibt auf dem
Stack nur die Rücksprungadresse übrig und damit kann man nun nicht so
besonders viel anfangen, weil die im eigenen Programm liegt.
Nano schrieb:> Hier wäre es doch aus Sicherheitsgründen naheliegend, wenn der> Maschinenbefehl wie POR, RET usw. nicht nur den Stackpointer> aktualisieren würde,
Welche Sicherheitsgründen? Welchen Inhalt? Worin besteht das Problem?
Dazu noch, welcher fremde Prozess lamm auf diese Faten zugreifen? Ein
Problem suchen, wo es keins gibt.
Ben B. schrieb:> und damit kann man nun nicht so> besonders viel anfangen, weil die im eigenen Programm liegt.
Damit kann man sehr viel anfangen, so funktionieren viele Exploits.
Ben B. schrieb:> Nö, nicht nach dem erfolgreichen Rücksprung.
Ich zitiere Dich mal:
Ben B. schrieb:> bevor sie den Return ausführt.
Merkste selber, ne?
Ich verstehe nicht was ihr wollt. Dreht ihr mir die Worte um, oder was?
Solange die zu sichernde Funktion oder was immer man da sicher haben
will, läuft, kann die sowieso jeden Scheiß mit dem PC machen. Und wenn
sie ihre Spuren auf dem Stack für welchen Sicherheitsgewinn auch immer
vewischen möchte, dann kann sie den von ihr verwendeten Stack-Speicher
problemlos bis auf die Rücksprungadresse selbst löschen. Und wenn diese
hier angenommene Funktion eine angreifende Funktion ist, dann hat der
Angreifen sein Ziel - den Start der Funktion - zu diesem Zeitpunkt
bereits erreicht.
Ich kann nicht verhindern, daß eine z.B. via call oder Interrupt
aufgerufene Funkion weiß, woher sie angesprungen wurde. Wobei, bei
verketteten Aufrufen evtl. schon, aber sie muss ja wissen, wo sie beim
Beenden mittels return wieder hinspringen soll.
Und wenn per NMI oder so aus einer zu sichernden Funktion
herausgesprungen wird, kann man nichts auf dem Stack löschen weil man
gar nicht weiß, welche Daten, die dort liegen, noch gebraucht werden. Da
würde sich das Programm oder die Funktion schwer wundern, wenn nach dem
NMI return der Stack leer ist.
Oder anders: Beschreibt doch mal ein konkretes Angriffsschema, daß sich
durch Löschen des Stacks verhindern lässt.
Damit ich einen Nutzen aus übriggebliebenen Daten auf dem Stack ziehen
kann, muss mein bereits laufendes Programm das angreifende sein (das die
Daten haben will) und dann eine anzugreifende Funktion aufrufen. Solange
diese meinen eigenen Stackspeicher nutzt, kann ich dann schauen, welche
Daten unterhalb des Stackpointers liegengeblieben sind. Okay, so weit so
schlecht, aber pumpt dort irgend jemand sicherheitsrelevante Daten
durch, mit denen ein Angreifer was anfangen könnte? Und selbst wenn, wie
oben angeführt, könnte er diese selbst wieder löschen bevor in mein
Programm zurückgesprungen wird.
Das gleiche könnte ich machen wenn ich einfach sage hey OS, gib mir
allen zur Verfügung stehenden Speicher. Dann kann ich diesen komplett
durchsuchen ob ich da was Brauchbares finde. Wenn da irgend ein Programm
sicherheitsrelevante Daten nicht gelöscht hat bevor der Speicher wieder
freigegeben wurde, dann ist das aus meiner Sicht ein Fehler des
Programms und nicht der Hardware.
Ben B. schrieb:> Damit ich einen Nutzen aus übriggebliebenen Daten auf dem Stack ziehen> kann, muss mein bereits laufendes Programm das angreifende sein
Naja, eventuell denkt er an ROP nach einem Buffer Overflow auf dem
Stack. Aber da würde das höchstens helfen, wenn der Stack nach Oben
statt nach Unten wächst. Aber dann überschreibt man ja auch seine
Rücksprung Adresse nicht mehr...
Ben B. schrieb:> Oder anders: Beschreibt doch mal ein konkretes Angriffsschema, daß sich> durch Löschen des Stacks verhindern lässt.
Eben, weil das Entscheidende nämlich nicht gelöscht werden kann, und
zwar die Rücksprungadresse VOR dem Rücksprung. Aber Du sagtest, damit
könne man nicht viel anfangen, und das ist falsch. Damit kann man so
ziemlich alles anfangen:
Selbst wenn man "nur" Code anspringt, der bereits im eigenen Programm
vorhanden ist, kann man damit z.B. libc-Fragmente beliebig verketten und
effektiv ein neues Programm erzeugen, das das tut, was der Angreifer
will.
https://de.wikipedia.org/wiki/Return_Oriented_Programminghttps://de.wikipedia.org/wiki/Return_into_libc> Das gleiche könnte ich machen wenn ich einfach sage hey OS, gib mir> allen zur Verfügung stehenden Speicher. Dann kann ich diesen komplett> durchsuchen ob ich da was Brauchbares finde.
Wenn Du mit malloc() allozierst, wird im Gegensatz zu calloc() nicht
garantiert genullt - aber die meisten OS (wie Linux oder Windows) wird
Dir aus Sicherheitsgründen Speicher mit ungenulltem Inhalt nur dann
liefern, wenn dieser vorher von Deinem eigenen Prozeß benutzt und wieder
freigegeben wurde.
Ach ja, und eine wirksame und recht einfache Abhilfe dagegen wäre nicht
das Löschen des Stacks, sondern unabhängige Stacks für Daten und
Return-Adressen. Also so, daß man mit Schreibzugriffen auf den
Daten-Stack den Return-Stack gar nicht erreichen kann.
Das Problem daran wäre aber, daß das ein komplett anderes ABI wäre, d.h.
existierende Software wäre zu diesem Mechanismus inkompatibel.
Nop schrieb:> Wenn Du mit malloc() allozierst, wird im Gegensatz zu calloc() nicht> garantiert genullt - aber die meisten OS (wie Linux oder Windows) wird> Dir aus Sicherheitsgründen Speicher mit ungenulltem Inhalt nur dann> liefern, wenn dieser vorher von Deinem eigenen Prozeß benutzt und wieder> freigegeben wurde.
malloc ist Sache der libc implementierung. Vom OS kann man sich (unter
linux/unix systemen) unter anderem Speicher per mmap holen (ohne file
(MAP_ANONYMOUS)). Die Pages sind dann genullt. Die libc hat Strategien,
effizient Speicher zu verwalten. Also kleinere Allokationen sinvoll auf
diese Pages zu verteilen, die es sich holt. Es wird also nicht bei jedem
malloc Speicher vom OS geholt. Context switches sind teuer, und werden
von Programmen wenn möglich vermieden. Am ende muss also die libc
schauen & entscheiden, ob es den Speicher auch wieder irgendwo nullt,
beim malloc oder free.
Aber ja, die meisten sollten das heutzutage nullen.
🐧 DPA 🐧 schrieb:> Aber ja, die meisten sollten das heutzutage nullen.
Ja, genau - was ich meinte: bei malloc wird oft genullt, aber es ist
nicht garantiert. Im Falle, daß nicht genullt wird, bekommt man aber
unter OS wie Linux oder Windows nur eigene alte Daten zurück.
Nop schrieb:> Ach ja, und eine wirksame und recht einfache Abhilfe dagegen wäre nicht> das Löschen des Stacks, sondern unabhängige Stacks für Daten und> Return-Adressen.
100% reicht das auch noch nicht. Ich kann ja in C auch so Zeug machen:
1
intmain(){
2
int(*print)(constchar*)=puts;
3
print("Hello World");
4
}
Man denke auch an callbacks und so, die müsste man ja auch schützen. Ich
denke, konkreter bräuchte man einen separaten Stack für alle Buffer, die
in irgend einer weise referenziert werden, und der Compiler nicht sicher
stellen/wissen kann, dass da immer alles in bounds ist.
🐧 DPA 🐧 schrieb:> 100% reicht das auch noch nicht.
Autsch, guter Punkt, an Funktionszeiger hatte ich gar nicht gedacht.
Andererseits wäre die Exploit-Nützlichkeit davon eingeschränkt, weil man
damit keine beliebige Verkettung hinbekommt, wenn man den Return-Stack
nicht mit Datenzugriffen manipulieren kann.
Man könnte allerdings existierende Funktionalität aufrufen, die in dem
Zustand nicht vorgesehen ist. Beispielsweise unter Umgehung von "Wollen
Sie wirklich XYZ machen?" direkt zur "Ja, will ich"-Stelle springen.
Nano schrieb:> Hier wäre es doch aus Sicherheitsgründen naheliegend, wenn der> Maschinenbefehl wie POR, RET usw. nicht nur den Stackpointer> aktualisieren würde, sondern auch gleich noch die alten Daten, auf die> der Stack nun nicht mehr zeigt, gleich noch nullt bevor er verändert> wird.
Hab jetzt die Antworten nur überflogen und meine Frage ist bereits
beantwortet worden. Aber wie sähe denn ein Angriffsvektor aus, der auf
alte/ungültige Werte des Stacks zugreift? Man müsste dazu ja noch
wissen, was diese Stackwerte denn gerade in diesem Moment bedeuten. Und
selbst dann: Was könnte ich damit anfangen? Manipulieren ist ja sinnlos,
da ohnehin nicht mehr gültig.
Viel interessanter ist doch Zugriff auf den gerade wirklich verwendeten
Stack zu haben und dort möglichst eine Rücksprungadresse zu manipulieren
um fremden Code auszuführen
Gibt es eigentlich CPUs, mit eingebautem bound checking? Also wo man
quasi erst sagt, "Speicher von da bis da, das ist jetzt objekt 1". und
danach "Speichere X in Objekt Y an offset Z". Dann könnte man die bound
checks in HW im Hintergrund ablaufen lassen, und es wahre viel
schwieriger, versehentlich irgend etwas ungewollt zu überschreiben. Und
mit C wäre sowas ja auch kompatibel, da da Pointerarithmetik ja nur
innerhalb des selben Objekt erlaubt ist.
🐧 DPA 🐧 schrieb:> Gibt es eigentlich CPUs, mit eingebautem bound checking? Also wo man> quasi erst sagt, "Speicher von da bis da, das ist jetzt objekt 1". und> danach "Speichere X in Objekt Y an offset Z".
Intels x86 16/32-Bit Segmentierung ab 286. In AMD64 abgeschafft. Ein
Grundproblem war die Entkopplung von Segment und Offset über getrennt
verwaltete Register.
In Zilogs Z8000 war der Segment-Selektor Teil des Pointers, was der
Sache deutlich näher kommt. Allerdings kommt man mit 128 Selektoren und
einer Längenauflösung auf 256 Bytes nicht sehr weit, wenn man das auf
Objekte beziehen will, statt auf grössere Speicherbereiche.
IIRC basierte Intels iAPX432 darauf.
(prx) A. K. schrieb:> Gästle schrieb:>> Was könnte ich damit anfangen?>> ASLR cracken, d.h. rausfinden, wo eine lib liegt.
Wie würde das aussehen. Erstmal muss ich ja herausfinden, was die nicht
mehr gültigen Werte auf dem Stack überhaupt bedeuten. Sind ja nicht nur
Pointer. Und wenn ich dann der Meinung bin einen Pointer gefunden zu
haben weiß ich ja immer noch nicht, ob dass eine spezielle gesuchte Lib
ist oder nicht zumal ja die Adressen auf dem Stack in der Regel
Rücksprungadressen sind, also keine Pointer auf Funktionen.
Könntest du oder jemand anderes etwas näher erklären, wie ich hier einen
sinnvollen Angriff starten kann? Ich weiß ja in der Regel nichtmal
welche Funktion denn die letzten nicht mehr gültiegen Werte auf dem
Stack geschrieben hat.
🐧 DPA 🐧 schrieb:> Dann könnte man die bound> checks in HW im Hintergrund ablaufen lassen
Betrachtet man die Performance, entsteht ein anderes Problem im
Vordergrund. Die Addition der Basisadresse ist Teil der Adressrechnung,
noch bevor der Zugriff auf den L1 Cache irgendwie in Angriff genommen
werden kann. Bei segmentierten x86 kam dabei insgesamt ein 4-Wege-Adder
raus, mit Segmentbasis + Basisregister + Indexregister + Konstante. In
der Praxis führt das meist zu einem Zusatztakt bei zu vielen solchen
Komponenten in der Adressrechnung.
Also trotz allem habe ich mal (Nicht den ganzen Thread gelesen)
Aber aus reiner Neugier, das versucht.
Es gibt von den MSP430 CPU mit integrierter LEA. Nun wenn man den Stack
in das Shared RAM (CPU und LEA) legt kann man die LEA so programmieren
dass sie den Stack nach jedem Read wieder überschreibt.
Nachteil:
Ist dann aber ohne CPU Intension einfach prinzipiell nach einem Read
gelöscht, unabhängig davon ob der CPU Stackpointer geändert wird.
Vorteil: die CPU wird nicht ausgebremst weil die LEA den Speicher
unabhängig nutzen kann, Heißt also die CPU kann im normalem Speicher
weiter werkeln während die LEA den speicher löscht. :-)
Ob das von Praktischem Nutzen ist?..Keine Ahnung, war aber mal ein
Interessantes Experiment :-)
Nano schrieb:> Gibt es CPUs bzw. µC die das automatisch per Hardware machen?
Ja, faellt unter die sog. 'Stack-Maschinen', da laesst sich das in der
HW (FPGA) implementieren, ist nicht sonderlich komplex. Muehsamer ist
es, wenn der Befehlssatz lokale Stack-Variablen zulaesst, also was von
der Art `SP.next = SP + x` existiert. Der pragmatischste Ansatz ist
aber, dedizierte Stackmemories zu nutzen, die im 'User mode' nicht
ausgelesen werden koennen, so kommt man mit den ueblichen Tricks, die
bei i86 eher zum Erfolg fuehren, an lokale Variablen (keys, etc.) nicht
ran.