Während der Laufzeit Prozeduren überschreiben, geht das?
Verfasst: 10.11.2015 19:22
Hallo,
leider komme ich letzter Zeit überwiegend mit Fragen, statt mit Lösungen und fertigen Codes daher. Das tut mir Leid, aber ich arbeite an einer Sache mit vielen, für mich neuen Themen (und ein wenig eingerostet bin ich bzgl. PB auch noch etwas). Ich hoffe dass das mal wieder anders sein wird.
Worum geht es?
Ich würde gern zur Laufzeit eines Programmes eine seiner Prozeduren mit einem Datenblock (Assemblercode) befüllen. Sinn und Zweck des ganzen soll ein Sicherheitsmechanismus sein. Der eigentliche Prozedurinhalt wird verschlüsselt in der Exe vorliegen. Die "Prozedurhülse" soll zu dem Zeitpunkt in der Exe existieren, aber eben nur Blödsinn oder NOPs enthalten. Diese Hülse soll dann zur Laufzeit mit eben jenem regulären Inhalt gefüllt werden, der da eigentlich reingehört.
Das ganze soll nicht auf dem Filesystem passieren, sondern ausschließlich im Speicher, damit die Exe zu keiner Zeit mit der regulären Prozedur auf Platte existiert.
Vom Prinzip her ist mir das alles klar, aber der Teufel steckt im Detail.
Der Plan war:
1) Es gibt auf Platte eine unmodifizierte Exe (z.B. SchütztMich.exe), die die reguläre Prozedur enthält. Und zwar so, wie sie von PB kompiliert worden ist. Außerdem enthält das Programm in der DataSection einen leeren DatenBlock, der so groß ist wie der Speicherbedarf der o.g. Prozedur
2) Der Beginn und das Ende der betreffende Prozedur bzw. der Speicheradressen zum Prozedur-Anfang und Ende muss irgendwie kenntlich gemacht werden. Z.B. durch einen String "PROCBEG" und "PROCEND"
3) Ein separates Patchprogramm lädt die SchützMich.exe in den Speicher und sucht anhand der beiden Strings die Position der zu verschlüsselnden Prozedur
4) Der Speicherblock, der die Prozedur repräsentiert, wird ausgelesen, verschlüsselt und in den leeren Datenblock in der DataSection geschrieben.
5) Der Speicherblock der soeben ausgelesenen Prozedur wird dann mit Müll gefüllt und die Schützmich.exe wieder auf Platte geschrieben (BinGeschützt.exe).
6) Die BinGeschützt.exe enzhält bereits eine entsprechende Prozedur, die zur Laufzeit den Datenblock aus der DataSection kopiert, entschlüsselt und so den mit Müll gefüllten Speicherbereich mit dem Inhalt der originalen Prozedur füllt.
Ich hoffe das war in etwa verständlich beschrieben.
Nun treten da einige Fragen und Probleme auf. Meine aktive Assemblerzeit ist nun auch schon Lichtjahre her und das war auf dem Amiga. Aber ich denke es wird ja Parallelen geben.
Abgesehen von den Dingen, die ich bisher übersehen habe oder gar nicht weiß, habe ich folgende Fragen zu dem Vorhaben:
- Ist es möglich den o.g. Kennungsstring "PROCBEG" in die Prozedur (also in die "Code Section" des Programms) einzufügen?
Mit Data.s "PROCBEG" geht es nicht, weil Data nur innerhalb einer DataSection erlaubt ist und die liegt nach dem Einlesen des Programms durch das Betriebssystem sicherlich in einem separaten Speicherbereich.
Mit Inline Assembler !dc.b "PROCBEG" geht es auch nicht, da der Assembler dann meckert "Illegal Instruction"
Mit Berechnung von Sprungmarkenadressen statt des Kennungsstrings hat es leider auch nicht funktioniert.
Die inneren werden nicht gefunden, die äußeren liefern 0.
Wie findet man also die betreffende Speicherstelle der Prozedur?
Manuell geht das schon, wenn man die Exe mit dem Disassembler "OlyDbg" untersucht und den so ermittelten Speicheroffset dann mittels Hexeditor in der Datei aufsucht. Wobei sich mir dann trotzdem die Frage stellt, ist der Offset (gemessen vom Programmanfang) noch der gleiche, wenn das Programm durch Windows in den Speicher geladen und ausgeführt wird?
- Gibt es bei Windows so etwas wie Reloc-tables?
Wenn ich mich richtig erinnere, war es beim Amiga so, dass alle lange Sprünge innerhalb eines Assemblerprogramms so ausgelegt waren, als würde das Programm ab Speicherstelle 0 beginnen. Wenn man also zu Speicherstelle 16 springen wollte, dann wurde JMP +16 (sinngemäß) codiert. Alle diese Sprünge wurden in einer Reloc-table in der "Exe" gespeichert (naja, beim Amiga hießen sie nicht Exen). Der Loader im Betriebssystem hat das Programm beim späteren Ausführen dann an irgend eine freie Speicherstelle im Speicher-Adressraum geladen - nehmen wir mal an, an die Speicherstelle 40000. Bei diesem Vorgang wurden dann alle JMP's im Programmcode mittles der Reloc-table "gepatcht". Das JMP +16 wurde mit JMP +40016 überschrieben, weil das neue Sprungziel nun an eben dieser Speicherstelle lag.
Wenn das bei Windows ähnlich ist, dann kann ich das ganze sicherlich knicken, da ich nicht weiß, ob der FASM irgendwelche relozierbaren Sprünge in meine Prozedur zaubert.
Tja, so weit gehen erstmal meine Gedanken hierzu. Ich vermute, dass das nicht so einfach sein wird.
Sollte jemand von euch dem Thema näher stehen, dann würde ich mich über hilfreiche Hinweise freuen.
Gruß Kurzer
leider komme ich letzter Zeit überwiegend mit Fragen, statt mit Lösungen und fertigen Codes daher. Das tut mir Leid, aber ich arbeite an einer Sache mit vielen, für mich neuen Themen (und ein wenig eingerostet bin ich bzgl. PB auch noch etwas). Ich hoffe dass das mal wieder anders sein wird.
Worum geht es?
Ich würde gern zur Laufzeit eines Programmes eine seiner Prozeduren mit einem Datenblock (Assemblercode) befüllen. Sinn und Zweck des ganzen soll ein Sicherheitsmechanismus sein. Der eigentliche Prozedurinhalt wird verschlüsselt in der Exe vorliegen. Die "Prozedurhülse" soll zu dem Zeitpunkt in der Exe existieren, aber eben nur Blödsinn oder NOPs enthalten. Diese Hülse soll dann zur Laufzeit mit eben jenem regulären Inhalt gefüllt werden, der da eigentlich reingehört.
Das ganze soll nicht auf dem Filesystem passieren, sondern ausschließlich im Speicher, damit die Exe zu keiner Zeit mit der regulären Prozedur auf Platte existiert.
Vom Prinzip her ist mir das alles klar, aber der Teufel steckt im Detail.
Der Plan war:
1) Es gibt auf Platte eine unmodifizierte Exe (z.B. SchütztMich.exe), die die reguläre Prozedur enthält. Und zwar so, wie sie von PB kompiliert worden ist. Außerdem enthält das Programm in der DataSection einen leeren DatenBlock, der so groß ist wie der Speicherbedarf der o.g. Prozedur
2) Der Beginn und das Ende der betreffende Prozedur bzw. der Speicheradressen zum Prozedur-Anfang und Ende muss irgendwie kenntlich gemacht werden. Z.B. durch einen String "PROCBEG" und "PROCEND"
3) Ein separates Patchprogramm lädt die SchützMich.exe in den Speicher und sucht anhand der beiden Strings die Position der zu verschlüsselnden Prozedur
4) Der Speicherblock, der die Prozedur repräsentiert, wird ausgelesen, verschlüsselt und in den leeren Datenblock in der DataSection geschrieben.
5) Der Speicherblock der soeben ausgelesenen Prozedur wird dann mit Müll gefüllt und die Schützmich.exe wieder auf Platte geschrieben (BinGeschützt.exe).
6) Die BinGeschützt.exe enzhält bereits eine entsprechende Prozedur, die zur Laufzeit den Datenblock aus der DataSection kopiert, entschlüsselt und so den mit Müll gefüllten Speicherbereich mit dem Inhalt der originalen Prozedur füllt.
Ich hoffe das war in etwa verständlich beschrieben.
Nun treten da einige Fragen und Probleme auf. Meine aktive Assemblerzeit ist nun auch schon Lichtjahre her und das war auf dem Amiga. Aber ich denke es wird ja Parallelen geben.
Abgesehen von den Dingen, die ich bisher übersehen habe oder gar nicht weiß, habe ich folgende Fragen zu dem Vorhaben:
- Ist es möglich den o.g. Kennungsstring "PROCBEG" in die Prozedur (also in die "Code Section" des Programms) einzufügen?
Mit Data.s "PROCBEG" geht es nicht, weil Data nur innerhalb einer DataSection erlaubt ist und die liegt nach dem Einlesen des Programms durch das Betriebssystem sicherlich in einem separaten Speicherbereich.
Mit Inline Assembler !dc.b "PROCBEG" geht es auch nicht, da der Assembler dann meckert "Illegal Instruction"
Mit Berechnung von Sprungmarkenadressen statt des Kennungsstrings hat es leider auch nicht funktioniert.
Code: Alles auswählen
aussen1:
Procedure.i blah(a)
innen1:
ProcedureReturn a+1
innen2:
EndProcedure
aussen2:
;MessageRequester("",Str(?innen2-?innen1))
MessageRequester("",Str(?aussen2-?aussen1))Wie findet man also die betreffende Speicherstelle der Prozedur?
Manuell geht das schon, wenn man die Exe mit dem Disassembler "OlyDbg" untersucht und den so ermittelten Speicheroffset dann mittels Hexeditor in der Datei aufsucht. Wobei sich mir dann trotzdem die Frage stellt, ist der Offset (gemessen vom Programmanfang) noch der gleiche, wenn das Programm durch Windows in den Speicher geladen und ausgeführt wird?
- Gibt es bei Windows so etwas wie Reloc-tables?
Wenn ich mich richtig erinnere, war es beim Amiga so, dass alle lange Sprünge innerhalb eines Assemblerprogramms so ausgelegt waren, als würde das Programm ab Speicherstelle 0 beginnen. Wenn man also zu Speicherstelle 16 springen wollte, dann wurde JMP +16 (sinngemäß) codiert. Alle diese Sprünge wurden in einer Reloc-table in der "Exe" gespeichert (naja, beim Amiga hießen sie nicht Exen). Der Loader im Betriebssystem hat das Programm beim späteren Ausführen dann an irgend eine freie Speicherstelle im Speicher-Adressraum geladen - nehmen wir mal an, an die Speicherstelle 40000. Bei diesem Vorgang wurden dann alle JMP's im Programmcode mittles der Reloc-table "gepatcht". Das JMP +16 wurde mit JMP +40016 überschrieben, weil das neue Sprungziel nun an eben dieser Speicherstelle lag.
Wenn das bei Windows ähnlich ist, dann kann ich das ganze sicherlich knicken, da ich nicht weiß, ob der FASM irgendwelche relozierbaren Sprünge in meine Prozedur zaubert.
Tja, so weit gehen erstmal meine Gedanken hierzu. Ich vermute, dass das nicht so einfach sein wird.
Sollte jemand von euch dem Thema näher stehen, dann würde ich mich über hilfreiche Hinweise freuen.
Gruß Kurzer