Jedes Programm, das auf einem Prozessor ausgeführt wird, besteht üblicherweise aus kleinen Teilen, oft Funktionen oder Routinen genannt. Diese Funktionen verändern dabei oft die kleinen, sehr schnellen Speicher des Prozessors, die beispielsweise dazu da sind, zwischen den Funktionen Werte zu übergeben oder um dem Prozessor zu sagen, wo der nächste Befehl, auch Instruktion genannt, steht. Diese kleinen Speicher heißen Register.
Wichtige Registerinhalte, die verändert werden, müssen also am Anfang einer Routine gesichert und am Ende wiederhergestellt werden. Den Anfang nennt man auch den Prolog, das Ende den Epilog.
Diese Teile einer Subroutine beinhalten oft auch Techniken, um Sicherheitslücken zu vermeiden.
Welche Register wie gesichert werden müssen, hängt vom Betriebssystem bzw. dessen Kernel ab.
So verwenden FreeBSD, macOS, Solaris und Linux (der Konvention des sogenannten System V ABI folgend) beispielsweise die Register RDI, RSI, RDX und RCX als Übergabe für Argumente einer Funktion mit vier Ganzzahlen.
Die Microsoft x64 calling convention verwendet hier RCX, RDX, R8 und R9.
Ein typischer Prolog in der Microsoft-Konvention wäre:
mov [RSP + 8], RCX
push R15
push R14
push R13
sub RSP, 32
lea R13, 128[RSP]
Der Epilog, der die Werte wiederherstellt, sähe dann so aus:
add RSP, 32
pop R13
pop R14
pop R15
ret
Hier werden nicht nur Inhalte gesichert sondern hier wird auch eine einfache Technik angewendet, um einen sogenannten Pufferüberlauf bzw. Stapelüberlauf zu erkennen. Diese Überlaufe sind die vermutlich häufigsten Software-Sicherheitslücken.
sub rsp, 28h
call __security_init_cookie
add rsp, 28h
jmp __scrt_common_main_seh
__security_init_cookie:
mov [rsp-8+arg_18], rbx
push rbp
mov rbp, rsp
sub rsp, 20h
mov rax, cs:__security_cookie
mov rbx, 123456789012h
cmp rax, rbx
jnz short ...