Forum: PC-Programmierung Gibt es CPUs, die den alten Speicherinhalt auf den der Stapelzeiger zeigte, selbstständig löschen?


von Nano (Gast)


Lesenswert?

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?

von Georg (Gast)


Lesenswert?

Nano schrieb:
> die alten Daten werden bei vielen CPUs nicht
> gelöscht, sondern bleiben erhalten

Ich wette eine Tüte Haribo: bei allen.

Georg

von Nano (Gast)


Lesenswert?

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.

von DPA (Gast)


Lesenswert?

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.

von Εrnst B. (ernst)


Lesenswert?

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"?

von Εrnst B. (ernst)


Lesenswert?

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
void tuwas() {
2
  uint32_t vielRam[10000000];
3
...
4
  return;
5
}
6
7
void main() {
8
  tuwas();
9
  tuwas();
10
}
Der zweite "tuwas"-Aufruf müsste dann blockiert werden, bis der 
Speicher-Aufräumer fertig ist.

von Nano (Gast)


Lesenswert?

Ε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.

von Nano (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Udo S. (urschmitt)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

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)

von rbx (Gast)


Lesenswert?

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.

von Teo D. (teoderix)


Lesenswert?

Ε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.

von DPA (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Nano (Gast)


Lesenswert?

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.

von Nano (Gast)


Lesenswert?

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.

von Michael B. (laberkopp)


Lesenswert?

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
void func(void)
2
{
3
  char buffer[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.

von Nano (Gast)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

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.

von VD (Gast)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

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.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Nö, nicht nach dem erfolgreichen Rücksprung.

von Nop (Gast)


Lesenswert?

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?

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

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.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

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...

von Nop (Gast)


Lesenswert?

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_Programming
https://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.

von Nop (Gast)


Lesenswert?

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.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

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.

von Nop (Gast)


Lesenswert?

🐧 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.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

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
int main(){
2
  int(*print)(const char*) = 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.

von Nop (Gast)


Lesenswert?

🐧 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.

von Gästle (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

Gästle schrieb:
> Was könnte ich damit anfangen?

ASLR cracken, d.h. rausfinden, wo eine lib liegt.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

🐧 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.

: Bearbeitet durch User
von Gästle (Gast)


Lesenswert?

(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.

von (prx) A. K. (prx)


Lesenswert?

🐧 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.

von Patrick L. (Firma: S-C-I DATA GbR) (pali64)


Lesenswert?

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 :-)

: Bearbeitet durch User
von Martin S. (strubi)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.