изменить / proc / PID / Environment после запуска процесса

Проблема с несколькими идентичными USB-устройствами

У меня Rasperry Pi с четырьмя камерами. Я беру пиксель с помощью fswebcam , который идентифицирует камеры как / dev / video0 .. video3 . Иногда камера video0 , vide02 , video4 и video6 , но пока об этом можно забыть.

Мне нужен постоянный идентификатор для идентификации номера камеры, например, video0 всегда одна и та же камера, потому что я подписываю изображения. К сожалению, это происходит ненадежно - при загрузке камеры нумеруются как video0 .. video3 , но не всегда одинаково.

Все камеры имеют одинаковый идентификатор и серийный номер.

Решение этой проблемы связано с правилами udev, но там также есть много рыболовных крючков.

Если вы введете команду

udevadm info –attribute-walk –path=/dev/video0

, вы получите готовый результат, но основные биты будут

KERNEL=”video0”, SUBSYSTEM=”video4linux” and KERNELS=”1:1.2.4:1.0”.

. Бит KERNELS - это порт концентратора USB. С четырьмя камерами их четыре - они не меняются при перезагрузке, но видео {x} , связанное с портом , может измениться.

Итак, нам нужно правило udev, чтобы привязать номер видео к порту USB-концентратора - что-то вроде:

KERNEL==”video0”,SUBSYSTEM=”video4linux”,KERNELS==”1:1.2.4:1.0”,SYMLINK+=”camera0” 

Выглядит просто - доступ к камере с помощью

fswebcam –d  $realpath /dev/camera0

За исключением того, что это не работает - если вы поместите это в udev правило, и система выделила video0 (при загрузке) на другой порт, правило udev игнорируется.Символьная ссылка на / dev / camera0 в основном говорит об отсутствии такого устройства . Квадратная.

Мы хотим привязать символическую ссылку к адресу USB-концентратора, а не к номеру видео {x} . Потребовалась программа на Python.

Первым шагом было выполнение

fswebcam –d /dev/video${x}  tst.jpg

для x между 1 и 8. Наличие tst.jpg после каждого вызова определяет, есть ли камера на этом номере видео. Из этого составьте список активных номеров видео. По моему опыту, это либо 0,1,2,3 , либо 0,2,4,6 для камер, которые я использовал.

Другие, конечно, могут составить этот список, используя другой процесс.

Затем для каждого номера видео в списке запустите

udevadm info –attribute-walk –path=/dev/videox > dd

и извлеките KERNELS = строку из dd . В результате вы получите список адресов USB-портов для камер. Отсортируйте этот список так, чтобы на следующем шаге вы всегда обрабатывали его в том же порядке. Назовите это «список адресов».

Запустите команду udevadm…> dd еще раз и создайте список, который выглядит как

KERNEL==”video0”, SUBSYSTEM=”video4linux”,KERNELS==”1:1.2.4:1.0 ”,SYMLINK+=”camerax”. Call this the “video list”.

Теперь просмотрите список адресов - для каждой записи найдите соответствующую запись в списке видео. Создайте новый список, который выглядит как набор строк, например

KERNEL==”video0”, SUBSYSTEM=”video4linux”,KERNELS==”1:1.2.4:1.0 ”,SYMLINK+=”camera2”

. X (номер символической ссылки) заменяется порядковым номером в списке адресов.

Теперь у вас есть работающее правило udev. Символьная ссылка, привязанная к адресу USB-концентратора, независимо от того, какой номер видео назначен этому порту при загрузке.

Запишите окончательный список в файл /etc/udev/rules.d/cam.rules . Запустите триггер udevadm , чтобы активировать его, и работа будет выполнена. / dev / camera2 будет той же камерой (USB-порт) независимо от ее номера видео.

9
12.08.2016, 11:13
2 ответа

В Linux вы можете перезаписать значение строк среды в стеке.

Таким образом, вы можете скрыть запись, перезаписав ее нулями или чем-то еще:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[], char* envp[]) {
  char cmd[100];

  while (*envp) {
    if (strncmp(*envp, "k=", 2) == 0)
      memset(*envp, 0, strlen(*envp));

    envp++;
  }

  sprintf(cmd, "cat /proc/%u/environ", getpid());

  system(cmd);
  return 0;
}

Запуск от имени:

$ env -i a=foo k=v b=bar ./wipe-env | hd
00000000  61 3d 66 6f 6f 00 00 00  00 00 62 3d 62 61 72 00  |a=foo.....b=bar.|
00000010

k = v был заменен на \ 0 \ 0 \ 0 .

Обратите внимание, что setenv ("k", "", 1) для перезаписи значения не будет работать, поскольку в этом случае выделяется новая строка "k =" .

