2016-12-21 1 views
0

Что я хочу сделать, это сделать системный вызов и заставить ядро ​​действовать так, как если бы это был другой процесс, делающий вызов. Вы могли бы подумать об этом, как о выдаче другого процесса. Я знаю, что могу использовать код программы, используя ptrace, но это не очень элегантно и, вероятно, требует настройки для работы с любым процессом, который я делаю. Плюс то, что я прошу, должно быть возможным для ядра без необходимости касаться памяти или процесса процесса другого процесса, если, конечно, эффект от системного вызова, который я выполняю, не вызвал бы этого.Есть ли системный вызов Linux, который позволяет мне делать системные вызовы в контексте другого процесса?

Способ, которым я собираюсь работать, будет иметь (привилегированный) системный вызов (назовем его setepid, для «set effective PID»), который принимает PID в качестве аргумента. После setepid любые будущие системные вызовы, выполненные этим процессом (или, возможно, только этот поток), будут вести себя так, как если бы это был указанный процесс, вызывающий системный вызов. Исключением является сам вызов setepid, который может быть использован для восстановления исходного контекста или иным образом нацелен на другой процесс.

Например, следующий код может быть использован для перенаправления стандартного вывода уже запущенного процесс (PID 1234 в данном примере) в файл output.txt, расположенный в процессе 1234 в текущем рабочем каталоге:

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 */ 

Возможна одна из возможных проблем: строка константы "output.txt", которая передается как указатель. В зависимости от того, как реализован setepid, возможно, по необходимости он может обрабатывать этот указатель как адрес в памяти процесса 1234. Как правило, она даже не имеет смысла статически выделить память (в том числе и для постоянной) в другом процессе во время компиляции, обходящий, что потребовало бы что-то уродливое, как это вместо:

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); 

Я предполагаю, что такая механизма не существует, хотя я надеюсь, что ошибаюсь. Если мое предположение верно, есть ли какие-либо проблемы, которые помешали бы этому добавить в будущую версию Linux? Было бы просто (или даже возможно) реализовать это в модуле ядра? Похоже, это был бы опасный, но мощный и потенциально полезный инструмент.

ответ

3

То, что вы предлагаете, не может работать без эффективного ввода кода в целевой поток и того, что вы уже можете сделать с помощью ptrace.

Одна из проблем заключается в том, что существуют данные, которые модифицируются только текущим потоком. Так как никто другой не модифицирует его, его можно тривиально читать без блокировок. Но с вашим подходом инвариант сломается. Тривиальный пример - это учетные данные.

Это не работает для случая замены игрушек fd. При выполнении любого syscall, принимающего fd в качестве аргумента, необходимо найти указатель целевого файла. Если таблица fd является общей, файл должен ссылаться, так как кто-то еще может закрыть (fd) тем временем. Ядро имеет микрооптимизацию для однопоточных процессов - поскольку таблица не используется совместно, никто не закрывает fd, поэтому нет необходимости ссылаться (и не указывать) позже. И еще раз, внезапная модификация таблицы fd легко приведет к использованию после освобождения при закрытии fd, используемого потоком.

И так далее.

Короче говоря, это не значит, что это будет летать. Самое близкое, что вы можете сделать, это ввести syscalls с помощью ptrace.

+0

Имеет смысл. Я надеялся поэкспериментировать с этим, но спасибо. – flarn2006

Смежные вопросы