2016-12-21 2 views
0

Ich möchte einen Systemaufruf machen und den Kernel so verhalten, als wäre es ein anderer Prozess, der den Anruf tätigt. Man könnte es sich so vorstellen, als würde man sich einen anderen Prozess vorstellen. Ich weiß, dass ich mich mit dem Programmcode mit ptrace anlegen könnte, aber das ist nicht sehr elegant und erfordert wahrscheinlich Optimierungen, um mit dem Prozess, den ich mache, zu arbeiten. Plus, was ich verlange, sollte für den Kernel möglich sein, ohne den Speicher- oder Ausführungsprozess des anderen Prozesses zu berühren, es sei denn, der Effekt des Systemaufrufs, den ich ausführe, würde das natürlich verursachen.Gibt es einen Linux-Systemaufruf, mit dem ich Systemaufrufe im Kontext eines anderen Prozesses ausführen kann?

Die Art, wie ich denke, dass es funktionieren würde, wäre ein (privilegierter) Systemaufruf (nennen wir es setepid, für "Set effektive PID"), die eine PID als Argument akzeptiert. Nach setepid verhalten sich alle zukünftigen Systemaufrufe, die von diesem Prozess (oder möglicherweise nur diesem Thread) ausgeführt werden, so, als wäre es der angegebene Prozess, der den Systemaufruf veranlasst. Die Ausnahme ist der Aufruf setepid selbst, der verwendet werden kann, um den ursprünglichen Kontext wiederherzustellen oder anderweitig einen anderen Prozess zu verwenden.

Zum Beispiel könnte der folgende Code verwendet werden, um die Standardausgabe eines bereits laufenden Prozess (PID 1234 in diesem Beispiel) in die Datei output.txt, in Prozess 1234 der aktuellen Arbeitsverzeichnis zu umleiten:

setepid(1234); /* perform following system calls on process 1234 */ 

int fd = open("output.txt", O_WRONLY|O_CREAT|O_TRUNC); 
if (fd > 0) { 
    dup2(fd, 1); 
    close(fd); 
} 

setepid(0); /* done acting as 1234, restore original context */ 

Ein mögliches Problem, das das haben könnte, ist die "output.txt" Zeichenfolge-Konstante, die als ein Zeiger übergeben wird. Abhängig davon, wie setepid implementiert wird, kann es sein, dass dieser Zeiger möglicherweise als eine Adresse im Speicher des Prozesses 1234 behandelt wird. Da es auch dann nicht sinnvoll, statisch Speicher zuweisen (einschließlich für eine konstante) in einem anderen Prozess zum Zeitpunkt der Kompilierung, um das immer etwas hässlich wie diese stattdessen erfordern würde:

setepid(1234); 
char *buf = mmap(NULL, 12, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 
setepid(0); 

const char *str = "output.txt\0"; /* extra byte to make it a multiple of 4 */ 
/* Disclaimer: I'm not sure if I'm using ptrace correctly, but you get the idea. */ 
ptrace(PTRACE_ATTACH, 1234, NULL, NULL); 
ptrace(PTRACE_POKEDATA, 1234, buf, *(void**)str); 
ptrace(PTRACE_POKEDATA, 1234, buf+4, *(void**)(str+4)); 
ptrace(PTRACE_POKEDATA, 1234, buf+8, *(void**)(str+8)); 
ptrace(PTRACE_DETACH, 1234, NULL, NULL); 

setepid(1234); 

int fd = open(buf, O_WRONLY|O_CREAT|O_TRUNC); 
if (fd > 0) { 
    dup2(fd, 1); 
    close(fd); 
} 

setepid(0); 

Ich gehe davon aus eine solche Mechanismus existiert nicht, obwohl ich hoffe, ich liege falsch. Wenn meine Annahme richtig ist, gibt es Probleme, die verhindern, dass dies in einer zukünftigen Version von Linux hinzugefügt wird? Wäre es einfach (oder sogar möglich), dies in einem Kernelmodul zu implementieren? Es klingt, als wäre es ein gefährliches, aber mächtiges und potentiell nützliches Werkzeug.

Antwort

3

Was Sie vorschlagen, kann nicht funktionieren, ohne effektiv Code in den Ziel-Thread zu injizieren und das können Sie schon mit ptrace tun.

Eines der Probleme ist, dass es Daten gibt, die nur vom aktuellen Thread geändert werden. Da niemand anders es verändert, kann es leicht ohne Sperren gelesen werden. Aber mit Ihrer Annäherung würde die Invariante brechen. Ein triviales Beispiel sind Anmeldeinformationen.

Dies funktioniert auch nicht für den mitgelieferten toy fd Ersatzkoffer. Wenn ein syscall fd als Argument verwendet, muss der Zieldateizeiger gefunden werden. Wenn die Tabelle fd freigegeben ist, muss die Datei referenziert werden, da in der Zwischenzeit eine andere Person (fd) schließen kann. Der Kernel hat eine Mikrooptimierung für Single-Threading-Prozesse - da die Tabelle nicht geteilt wird, gibt es niemanden, der das fd schließt, so dass es nicht notwendig ist, später darauf zu verweisen (und es nicht zu referenzieren). Und noch einmal, eine plötzliche Änderung der fd-Tabelle würde leicht zu einer Nutzung nach der Freigabe führen, wenn Sie die vom Thread verwendete fd schließen.

Und so weiter.

Kurz gesagt, es gibt keine Möglichkeit, das wird fliegen. Das nächste, was Sie tun können, ist syscalls mit ptrace zu injizieren.

+0

Sinn macht. Ich wollte damit experimentieren, aber danke. – flarn2006

Verwandte Themen