【Android】Frida Hook-Datei-Lese- und Schreibvorgänge

Vorwort

Bei der Suche nach clientseitigen Schwachstellen konzentrieren wir uns normalerweise darauf, welche Dateien die Anwendung Lese- und Schreibvorgänge durchführt. Wenn wir die gelesenen Dateien kontrollieren oder vertraulich geschriebene Dateien beobachten können, können wir normalerweise einen gewissen Schaden anrichten. In diesem Artikel erfahren Sie, wie Sie Lese- und Schreibvorgänge für Dateien über Frida überwachen.

verwandte Informationen

1. Lesen und schreiben Sie zugehörige API-Aufrufe

Unter Linux-Systemen umfassen Dateierstellungs-, Lese-, Anhänge-, Schreib- und Löschvorgänge die folgenden Systemaufrufe:

  • Öffnen: Datei öffnen, wird zum Erstellen und Öffnen von Dateien verwendet.
  • fopen: eine Datei öffnen
  • read: Daten aus der Datei lesen.
  • schreiben: Daten in die Datei schreiben.
  • lseek: Bewegen Sie den Dateizeiger.
  • schließen: Schließen Sie die Datei.
  • Verknüpfung aufheben: Datei löschen.

2. Interceptor.attach

In Frida ist **Interceptor.attach()** eine Funktion zum Abfangen von Funktionsaufrufen. Es ermöglicht uns, beliebige Funktionsaufrufe im Zielprozess abzufangen und benutzerdefinierten JavaScript-Code vor oder nach dem Funktionsaufruf auszuführen.

Die Syntax der Funktion Interceptor.attach() lautet wie folgt:

Interceptor.attach(target, callbacks)

Unter diesen gibt der Zielparameter die abzufangende Funktion an, bei der es sich um den Funktionsnamen oder die Funktionsadresse handeln kann. Der Callbacks-Parameter ist ein Callback-Funktionsobjekt, mit dem der Code definiert wird, der beim Abfangen von Funktionsaufrufen ausgeführt werden soll. Das Callback-Funktionsobjekt kann die folgenden Funktionen enthalten:

  • onEnter(args): Die Rückruffunktion, die vor dem Aufruf der Funktion ausgeführt wird. Der Parameter args ist ein Array, das die Parameter enthält, wenn die Funktion aufgerufen wird.
  • onLeave(retval): Eine Rückruffunktion, die nach dem Funktionsaufruf ausgeführt wird. Der Parameter retval ist der Rückgabewert des Funktionsaufrufs.

3. Abfangjäger

In Frida ist das Interceptor- Objekt ein Toolset zum Abfangen von Funktionsaufrufen und zum Ändern von Funktionsimplementierungen. Zusätzlich zur Funktion **Interceptor.attach()** stellt das Interceptor-Objekt auch die folgenden häufig verwendeten Methoden bereit:

  • Interceptor.replace(target, replacement) : Wird zum Ersetzen der Funktionsimplementierung verwendet. Der Zielparameter gibt die zu ersetzende Funktion an. Dabei kann es sich um einen Funktionsnamen oder eine Funktionsadresse handeln. Der Ersetzungsparameter ist eine JavaScript-Funktion, die zum Ersetzen der Implementierung der ursprünglichen Funktion verwendet wird. Die Parameter und der Rückgabewert der Ersatzfunktion müssen mit der Originalfunktion übereinstimmen. Das Folgende ist ein Beispiel für die Verwendung der Funktion Interceptor.replace() zum Ersetzen der Implementierung der Funktion strcmp():
// Replace the implementation of strcmp() 
function Interceptor.replace(
	Module.findExportByName("libc.so", "strcmp"), 
	new NativeCallback(function (str1, str2) {
    
       
		console.log("strcmp() called with arguments: " + Memory.readUtf8String(str1) + ", " + Memory.readUtf8String(str2));   
		return 0; 
	}, 'int', ['pointer', 'pointer'])); 
  • Interceptor.attachOnce(target, callbacks) : Wird zum einmaligen Abfangen von Funktionsaufrufen verwendet. Im Gegensatz zur Funktion Interceptor.attach() fängt die Funktion Interceptor.attachOnce() einen Funktionsaufruf nur einmal ab und entsperrt ihn dann automatisch. Die Parameter dieser Methode sind die gleichen wie die der Funktion Interceptor.attach().

  • Interceptor.detachAll() : Wird zum Trennen aller Abfangjäger verwendet. Diese Methode entsperrt alle Interceptoren, die über die Funktionen Interceptor.attach() und Interceptor.attachOnce() hinzugefügt wurden.

  • Interceptor.flush() : wird zum Aktualisieren aller geänderten Funktionsimplementierungen verwendet. Nachdem Sie die Funktionsimplementierung geändert haben, müssen Sie die Funktion Interceptor.flush() aufrufen, um den Funktionscache im Zielprozess zu aktualisieren. Dadurch wird sichergestellt, dass die geänderte Funktionsimplementierung wirksam wird.

4. Module.findExportByName()