Если вы иным образом не изменили переменную среды k с помощью setenv () / putenv () , то вы также должны иметь возможность что-то делать вот так, чтобы получить адрес строки k = v в стеке (ну, одной из них):

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(int argc, char* argv[]) {
  char cmd[100];
  char *e = getenv("k");

  if (e) {
    e -= strlen("k=");
    memset(e, 0, strlen(e));
  }

  sprintf(cmd, "cat /proc/%u/environ", getpid());

  system(cmd);
  return 0;
}

Обратите внимание, однако, что она удаляет только один из ] k = v записей, полученных в среде. Обычно есть только один, но ничто не мешает кому-либо передать оба k = v1 и k = v2 (или k = v дважды) в env список передан в execve () . Это было причиной уязвимостей системы безопасности в прошлом, таких как CVE-2016-2381 . Это действительно могло произойти с bash до шеллшока при экспорте и переменной, и функции с одним и тем же именем.

В любом случае всегда будет небольшое окно, в течение которого строка env var еще не была переопределена, поэтому вы можете найти другой способ передать команде секретную информацию (например, канал, например), если его раскрытие через / proc / pid / окружающая среда вызывает беспокойство.

Также обратите внимание, что в отличие от / proc / pid / cmdline , / proc / pid / environment доступны только процессам с тем же euid или root (или root, только если euid и ruid процесса - это не одно и то же, казалось бы).

Вы можете скрыть от них это значение в / proc / pid / environmental , но они все равно смогут получить любую другую копию, которую вы сделали из строки в памяти, например, прикрепив отладчик к нему.

См. https: //www.kernel.org / doc / Documentation / security / Yama.txt , чтобы узнать о способах предотвратить это по крайней мере для пользователей без полномочий root.

12
27.01.2020, 20:05

С 2010 года не было необходимости перезаписывать приведенные выше строки (не совсем в)стеке основного потока в Linux.

Как /proc/self/cmdline, так и /proc/self/environмогут быть изменены самим процессом во время выполнения путем вызова функции prctl()соответственно PR_SET_MM_ARG_START+ PR_SET_MM_ARG_ENDили PR_SET_MM_ENV_START+ PR_SET_MM_ENV_END. Они напрямую устанавливают указатели памяти на пространство памяти приложения процесса, удерживаемое ядром для каждого процесса, которые используются для извлечения содержимого /proc/${PID}/cmdlineи /proc/${PID}/environи, следовательно, командной строки и среды, о которых сообщает psкоманда.

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

Это описано на странице руководства Linux для функции prctl(2), а также на странице руководства environ(7). Что не задокументировано , так это то, что ядро ​​отклоняет любую попытку установить начальный адрес выше конечного адреса или конечный адрес ниже начального адреса; или (или -)установить любой адрес равным нулю. Кроме того, это не оригинальный механизм, предложенный Брайаном Донланом в 2009 году, который позволял устанавливать начало и конец в одной операции, атомарно. Более того, ядро ​​не предоставляет способа получить текущие значения этих указателей.

Это затрудняет изменение среды и областей командной строки с помощью prctl(). Функцию prctl()приходится вызывать до четырех раз, поскольку первые попытки могут привести к попыткам установить начальный указатель выше конечного, в зависимости от того, где в памяти находятся старые и новые данные.Нужно вызвать его далее четыре раза, если вы хотите гарантировать, что это не приведет к появлению возможности для других процессов в системе проверять произвольный диапазон пространства памяти процесса в период когда новое начало/конец было установлено, но новое начало/конец не было.

Один атомарный системный вызов, который устанавливает весь диапазон за один раз, был бы намного проще для безопасного использования прикладными программами.

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

Я написал довольно простые функции setprocargv()и setprocenvv()для своих наборов инструментов, которые используют это. Цепочка -загрузки программ из встроенных наборов инструментов, таких как setenvи foreground, таким образом, отражает связанную -с аргументами команды и средой, где это разрешено Linux.

# /package/admin/nosh/command/clearenv setenv WIBBLE wobble foreground pause \; true &
[1] 1057
# hexdump -C /proc/1057/cmdline
00000000  66 6f 72 65 67 72 6f 75  6e 64 00 70 61 75 73 65  |foreground.pause|
00000010  00 3b 00 74 72 75 65 00                           |.;.true.|
00000018
# hexdump -C /proc/1057/environ
00000000  57 49 42 42 4c 45 3d 77  6f 62 62 6c 65 00        |WIBBLE=wobble.|
0000000e
# hexdump -C /proc/1058/cmdline
00000000  70 61 75 73 65 00                                 |pause.|
00000006
# hexdump -C /proc/1058/environ
00000000  57 49 42 42 4c 45 3d 77  6f 62 62 6c 65 00        |WIBBLE=wobble.|
0000000e
# 

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

Дополнительная литература

10
27.01.2020, 20:05

Теги

Похожие вопросы