Wie wichtig ist es heutzutage sicherzustellen, dass alle meine DLLs nicht widersprüchliche Basisadressen haben?

Raymond

20. Januar, 2017

Früher wurden Sie aufgefordert, Ihre DLLs so umzubasen, dass sie alle nicht überlappende Adressbereiche hatten, wodurch die Kosten für die Laufzeitverlagerung vermieden wurden. Ist das heutzutage noch wichtig?

Diese Situation ist eine weitere Demonstration dafür, wie wichtig es ist, dass gute Ratschläge eine Begründung haben, damit Sie erkennen können, wann sie zu schlechten Ratschlägen werden.

Die Begründung für das Rebasing lautet wie folgt: Wenn eine DLL an ihrer bevorzugten Basisadresse geladen wird, kann das Image direkt aus dem Speicher ausgelagert werden, ohne dass Korrekturen erforderlich sind. Dies bedeutet, dass die Seiten zwischen Prozessen geteilt werden können, da jeder Prozess eine identische Kopie erhält. (Natürlich stoppt die Freigabe, sobald jemand auf die Seite schreibt und seine Kopie von der freigegebenen Kopie unterscheidet.)

Wenn eine DLL nicht an ihrer bevorzugten Adresse geladen werden kann, wird das Image verschoben, und die gesamte verlagerte DLL wird jetzt von der Auslagerungsdatei unterstützt.1 Dies ist ein relativ teurer Vorgang, da die DLL von der Festplatte gelesen und repariert werden muss und eine Festschreibgebühr für die Auslagerungsdatei anfällt, um sicherzustellen, dass Platz zum Schreiben der reparierten Seiten vorhanden ist. Wenn zwei Prozesse die DLL verschieben und zufällig an denselben Ort verschieben, versucht Windows NT nicht, die verlagerten Images gemeinsam zu nutzen. Die Auslagerungsdatei enthält mehrere Kopien.

Die Kosten dieser dynamischen Verlagerung versucht Rebasing zu vermeiden. Nennen wir das die “Relocation Penalty”.”

Geben Sie ASLR ein.

ASLR bewirkt, dass die DLLs an pseudozufälligen Adressen geladen werden. Folglich wird eine DLL nur im Falle eines erstaunlichen Zufalls an ihrer bevorzugten Basisadresse geladen.

Okay, also gehen wir zurück zur Begründung, um zu sehen, ob sie noch zutrifft.

Verursacht eine DLL, die von ihrer bevorzugten Basisadresse geladen wird, eine Verlagerungsstrafe? Wenn Sie darüber nachdenken, bedeutet ASLR, dass keine DLL jemals an ihrer bevorzugten Adresse geladen wird, aber wir haben auch gesehen, dass der Kernel dies berücksichtigt, sodass ASLR unterworfene DLLs weiterhin Seiten freigeben können, ohne dass die gesamte DLL beim ersten Laden verschoben werden muss. Es gibt also keine Umzugsstrafe für den Fall, dass die DLL von ASLR verschoben wurde.

Aber was ist, wenn die DLL aus einem anderen Grund verschoben wird? Beispielsweise könnte es sein, dass die von ASLR gewählte Basisadresse im Prozess nicht verfügbar ist, weil der Prozess an dieser Stelle bereits etwas anderes zugewiesen hat. In diesem Fall muss ein traditioneller Umzug stattfinden, und Sie zahlen die Umzugsstrafe.

Ah, aber hier ist die Sache: Wenn eine DLL geladen wird, weist ASLR zufällig eine Basisadresse aus den verfügbaren Basisadressen zu, die noch nicht verwendet werden.2 Sie werden also nicht in das Szenario “Die von ASLR ausgewählte Basisadresse ist nicht verfügbar” geraten, da ASLR die Basisadresse der DLL aus den verfügbaren Basisadressen auswählt.3

Okay, Sie können also immer noch in eine Konfliktsituation geraten, aber Sie müssen wirklich daran arbeiten. Sie können beispielsweise eine DLL in einen Prozess laden und eine ASLR-zugewiesene Basisadresse abrufen. Sie starten dann einen zweiten Prozess, weisen absichtlich Speicher an dieser Adresse zu (um die Kollision zu erzwingen) und laden dann die DLL. In diesem Fall kommt es zu einem Umzug, weil Sie an der Stelle hockten, an der ASLR die DLL ablegen wollte. Aber das ist nicht schlimmer als das, was Sie vor ASLR hatten: In der Welt vor ASLR hätte das Hocken auf der bevorzugten Basisadresse einer DLL ohnehin eine Umzugsstrafe erzwungen.

Also, mal sehen, was die Geschichte ist. Rebase oder nicht Rebase?

In Gegenwart von ASLR hat das Umbasen Ihrer DLLs keine Auswirkung, da ASLR Ihre Basisadresse sowieso ignoriert und die DLL an einen Ort ihrer pseudozufälligen Wahl verlagert.

Wohlgemerkt, obwohl Rebasing keine Wirkung hat, tut es auch nicht weh.

Wenn Sie sich auf einem System ohne ASLR befinden (entweder weil es älter als ASLR ist oder weil ASLR aus irgendeinem Grund deaktiviert wurde), hilft das Rebasing aus den traditionellen Gründen.

Wohlgemerkt, Systeme ohne ASLR sind heutzutage wirklich schwer zu finden, daher bietet das Rebasing in den allermeisten Fällen keinen Nutzen. Aber in diesem verschwindend kleinen Prozentsatz der Fälle, in denen Sie keine ASLR haben, hilft das Rebasing.

Fazit: Es tut nicht weh, für alle Fälle eine Neubasis zu erstellen, aber verstehen Sie, dass die Auszahlung äußerst selten sein wird. Erstellen Sie Ihre DLL mit /DYNAMICBASE aktiviert (und mit /HIGHENTROPYVA für ein gutes Maß) und lassen Sie ASLR die Arbeit erledigen, um sicherzustellen, dass keine Basisadressenkollision auftritt. Das wird so ziemlich alle realen Szenarien abdecken. Wenn Sie in einen der sehr seltenen Fälle geraten, in denen ASLR nicht verfügbar ist, funktioniert Ihr Programm weiterhin. Aufgrund der Umzugsstrafe kann es nur etwas langsamer laufen.

1 Genauer gesagt werden alle Seiten, die Korrekturen enthielten, in die Auslagerungsdatei eingefügt. Wir haben diesen feineren Punkt letztes Mal besprochen.

2 Okay, es gibt einen dritten Fall, in dem ASLR einfach die Basisadressen ausgegangen sind. Aber auch das ist nicht schlimmer als das, was Sie vor ASLR hatten: Wenn Ihnen die Basisadressen ausgehen, dann ist es jeder für sich. Jedes Mal, wenn eine neue DLL geladen wird, muss der Kernel nach einem ausreichend großen Teil des verfügbaren Adressraums suchen, in den die DLL geladen werden kann.

3 Infolgedessen vermeidet ASLR Kollisionen tatsächlich besser als manuelles Rebasing, da ASLR das System als Ganzes anzeigen kann, während manuelles Rebasing erfordert, dass Sie alle DLLs kennen, die in Ihren Prozess geladen werden, und die Koordination von Basisadressen über mehrere Anbieter hinweg ist im Allgemeinen nicht möglich.

Raymond Chen

Folgen

Leave a Reply