Module.findExportByName() ist eine Funktion, die verwendet wird, um die Adresse der exportierten Funktion im angegebenen Modul zu finden. Es kann die Funktionsadresse über den Modulnamen und den Funktionsnamen finden und ein NativePointer-Objekt zurückgeben, das auf die Funktionsadresse zeigt.

Die Syntax der Funktion Module.findExportByName() lautet wie folgt:

Module.findExportByName(moduleName, symbolName)

Unter diesen ist der Parameter moduleName der Name des zu durchsuchenden Moduls, bei dem es sich um einen Moduldateinamen oder eine Modulnamenzeichenfolge handeln kann. Der Parameter symbolName ist der zu suchende Funktionsname, bei dem es sich um eine Funktionsnamenzeichenfolge oder eine Funktionsadresse handeln kann . Es ist zu beachten, dass, wenn moduleName ein Moduldateiname ist, die Dateipfadinformationen enthalten sein müssen und das Pfadtrennzeichen mit einem Backslash (\) maskiert werden muss.

Wenn der erste Parameter NULL ist, bedeutet dies, dass die Suchfunktion global ausgeführt wird.

Das Folgende ist ein Beispiel für die Verwendung der Funktion Module.findExportByName(), um die Adresse der Funktion printf() im Modul libc.so zu finden:

var printfAddr = Module.findExportByName("libc.so", "printf");
console.log("printf() address: " + printfAddr);

Im obigen Beispiel verwenden wir die Funktion Module.findExportByName(), um die Adresse der Funktion printf() im Modul libc.so zu finden und das Ergebnis an die Konsole auszugeben.

Es ist zu beachten, dass die Funktion Module.findExportByName() nur Funktionsadressen in geladenen Modulen finden kann . Wenn Sie die Funktionsadresse in einem entladenen Modul suchen möchten, können Sie die Funktion Module.load() verwenden, um das Modul dynamisch zu laden, und die Funktion Module.getExportByName(), um die Funktionsadresse zu finden.

Frida-Skript zum Überwachen des Lesens und Schreibens von Dateien

Schreiben Sie auf der Grundlage der oben genannten relevanten Kenntnisse das folgende Frida-Skript

function hook_file_read() {
    
    

    // Hook open、fopen、read函数
    Interceptor.attach(Module.findExportByName(null, "open"), {
    
    
        onEnter: function (args) {
    
    
            var path = Memory.readUtf8String(args[0]);
            this.path = path;
        },
        onLeave: function (retval) {
    
    
            if (retval > 0) {
    
    
                console.log("[open] path: " + this.path);


            }
        }
    });

    Interceptor.attach(Module.findExportByName(null, "fopen"), {
    
    
        onEnter: function (args) {
    
    
            var path = Memory.readUtf8String(args[0]);
            this.path = path;
        },
        onLeave: function (retval) {
    
    
            if (retval != 0) {
    
    
                console.log("[fopen] path: " + this.path);

            }
        }
    });

    Interceptor.attach(Module.findExportByName(null, "read"), {
    
    
        onEnter: function (args) {
    
    
            this.fd = args[0].toInt32();
            this.buf = args[1];
            this.size = args[2].toInt32();
        },
        onLeave: function (retval) {
    
    
            if (retval > 0 && this.fd > 0) {
    
    
                var data = Memory.readByteArray(this.buf, retval >>> 0);
                console.log("[read] fd: " + this.fd + ", size: " + retval + ", data: " + data);
            }
        }
    });
    // 3. Hook the write system call
    Interceptor.attach(Module.findExportByName(null, "write"), {
    
    
        onEnter: function (args) {
    
    
            // Get the file descriptor and buffer address
            var fd = args[0].toInt32();
            var buf = args[1];

            // Log the write call
            console.log("[write] fd: " + fd + ", buf: " + buf);
        }
    });

    // 4. Hook the lseek system call
    Interceptor.attach(Module.findExportByName(null, "lseek"), {
    
    
        onEnter: function (args) {
    
    
            // Get the file descriptor and offset
            var fd = args[0].toInt32();
            var off = args[1].toInt32();

            // Log the lseek call
            console.log("[lseek] fd: " + fd + ", offset: " + off);
        }
    });

    // 5. Hook the close system call
    Interceptor.attach(Module.findExportByName(null, "close"), {
    
    
        onEnter: function (args) {
    
    
            // Get the file descriptor
            var fd = args[0].toInt32();

            // Log the close call
            console.log("[close] fd: " + fd);
        }
    });

    // 6. Hook the unlink system call
    Interceptor.attach(Module.findExportByName(null, "unlink"), {
    
    
        onEnter: function (args) {
    
    
            // Get the file path
            var path = Memory.readUtf8String(args[0]);

            // Log the unlink call
            console.log("[unlink] path: " + path);
        }
    });


}

// 调用hook_file_read函数
setImmediate(hook_file_read);

Laufbeispiel:
Fügen Sie hier eine Bildbeschreibung ein

Nachwort

Interceptor.attach ist sehr leistungsstark. Es hängt hauptsächlich von den Mining-Ideen ab und kann oft einen sehr guten Hilfseffekt haben.

Ich denke du magst

Origin blog.csdn.net/xiru9972/article/details/131182528
Empfohlen
Rangfolge