Angenommen, Sie geben ein einzelnes C source file
, das eine max. von 300 Zeilen Code.
Nehmen Sie auch an, dass die Datei bei der Implementierung mehrerer Funktionen NICHT das Zeichen '#' enthält (dh, es gibt NO #include
statmements, und keine anderen Anweisungen, die '#' in der Datei haben.
Meine Frage ist: Garantiert die obige , dass die Datei nicht eine E / A ausführt? garantiert es, dass die Datei nicht in der Lage ist, (sagen wir) den Inhalt der Festplatte zu löschen oder andere fischige Dinge zu tun?
(Ich soll 100-200 einzelne C
Dateien bekommen, die (wie erwähnt) das char #
nicht enthalten. Ich wurde gebeten, ein einfaches Programm zu schreiben, das programmgesteuert Prüfe, ob ein einzelnes C source file
mit keinem #
möglicherweise an I / O beteiligt ist und auf das Netzwerk usw. zugreift.
Angesichts der Tatsache, dass keine Anweisungen mit #
erlaubt sind - was ist der WORST-Code, den ein Coder in einer solchen C
-Datei enthalten kann, um das System desjenigen, der es ausführt, zu beschädigen?
Ich weiß , dass kein Check zu 100% genau ist - aber ich bin daran interessiert mindestens einige grundlegende Checks durchzuführen, die bei bestimmten Ausdrücken / Keywords eine rote Flagge auslösen gefunden werden. Irgendwelche Ideen, wonach Sie suchen sollten?
Nein, das kann ich nicht garantieren. Sie können den Code erzeugen, in dem alle Includes und Makros expandiert werden, und Sie können es zu einer einzigen großen Datei machen, dann kompilieren Sie es ... diese Datei enthält keine Präprozessor-Direktive, obwohl sie alles tun kann, was C normalerweise kann auf einem System.
Man könnte einfach die Definitionen von Standarddateitypen und -funktionen (z. B. FILE, fopen (), fprintf (), flocse ()) usw. in eine C-Datei kopieren und einfügen. Auf diese Weise wird kein Include benötigt und wenn die Datei kompiliert und mit den richtigen Bibliotheken verknüpft ist, kann sie I / O durchführen.
#
ist nicht das einzige Token, das eine Präprozessordirektive starten kann. ??=
und %:
sind äquivalente Definitionen im Standard. (Aber sie werden nicht von allen Compilern erkannt.)
C ermöglicht unsichere Operationen mit Zeigern. Zum Beispiel ist es auf einem System ohne ASLR trivial, den Zeiger auf beliebige Bibliotheksfunktionen zu bekommen. Es ist nicht sehr robust, da eine Speicherzugriffsverletzung Sie töten wird, aber zumindest wenn Sie das Zielsystem kennen, ist es möglich.
ASLR macht es etwas schwieriger, aber ich nehme an, Sie könnten einfach einen Zeiger auf die aktuelle Position auf dem Stapel bekommen und dann nach oben kriechen, bis Sie den Stapel erreichen, der zum Einstiegspunkt Ihres Threads gehört. Was sicherlich einige interessante Hinweise haben wird.
Das Fehlen von Präprozessordirektiven garantiert nicht irgendetwas außer dem Fehlen von Präprozessordirektiven.
Sie können die Datentypen und Funktionsprototypen für beliebige Bibliotheksfunktionen, die Sie interessieren, noch manuell hinzufügen. Wenn Sie mit der zugrunde liegenden Plattform vertraut sind, können Sie die Standardbibliothek vollständig umgehen und Systemaufrufe direkt ausführen.
Es war einmal, ich sah Code (wahrscheinlich für die IOCCC), die ein Array von unsigned Char verwendet, um rohe Opcodes zu speichern und dann Typ Punning verwendet, um es als eine Funktion zu behandeln, etwas wie
%Vor%Beachten Sie, dass sich dies auf undefiniertes Verhalten und eine Menge nicht portabler Annahmen stützte, und ich bin mir nicht einmal sicher, dass ein solcher Ansatz mehr funktionieren würde. Aber wenn dies der Fall wäre, wäre dies mit einem einfachen Quell-Scan nicht leicht zu erfassen.
BEARBEITEN
Ich habe den Code gefunden, an den ich gedacht habe - es war ein IOCCC -Eintrag von 1984. Das tut er nicht arbeite so, wie ich es beschrieben habe. Hey, ich werde alt und Sachen kleben nicht mehr so an mein Gehirn wie früher.
%Vor%Hier ist die Erklärung :
%Vor%Ich weiß auch nicht, ob dieser Trick auf einem modernen Desktop-Betriebssystem funktionieren würde, aber es würde Spaß machen, das herauszufinden.
nicht notwendig. Die meisten Compiler erzeugen Warnungen für implizite Deklarationen, verknüpfen sie aber trotzdem in den Funktionen. Sie können eine Liste von io-performing-Funktionen generieren und sehen, ob sie aufgerufen werden, aber das schließt immer noch nicht aus, dass inline asm io-bezogene Systemaufrufe aufruft.
Sie sollten wahrscheinlich mit niedrigen Privilegien in einer Sandbox laufen und sich ansehen, was sie mit sowas wie strace machen.
Das folgende Programm ist ein gültiges C-Programm, das die Ausgabe in stdout
erzeugt. Es enthält keine #
-Zeichen:
Es erzeugt nicht einmal eine Warnung vom Compiler ( /Wall /W3
auf MSVC und -Wall -Wextra
auf MinGW), geschweige denn einen Fehler.
Sie können auch versuchen, die C-Dateien in eine statische Binärdatei zu kompilieren, sie zu zerlegen und nach Systemaufruf-Anweisungen (sysenter, int) zu suchen. IO kann nicht vom Benutzerbereich aus ausgeführt werden und ein Prozess muss zum Kernel gehen, um irgendeine Art von IO auszuführen.
Dies schützt jedoch immer noch nicht vor der Ausführung von Anweisungen in Nicht-Textabschnitten Ihrer Binärdatei. Im schlimmsten Fall haben Sie möglicherweise Anweisungen, die zur Laufzeit erstellt und ausgeführt werden. Dafür denke ich, dass es am besten ist, die Codeabdeckung zu übernehmen und den Prozess für Systemaufrufe zu verfolgen. Linux hat strace, was dabei helfen kann.
Tags und Links c