Stapel eines anderen Prozesses lesen?

16

Ich versuche, den Stapel eines untergeordneten Prozesses zu lesen, habe aber kein Glück. Ich weiß, dass dies möglich ist ptrace, aber über ptracedie Benutzeroberfläche können Sie jeweils nur ein Wort lesen, und ich versuche, einen größeren Teil des Stapels zu scannen.

Ich habe auch versucht, das /proc/$pid/memaus den Grenzen des Stapels zu lesen, wie es aus der /proc/$pid/mapsDatei extrahiert wurde, nachdem zuerst ptrace zum Anhängen verwendet wurde (wie hier vorgeschlagen ), aber das Lesen schlägt weiterhin fehl (selbst wenn es als Root ausgeführt wird), obwohl derselbe Code beim Versuch erfolgreich ist Lesen aus verschiedenen Teilen des Prozesses (z. B. Haufen).

Was mache ich falsch? Gibt es noch eine andere Möglichkeit?

user4537
quelle
Hast du waitpidzwischen ptrace(PTRACE_ATTACH,…)und angerufen read(sonst gibt es eine mögliche Rennbedingung)? Welcher Fehler kommt readzurück? Tut das Kind etwas Besonderes an der Speicherzuordnung - können Sie Ihren Code mit einem einfachen Kind wie dem versuchen sleep?
Gilles 'SO- hör auf böse zu sein'
Ich habe wait nach ptrace verwendet und ein scanf in das Kind eingefügt, um es zum Warten zu zwingen.
User4537
Geht das nur unter Linux? Solaris hat auch ein / proc-Dateisystem, aber es unterscheidet sich philosophisch völlig von Linux. Viele "Binärdateien".
Bruce Ediger
Mach einfach ein System ("pstack pid ") und analysiere die Ausgabe ..
vrdhn
Siehe ps: vollständiger Befehl ist für einige Beispiele zu lang
Stéphane Chazelas

Antworten:

5

ptraceÜber die Benutzeroberfläche können Sie jeweils nur ein Wort lesen, und ich versuche, einen größeren Teil des Stapels zu scannen

Dann benutze einfach eine Schleife. Ich verstehe ehrlich gesagt nicht, wie das ein Problem darstellt ptrace, ich nutze es die ganze Zeit, um aus der Ferne auf Prozesse zuzugreifen.

Ich benutze so etwas:

static int memcpy_from_target(pid_t pid, char *dest, long src, size_t n)
{
    static int const align = sizeof(long) - 1;

    while (n)
    {
        size_t todo = MIN(n, sizeof(long) - (src & align));
        long data = ptrace(PTRACE_PEEKTEXT, pid, src - (src & align), 0);
        if (errno)
        {
            perror("ptrace_peektext (memcpy_from_target)");
            return -1;
        }
        memcpy(dest, (char *)&data + (src & align), todo);

        dest += todo; src += todo; n -= todo;
    }

    return 0;
}
sam hocevar
quelle
Hallo Sam, während dein Code funktioniert (und genau das mache ich gerade), hat er einen hohen Leistungsaufwand.
user4537
@ user4536: Ich verstehe. Ich denke an eine andere Strategie, die ich veröffentlichen werde, wenn ich Zeit habe, sie aufzuschreiben. Was sind Ihre typischen Stapelgrößen?
Sam Hocevar
Das ist wirklich schwer zu sagen, da meine Forschung keine bestimmte Stapelgröße voraussetzt, aber für dieses Argument lassen Sie uns mindestens ein paar Seiten lang sagen. Können Sie einen Hinweis zu Ihrer Strategie geben? Trotzdem danke für die Hilfe!
user4537
1

