Patch Points

Date: 2013-09-13

Gestern hab ich ja über Selbstmodifizierende Binaries geschrieben. Das möchte ich heute etwas fortsetzen. Allerdings geht es diesmal nicht um dauerhafte Modifikation, sondern um Modifikation die während der Laufzeit bestehen bleibt. Aber theoretisch könnte man beides auch verbinden. Vor längerer Zeit habe ich mal darüber nachgedacht, dass man ja manchmal ein System dynamisch umkonfigurieren will. Dabei werden dann die unterschiedlichen Varianten über eine Wenn-Abfrage ausgewählt:

if (featureA.enabled()) {
    // Feature A;
}
if (featureB.enabled()) {
    // Feature B;
}

Dies führt allerdings dazu, dass zur Laufzeit beständig Funktionsaufrufe an die .enabled() Funktionen gemacht werden. Der dazu passende Assembler sieht dann ungefähr so aus:

 e8 66 05 00 00          call   8048a6e <__enabled_featureA>
 83 f8 17                cmp    $0x17,%eax
 75 ab                   jne    80484c0 <main+0x10>

Jetzt habe ich mir den Spass gemacht die patch-point Bibliothek zu schreiben. Sie löst das Problem dadurch, dass sie die Stelle, an der solche Abfragen stehen entweder durch nop Instruktionen ersetzt, oder durch einen unbedingten Sprungbefehl. Das ganze wird dann noch mit einem bischen synaktischem Zucker gewürzt, und dann sieht der C-Code dazu so aus:

#include <patch_point.h>
#include <stdio.h>
patch_point_list ppl;

void foo() {
    patch_point(&ppl, "pp") {
        printf("enabled\n");
    } else {
        printf("disabled\n");
    }
}

int main() {
    foo();
    patch_point_disable(&ppl, "pp");
    foo();
}

Dabei sieht das patch_point macro etwas seltsam aus, aber es wird vom Präprozessor zu einem if (__patch_point(&ppl, "pp") == 23 gemacht. Nach dem komplieren sieht der Code ungefähr so aus, wie das Assembler-Fragment oben. Die Funktion __patch_point sucht sich dann über die Rücksprungaddresse die Stelle im Code raus, wo diese Instruktionen stehen. Nach dem ersten Aufruf, patcht sich der Funktionsaufruf selbst raus, und speichert die Stelle und weitere Informationen in der patch point Liste ab. Der Code sieht dann so aus:

 90 90 90 90 90          nop nop nop nop nop
 e9 99 02 00 00          jmp    804878d <main+0x10>

Mit dem Aufruf zu patch_point_disable Wird der jmp Befehl wieder rausgepatcht, und der Code im Speicher sieht dann so aus:

 90 90 90 90 90          nop nop nop nop nop
 90 90 90 90 90          nop nop nop nop nop