Hier ist eine weitere Strategie, die möglicherweise optimiert werden muss, bei großen Datenmengen jedoch effizienter sein sollte. Die Idee ist, Syscalls im Remote-Prozess auszuführen, um den Stack-Inhalt abzurufen. Es wird einen bestimmten Architekturcode benötigen, aber wenn Sie nur x86 / x86_64 als Ziel festlegen, sollte dies nicht zu aufwändig sein.

  1. Erstellen Sie eine Named Pipe wie "/tmp/fifo"in Ihrem aufrufenden Prozess.
  2. Begib dich in den verfolgten Prozess, bis er von einem Systemaufruf zurückkehrt, und verwende " PTRACE_SYSCALLto step", um waitpid()zu warten und PTRACE_GETREGS/ oder PTRACE_PEEKTEXTden aktuell ausgeführten Opcode zu überprüfen.
  3. Sichern Sie die Register des Remote-Prozesses und einen kleinen Bereich seines Stapels.
  4. Führen Sie syscalls auf dem Remote - Prozess durch Überschreiben seines Stack mit eigenen Daten: open("/tmp/fifo"), write()die Stack - Inhalte, close()die Beschreiber.
  5. Stellen Sie den Status des Remote-Prozesses wieder her.
  6. Lesen Sie die FIFO-Daten aus Ihrem Anrufprozess.

Es gibt vielleicht elegantere Alternativen zur Named Pipe, aber mir fällt gerade keine ein. Der Grund, warum ich nur Syscalls verwende, ist, dass die Ferncodeinspeisung auf modernen Systemen aufgrund verschiedener Sicherheitsmaßnahmen ziemlich unzuverlässig ist. Der Nachteil ist, dass es hängen bleibt, bis der Remote-Prozess einen Systemaufruf ausführt (was bei einigen Programmen, die meistens Berechnungen ausführen, ein Problem sein kann).

In dieser Quelldatei sehen Sie freien Code, der den größten Teil der Arbeit ausführt . Feedback zum Code ist willkommen!

sam hocevar
quelle
1

Ein weiterer Vorschlag.

Wenn es im Hauptkernelbaum von Linux akzeptiert wird, können Sie Christopher Yeohs Cross Memory Attach- Patch verwenden. Siehe beispielsweise die Dokumentation zu process_vm_readv .

sam hocevar
quelle
1

Mit dem proc-Dateisystem können Sie problemlos den Stapel eines anderen Prozesses lesen (Sie benötigen dafür root-Zugriff). Bevor Sie willkürlich aus dem / proc / pid / mem lesen, müssen Sie das / proc / pid / maps konsultieren. Ein einfaches Einlesen dieser Datei zeigt viele Einträge. Wir sind an dem als Stack gekennzeichneten Eintrag interessiert. Sobald Sie dies erhalten, müssen Sie die unteren und oberen Grenzen des Stapels lesen. Öffnen Sie nun einfach die Datei / proc / pid / mem, suchen Sie die untere Grenze des Stapels und lesen Sie die richtige Datengröße.

Ajay Brahmakshatriya
quelle
1
Bist du sicher, dass du meinst memsund nicht maps? (Ich kann keine memsEinträge unter meinem /procDateisystem sehen.) Das OP hat bereits erwähnt, dass die Stapelgrenzen gelesen wurden /proc/$pid/maps- was schlagen Sie vor, was sie anders machen?
JigglyNaga
Bearbeitet den Tippfehler. Ich habe genau das getan, was ich in meiner Antwort erwähnt habe, und es wurden 132 KB Stack-Daten ausgegeben. Wir brauchen mehr Informationen darüber, was OP falsch gemacht hat. Vielleicht kann OP den Code teilen, den er zum Lesen der Stapelgrenzen verwendet hat. Wenn er nicht antwortet, teile ich meine.
Ajay Brahmakshatriya
0

Sie könnten es mit lsstack versuchen . Es verwendet ptrace, genau wie jedes andere erfolgreiche Programm, das den Stack eines anderen Prozesses liest. Ich konnte ein Programm mit / proc / $ pid / mem nicht zum Laufen bringen. Ich glaube, dass Sie es nicht so machen können, obwohl Sie es logischerweise sollten.

Bruce Ediger
quelle