Как в процессе найти переменную

Анализ памяти процесса

Организация памяти процессов ОС Windows рассмотрена во многих книгах и статьях. Мы изучим только те аспекты этого вопроса, которые имеют отношение к поиску переменных в памяти, а также чтению и записи их значений.

Адресное пространство процесса

Исполняемый EXE-файл и запущенный процесс ОС – это не одно и то же. Файл – это некоторые данные, записанные на устройство хранения информации (например жёсткий диск). Исполняемый файл содержит инструкции (или машинный код), которые выполняет процессор без каких либо дополнительных преобразований.

Когда вы запускаете EXE-файл, для его исполнения ОС нужно выполнить несколько шагов. Во-первых, прочитать его содержимое с устройства хранения и записать в

оперативную память

(random-access memory или RAM). Благодаря этому процессор получает намного более быстрый доступ к инструкциям из файла, поскольку скорость его интерфейса с RAM на несколько порядков выше чем с любым диском.

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

планировщик

(scheduler). Благодаря ей каждый процесс получает единицы времени (тики или секунды) в зависимости от своего приоритета.

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

Где процесс хранит свои данные? Мы уже знаем, что ОС всегда загружает исполняемые инструкции в оперативную память. В случае данных, сам процесс может свободно выбрать место их хранения: жесткий диск, оперативная память или даже удалённый компьютер (например игровой сервер подключённый по сети). Большая часть данных, необходимых во время работы процесса копируются в оперативную память для ускорения доступа к ней. Поэтому, именно в RAM мы можем прочитать состояния игровых объектов. Они будут доступны на протяжении всего времени выполнения (runtime) процесса.

Иллюстрация 3-2 демонстрирует элементы типичного процесса. Как правило, он состоит из нескольких модулей. Обязательным из них является EXE, который содержит все инструкции и данные, загруженные из исполняемого файла. Другие модули (обозначенные DLL_1 и DLL_2) соответствуют библиотекам, функции которых вызываются из EXE.

Иллюстрация 3-2. Элементы типичного процесса Windows

Все Windows приложения используют как минимум одну системную библиотеку, которая предоставляет доступ к WinAPI функциям. Даже если вы не пользуетесь WinAPI явно в своей программе, компилятор вставляет вызовы ExitProcess и VirtualQuery автоматически в ходе компиляции. Они отвечают за корректное завершение процесса и управление его памятью.

Мы рассмотрели исполняемый файл и запущенный процесс. Теперь поговорим о библиотеках с функциями. Они делятся на два типа: динамически подключаемые (dynamic-link libraries или DLL) и статически подключаемые (static libraries). Главное различие между ними заключается во времени разрешения зависимостей. Когда исполняемый файл использует функцию библиотеки, говорят, что он от неё зависит.

Статически подключаемые библиотеки должны быть доступны в момент компиляции. Программа

компоновщик

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

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

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

Запущенное приложение может использовать несколько алгоритмов в ходе своей работы. Некоторые из них могут выполняться параллельно (так же как процессы в многозадачной ОС).

Поток

(thread) – это часть машинного кода процесса, которая может выполняться независимо от других частей. Потоки взаимодействуют друг с другом (обмениваются информацией) через разделяемые ресурсы, например файл или область RAM. За выбор потока для исполнения в данный момент отвечает уже знакомый нам планировщик ОС. Как правило, число одновременно работающих потоков определяется числом ядер процессора. Но есть технологии (например hyper-threading от Intel), позволяющие более эффективно использовать мощности процессора и исполнять сразу два потока на одном ядре.

Иллюстрация 3-2 демонстрирует, что модули процесса могут содержать несколько потоков, а могут не содержать ни одного. EXE модуль всегда имеет главный поток (main thread), который первым получает управление при старте приложения.

Рассмотрим структуру памяти типичного процесса. Иллюстрация 3-3 демонстрирует адресное пространство процесса, состоящего из двух модулей: EXE и DLL библиотеки. Адресное пространство – это множество всех доступных процессу адресов памяти. Оно разделено на блоки, называемые сегментами. У каждого из них есть базовый адрес, длина и набор прав доступа (на запись, чтение и исполнение). Разделение на сегменты упрощает задачу контроля доступа к памяти. С их помощью ОС может оперировать блоками памяти, а не отдельными адресами.

Иллюстрация 3-3. Адресное пространство типичного процесса

Процесс на иллюстрации 3-3 имеет три потока (включая главный). У каждого потока есть свой сегмент стека. Стек – это область памяти, организованная по принципу “последним пришёл — первым вышел” (“last in — first out” или LIFO). Она инициализируется ОС при старте приложения и используется для хранения переменных и вызова функций. В стеке сохраняется адрес инструкции, следующей за вызовом. После возврата из функции процесс продолжает свое выполнение с этой инструкции. Также через стек передаются входные параметры функций.

Кроме сегментов стека, у процесса есть несколько сегментов динамической памяти (heap), к которым имеет доступ каждый поток.

У всех модулей процесса есть обязательные сегменты: .text, .data и .bss. Кроме обязательных могут быть и дополнительные сегменты (например .rsrc). Они не представлены на схеме 3-3.

Таблица 3-1 кратко описывает каждый сегмент из иллюстрации 3-3. Во втором столбце приведены их обозначения в отладчике OllyDbg.

Таблица 3-1. Описание сегментов

Сегмент

Обозначение в OllyDbg

Описание

Стек главного потока

Stack of main thread

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

Динамическая память ID 1

Heap

Дополнительный сегмент памяти, который создаётся при переполнении сегмента динамической памяти ID 0.

Динамическая память ID 0

Default heap

ОС всегда создает этот сегмент при запуске процесса. Он используется по умолчанию для хранения переменных.

Стек потока 2

Stack of thread 2

Выполняет те же функции, что и стек главного потока, но используется только потоком 2.

.text EXE модуля

Code

Содержит машинный код модуля EXE.

.data EXE модуля

Data

Содержит статические и не константные глобальные переменные модуля EXE, которые инициализируются значениями при создании.

.bss EXE модуля

Содержит статические и не константные глобальные переменные модуля EXE, которые не инициализируются при создании.

Стек потока 3

Stack of thread 2

То же самое, что и стек потока 2, только используется потоком 3.

Динамическая память ID 2

Дополнительный сегмент памяти, расширяющий сегмент динамической памяти ID 1 при его переполнении.

.text DLL модуля

Code

Содержит машинный код модуля DLL.

.data DLL модуля

Data

Содержит статические и не константные глобальные переменные модуля DLL, которые инициализируются значениями при создании.

.bss DLL модуля

Содержит статические и не константные глобальные переменные модуля DLL, которые не инициализируются при создании.

Динамическая память ID 3

Дополнительный сегмент памяти, расширяющий сегмент динамической памяти ID 2 при его переполнении.

TEB потока 3

Data block of thread 3

Содержит блок информации о потоке (Thread Information Block или TIB), также известный как блок контекста потока (Thread Environment Block или TEB). Он представляет собой структуру с информацией о потоке 3.

TEB потока 2

Data block of thread 2

Содержит TEB структуру потока 2.

TEB главного потока

Data block of main thread

Содержит TEB структуру главного потока.

PEB

Process Environment Block

Содержит блок контекста процесса (Process Environment Block или PEB). Эта структура данных с информацией о процессе в целом.

Пользовательские данные

User Share Data

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

Память ядра

Kernel memory

Область памяти, зарезервированная для нужд ОС.

Предположим, что на иллюстрации 3-3 приведено адресное пространство процесса игрового приложения. В этом случае состояние игровых объектов может находится в сегментах, отмеченных красным цветом.

ОС назначает базовые адреса этих сегментов в момент старта приложения. Эти адреса могут отличаться от запуска к запуску. Кроме того, последовательность сегментов в памяти может также меняться. В то же время некоторые из сегментов, отмеченных синим цветом на иллюстрации 3-3 (например PEB, User Share Data и Kernel memory), имеют неизменный адрес при каждом старте приложения.

Отладчик OllyDbg позволяет прочитать структуру памяти (memory map) запущенного процесса. Иллюстрации 3-4 и 3-5 демонстрируют вывод OllyDbg для приложения, адресное пространство которого приведено на схеме 3-3.

Структура памяти процесса в OllyDbg

Иллюстрация 3-4. Структура памяти процесса в OllyDbg

Структура памяти процесса в OllyDbg

Иллюстрация 3-5. Структура памяти процесса в OllyDbg (продолжение)

Таблица 3-2 демонстрирует соответствие между схемой 3-3 и сегментами настоящего процесса из иллюстраций 3-4 и 3-5.

Таблица 3-2. Сегменты процесса

Базовый адрес

Сегмент

Обозначение в OllyDbg

001ED000

Стек главного потока

Stack of main thread

004F0000

Динамическая память ID 1

Heap

00530000

Динамическая память ID 0

Default heap

00ACF000
00D3E000
0227F000

Стеки вспомогательных потоков

Stack of thread N

00D50000-00D6E000

Сегменты EXE модуля “ConsoleApplication1”

02280000-0BB40000
0F230000-2BC70000

Дополнительные сегменты динамической памяти

0F0B0000-0F217000

Сегменты DLL модуля “ucrtbased”

7EFAF000
7EFD7000
7EFDA000

TEB вспомогательных потоков

Data block of thread N

7EFDD000

TEB главного потока

Data block of main thread

7EFDE000

PEB главного потока

Process Environment Block

7FFE0000

Пользовательские данные

User shared data

80000000

Память ядра

Kernel memory

Возможно, вы обратили внимание, что OllyDbg не может автоматически идентифицировать все сегменты динамической памяти. С этой задачей лучше справляются отладчик WinDbg и инструмент HeapMemView.

Поиск переменной в памяти

Внутриигровые боты читают состояния объектов из памяти процесса игрового приложения. Эти состояния могут храниться в нескольких переменных, находящихся в разных сегментах. Базовые адреса этих сегментов и смещение переменных внутри них могут меняться от запуска к запуску. Это означает, что абсолютные адреса переменных непостоянны. К сожалению, бот может читать данные из памяти только по абсолютным адресам. Следовательно, он должен уметь искать нужные ему переменные самостоятельно.

Термин “абсолютный адрес” неточен, если мы говорим о

модели сегментации памяти x86

. x86 – это архитектура процессора, впервые реализованная компанией Intel. Сегодня практически все настольные компьютеры имеют процессоры этой архитектуры. Правильный термин, который следует употреблять – “линейный адрес”. Он вычисляется по следующей формуле:

линейный адрес = базовый адрес сегмента + смещение в сегменте

В этой главе мы продолжим использовать термин “абсолютный адрес”, поскольку он интуитивно понятен.

Задачу поиска переменной в памяти процесса можно разделить на три этапа. В результате получится следующий алгоритм:

  1. 1.

    Найти сегмент, который содержит искомую переменную.

  2. 2.

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

  3. 3.

    Определить смещение переменной внутри сегмента.

Очень высока вероятность того, что переменная будет храниться в одном и том же сегменте при каждом старте приложения. Это правило не выполняется для сегментов динамической памяти, что связано с особенностью её организации. Если мы установили, что переменная не находится в сегменте динамической памяти, первый шаг алгоритма может быть выполнен вручную. Полученный результат можно закодировать в боте без каких-либо дополнительных условий и проверок. В противном случае бот должен искать сегмент самостоятельно.

Второй шаг алгоритма бот должен всегда выполнять сам. Как мы упоминали ранее, адреса сегментов меняются при старте приложения.

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

Таблица 3-3. Смещение переменных в различных типах сегментов

Смещение переменной не меняется при перезапуске приложения.

В большинстве случаев смещение переменной не меняется. Но оно зависит от порядка выполнения инструкций (control flow). Если этот порядок меняется, смещение, скорее всего, тоже изменится.

Смещение переменной меняется при перезапуске приложения.

Поиск переменной в 32-битном приложении

Применим алгоритм поиска переменной на практике. Выполним все его шаги вручную для приложения ColorPix, которым мы пользовались в прошлой главе для чтения цветов и координат пикселей экрана. Это поможет лучше понять и запомнить все необходимые действия.

Приложение ColorPix является 32-битным. Скриншот его окна приведён на иллюстрации 3-6. Попробуем найти в памяти переменную, которая соответствует координате X выделенного на экране пикселя. На иллюстрации 3-6 она подчеркнута красной линией.

Иллюстрация 3-6. Окно приложения ColorPix

В ходе дальнейших действий вы не должны закрывать уже запущенное приложение ColorPix. Иначе, вам придется начать поиск переменной сначала.

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

  1. 1.

    Найти абсолютный адрес переменной с помощью сканера памяти Cheat Engine.

  2. 2.

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

Чтобы найти переменную с помощью Cheat Engine, выполните следующие действия:

  1. 1.

    Запустите 32-битную версию сканера с правами администратора.

  2. 2.

    Выберите пункт главного меню “File” ➤ “Open Process”. Вы увидите диалог со списком запущенных процессов (см. иллюстрацию 3-7).

Диалог выбора процесса Cheat Engine

Иллюстрация 3-7. Диалог выбора процесса Cheat Engine

  1. 1.

    Выберите процесс с именем “ColorPixel.exe” и нажмите кнопку “Open”. В результате имя этого процесса отобразится в верхней части окна Cheat Engine.

  2. 2.

    Введите значение координаты X, которое вы видите в данный момент в окне ColorPixel, в поле “Value” окна Cheat Engine.

  3. 3.

    Нажмите кнопку “First Scan”, чтобы найти абсолютный адрес указанного значения координаты X в памяти процесса ColorPixel.

Когда вы нажимаете кнопку “First Scan”, значение в поле “Value” окна Cheat Engine, должно соответствовать тому, что отображает ColorPixel. Координата X изменится, если вы переместите курсор мыши по экрану, поэтому нажать на кнопку будет затруднительно. Воспользуйтесь комбинацией клавиш Shift+Tab, чтобы переключиться на неё и Enter, чтобы нажать.

В левой части окна Cheat Engine вы увидите результаты поиска, как на иллюстрации 3-8.

Результаты поиска Cheat Engine

Иллюстрация 3-8. Результаты поиска в окне Cheat Engine

Если в момент сканирования процесса несколько переменных имеют то же самое значение что и координата X, найденных переменных будет больше чем две. В этом случае вам надо отфильтровать ошибочные результаты. Для этого выполните следующие шаги:

  1. 1.

    Переместите курсор мыши, чтобы значение координаты X в окне ColorPixel изменилось.

  2. 2.

    Введите новую координату X в поле “Value” окна Cheat Engine.

  3. 3.

    Нажмите кнопку “Next Scan”.

После этого в окне результатов должны остаться только две переменные, как на иллюстрации 3-8. В моём случае их абсолютные адреса равны 0018FF38 и 0025246C. У вас они могут отличаться, но это не существенно для нашего примера.

Мы нашли абсолютные адреса двух переменных, хранящих значение координаты X. Теперь определим сегменты, в которых они находятся. Для этой цели воспользуемся отладчиком OllyDbg. Для поиска сегментов выполните следующие шаги:

  1. 1.

    Запустите отладчик OllyDbg с правами администратора. Путь к нему по умолчанию:
    C:Program Files (x86)odbg201ollydbg.exe.

  2. 2.

    Выберите пункт главного меню “File” ➤ “Attach”. Вы увидите диалог со списком запущенных 32-битных процессов (см. иллюстрацию 3-9).

Диалог выбора процесса OllyDbg

Иллюстрация 3-9. Диалог выбора процесса в отладчике OllyDbg

  1. 1.

    Выберите процесс “ColorPix” в списке и нажмите кнопку “Attach”. Когда отладчик подключится к нему, вы увидите состояние “Paused” в правом нижнем углу окна OllyDbg.

  2. 2.

    Нажмите комбинацию клавиш Alt+M, чтобы открыть окно, отображающее структуру памяти процесса ColorPix. Это окно “Memory Map” приведено на иллюстрации 3-10.

Иллюстрация 3-10. Окно “Memory Map” со структурой памяти процесса

Переменная с абсолютным адресом 0018FF38 хранится в сегменте стека главного процесса (“Stack of main thread”), который занимает адреса с 0017F000 по 00190000.

OllyDbg отображает только адрес начала сегмента и его размер. Чтобы вычислить конечный адрес, вы должны сложить два эти числа. Результат будет равен адресу начала следующего сегмента.

Вторая найденная нами переменная с адресом 0025246C находится в сегменте с базовым адресом 00250000, тип которого неизвестен. Найти его будет труднее чем сегмент стека. Поэтому мы продолжим работу с первой переменной.

Последний шаг поиска – расчёт смещения переменной в сегменте стека. Стек в архитектуре x86 растёт вниз. Это означает, что он начинается с больших адресов и расширяется в сторону меньших. Следовательно, базовый адрес стека равен его верхней границе (в нашем случае это 00190000). Нижняя границе стека может меняться по ходу его увеличения.

Смещение переменной равно разности базового адреса сегмента, в котором она находится, и её абсолютного адреса. В нашем случае мы получим:

Для сегментов динамической памяти, .bss и .data это вычисление выглядело бы иначе. Все они растут вверх (в сторону больших адресов), поэтому их базовый адрес соответствует нижней границе.

Теперь у нас есть вся необходимая информация, чтобы найти и прочитать координату X в любом запущенном процессе ColorPix. Алгоритм бота, который бы это делал, выглядит следующим образом:

  1. 1.

    Прочитать базовый адрес сегмента стека главного потока. Этот адрес хранится в TEB сегменте.

  2. 2.

    Вычесть смещение переменной (всегда равное C8) из базового адреса сегмента стека. В результате получим её абсолютный адрес.

  3. 3.

    Прочитать значение переменной из памяти процесса ColorPix по её абсолютному адресу.

Корректность первого шага алгоритма мы можем проверить вручную с помощью отладчика OllyDbg. Он позволяет прочитать информацию сегмента TEB в удобном виде. Для этого дважды щелкните по сегменту, который называется “Data block of main thread”, в окне “Memory Map” отладчика. Вы увидите окно как на иллюстрации 3-11.

Иллюстрация 3-11. Окно OllyDbg с информацией TEB

Базовый адрес сегмента стека 00190000 указан во второй строчке открывшегося окна. Учтите, что этот адрес может меняться при каждом запуске приложения.

Поиск переменной в 64-битном приложении

Применим наш алгоритм поиска переменной для 64-битного приложения.

Отладчик OllyDbg не поддерживает 64-битные приложения, поэтому вместо него воспользуемся WinDbg.

Resource Monitor (монитор ресурсов) Windows 7 будет нашим приложением для анализа. Он распространяется вместе с ОС и доступен сразу после её установки. Разрядность Resource Monitor совпадает с разрядностью Windows. Чтобы запустить приложение, откройте меню Пуск (Start) Windows и введите следующую команду в строку поиска:

Иллюстрации 3-12 демонстрирует окно Resource Monitor.

Иллюстрация 3-12. Окно приложения Resource Monitor

Найдём переменную, хранящую размер свободной памяти системы. На иллюстрации её значение подчёркнуто красной линией.

Прежде всего найдём сегмент, содержащий искомую переменную. Для этого воспользуемся 64-битной версией сканера Cheat Engine. Интерфейс его 64 и 34-битных версий одинаков, поэтому вам нужно выполнить те же действия, что и при анализе приложения ColorPixel.

В моем случае сканер нашёл две переменные с адресами 00432FEC и 00433010. Определим сегменты, в которых они хранятся. Чтобы прочитать структуру памяти процесса с помощью отладчика WinDbg, выполните следующие действия:

  1. 1.

    Запустите 64-битную версию WinDbg с правами администратора. Путь к нему по умолчанию:
    C:Program Files (x86)Windows ­Kits8.1Debuggersx64windbg.exe.

  2. 2.

    Выберите пункт главного меню “File” ➤ “Attach to a Process…”. Откроется окно диалога со списком запущенных 64-разрядных процессов, как на иллюстрации 3-13.

Диалог выбора процесса WinDbg

Иллюстрация 3-13. Диалог выбора процесса в отладчике WinDbg

  1. 1.

    Выберите в списке процесс “perfmon.exe” и нажмите кнопку “OK”.

  2. 2.

    В командной строке отладчика, расположенной в нижней части окна “Command”, введите текст !address и нажмите Enter. Структура памяти процесса отобразится в окне “Command”, как на иллюстрации 3-14.

Структура памяти в WinDbg

Иллюстрация 3-14. Вывод структуры памяти процесса в окне “Command”

Обе переменные с абсолютными адресами 00432FEC и 00433010 находятся в сегменте динамической памяти с ID 2. Границы этого сегмента: с 003E0000 по 00447000. Смещение первой переменной в сегменте равно 52FEC:

00432FEC – 003E0000 = 52FEC

Для бота алгоритм поиска переменной, хранящей размер свободной памяти ОС в приложении Resource Monitor, выглядит следующим образом:

  1. 1.

    Прочитать базовый адрес сегмента динамической памяти с ID 2. Чтобы получить доступ к этим сегментам, надо воспользоваться следующими WinAPI функциями:

  2. 2.

    Добавить смещение переменной (в моем случае равное 52FEC) к базовому адресу сегмента. В результате получится её абсолютный адрес.

  3. 3.

    Прочитать значение переменной из памяти процесса.

Как вы помните, смещение переменной в сегменте динамической памяти обычно меняется при перезапуске приложения. В случае если приложение достаточно простое (как рассматриваемый нами Resource Monitor), порядок выделения динамической памяти может быть одним и тем же при каждом старте программы.

Попробуйте перезапустить Resource Monitor и найти переменную еще раз. Вы получите то же самое её смещение в сегменте, равное 52FEC.

Мы рассмотрели адресное пространство Windows процесса. Затем составили алгоритм поиска переменной в памяти и применили его к 32 и 64-разрядному приложениям. В ходе этого мы познакомились с функциями отладчиков OllyDbg и WinDbg для анализа структуры памяти процесса.

How do I view the environment variable of a Windows process? Looking for Windows equivalent for something like environ file in procfs on Unix.

phuclv's user avatar

phuclv

36.9k14 gold badges151 silver badges462 bronze badges

asked Feb 13, 2010 at 0:24

theactiveactor's user avatar

theactiveactortheactiveactor

7,25415 gold badges52 silver badges60 bronze badges

1

Process Explorer or one of its friends should help.

answered Feb 13, 2010 at 0:29

ziya's user avatar

7

phuclv's user avatar

phuclv

36.9k14 gold badges151 silver badges462 bronze badges

answered Feb 13, 2010 at 0:29

John Feminella's user avatar

John FeminellaJohn Feminella

302k45 gold badges338 silver badges357 bronze badges

1

As ziya suggests, you can use the SysInternal’s Process Explorer or, for example, slighly more feature rich Process Hacker.

Even though it’s really trivial, I’ll describe how to find the environment variables used by a specific process in any of those two applications:

  1. in the list of running processes, select the process you’re interested in (in Process Hacker or Process Explorer 16.40 and newer, you can use the search box on the main toolbar)
  2. open the process’ Properties dialog (double-click, press Enter or click Properties in the contect menu)
  3. open the Environment tab

Process Explorer is displaying all environment variables in a single list, but Process Hacker organizes them into three sections by their scope:

  1. Process: environment variables specific to the current process (e.g. passed from the command line or defined during the process creation
  2. User: environment variables defined for the user who owns the process
  3. System: system-wide environment variables shared by all processes

answered Jun 12, 2021 at 22:47

David Ferenczy Rogožan's user avatar

Take a look at the _environ variable.

phuclv's user avatar

phuclv

36.9k14 gold badges151 silver badges462 bronze badges

answered Feb 13, 2010 at 0:30

To get process environment, you must be able to obtain its context. And within the context, use getenv() to get specific variable.

answered Feb 13, 2010 at 0:29

alemjerus's user avatar

alemjerusalemjerus

7,9533 gold badges32 silver badges40 bronze badges

Время на прочтение
4 мин

Количество просмотров 26K

Часто бывает так, что приходишь на машину и обнаруживаешь какой-то скрипт, запущенный под системным пользователем неделю назад. Кто его запустил? Где искать этот run.php? Или добавляешь запись в /etc/crontab, а скрипт там падает с ошибкой «command not found». Почему? И что делать? 

У меня есть ответы на эти вопросы.

Переменные окружения

Практически во всех современных операционных системах у процессов существуют переменные окружения. Технически они представляют собой набор именованных строк. Если запускается подпроцесс, то он автоматически наследует копию окружения родителя. 

Среди прочих есть переменная PATH, которая указывает пути для поиска исполняемых файлов, переменная HOME, которая указывает на домашнюю директорию пользователя, переменные, отвечающие за языковые предпочтения пользователя, и многие другие. 

Есть множество обзоров, описывающих значения этих переменных, а вот статей о том, как расследовать проблемы, практически нет. Восполним этот пробел.

Кто запустил процесс?

Итак, мы обнаружили скрипт, запущенный под системным пользователем неделю назад. Кто его запустил? Зачем? Может, про него просто забыли? Запустить его потенциально могли человек 10–15, всех не опросишь. Как найти, кто же это был? И где лежит этот run.php?

$ ps x  | grep run.php
10684 ?    	Ss   472:25 /local/php/bin/php run.php 

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

Переменные окружения (и многое другое) для любого запущенного процесса можно посмотреть в /proc. Вуаля:

$ cat /proc/10684/environ | tr '' 'n' | grep SUDO_USER
SUDO_USER=alexxz
$ cat /proc/10684/environ | tr '' 'n' | grep PWD
PWD=/home/etlmaster

Кхм, сам и запустил. Ну с кем не бывает?.. 

В общем, вот таким нехитрым методом в простых ситуациях можно найти информацию о процессе, которая в общем случае недоступна.

Скрипт работает из командной строки, но не работает из cron

Одним из случаев, когда приходится вспоминать о переменных окружения, является ситуация, когда добавленный в /etc/crontab скрипт падает с ошибкой. Заходишь на сервер по SSH, запускаешь команду, всё вроде работает как надо. А при автоматическом запуске показывает что-то типа «hive: command not found». 

Вообще есть хорошая практика прописывать полный путь до исполняемых команд, однако это не всегда возможно. В таких случаях разработчики выкручиваются кто как может. Кто-то добавляет нужный путь в PATH частью команды в кронтабе. Более опытные оборачивают свою команду в bash -l. А наученные горьким опытом крон-бомбы ещё и flock довернуть не забывают. Всё так: сделал, добавил в мониторинг и забыл.

После таких манипуляций в душе настоящего инженера остаётся некий осадочек. Да, задача решена. Но я же ни фига не понял, что происходит! Чем один подход лучше другого? Где все эти настройки хранятся и кем меняются?

Давайте сравним переменные окружения, которые есть у процесса, когда он запускается из крона, и переменные окружения, которые есть у нас в командной строке. Логируем вывод команды env из крона и своё текущее окружение:

$ echo "* * * * * env > ~/crontab.env" | crontab; sleep 60; echo "" | crontab;
$ env > my.env

Смотрим, что там в переменной PATH:

> grep ^PATH= crontab.env my.env
Crontab.env:
PATH=/usr/bin:/bin
My.env:
PATH=/local/hive/bin:/local/python/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hive/bin:/local/hadoop/bin:/usr/local/bin:/usr/bin:/bin

Мама мия! Так там под кроном только самый минимум! Конечно же, надо подгружать нормальные переменные окружения. 

Давайте посмотрим, какое окружение будет, если добавить bash -l:

$ echo "* * * * * bash -l env > ~/crontab.env" | crontab; sleep 60; echo "" | crontab;

alexxz@bi1.mlan:~> grep ^PATH= crontab.env my.env
Crontab.env:
PATH=/local/hive/bin:/local/python/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hive/bin:/usr/local/bin:/usr/bin:/bin
My.env:
PATH=/local/hive/bin:/local/python/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hive/bin:/local/hadoop/bin:/usr/local/bin:/usr/bin:/bin

Разница уже не так заметна. Все пути представлены. Некоторые в другом порядке, некоторые повторяются, но это уже намного лучше, чем было. Остальные переменные тоже неплохо настроены. Есть, конечно, небольшая разница в локали, в переменных от SSH, но это уже не должно драматично влиять на работу скрипта. 

Теперь понятно, почему bash -l просто необходим в crontab-записях. И, конечно же, не забываем про flock.

Отлаживаем инициализацию логин-скриптов

Проблема вроде бы решена, всё из крона работает. Но как же получается, что некоторые пути дублируются в переменной PATH? Значит, есть какой-то беспорядок в настройке сервера. Давайте попробуем разобраться. 

Открываем какой-нибудь ман по инициализации окружения, вычитываем, какие скрипты и в каком порядке выполняются, с воодушевлением начинаем пробегать их глазами — и через несколько минут приходит чувство отчаяния. Какой-то бесконечный поток условий про какие-то особые случаи архитектур, терминалов и невероятно важных настроек цветов для команды ls. Боль, отчаяние, ненависть! Нас интересует одна чёртова переменная PATH!

На самом деле всё несколько проще. Знакомьтесь:

env -i bash -x -l -c 'echo 123' > login.log 2>&1

Что делает эта команда? Создаёт новый процесс bash с девственно чистым окружением, указывает, что надо запустить скрипты инициализации и всё подробно залогировать в файле login.log. Теперь у нас есть возможность не выполнять в уме все скрипты, а просто прочитать, что, где и когда выполнилось и откуда появилась та или иная настройка окружения.

Я не буду детально разбирать, как читать получившийся лог. Там всё почти тривиально. Упомяну лишь, что одно попадание у меня оказалось из /etc/profile и два — из /etc/bash.bashrc. Да, где-то перемудрили при настройке пакетов в паппете. Ну ничего, мне работать это не мешает.

Зато теперь я знаю и умею!

P. S. В совсем сложных случаях и чтобы разобраться вообще во всём, можно обернуть команду в strace:

 strace -f env -i bash -x -l -c 'echo 123' > login.log 2>&1

Статья написана студентом Хекслета. Мнение автора может не совпадать с позицией редакции

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

  • Введение
  • Как работает окружение и его переменные
  • Вывод переменных оболочки и окружения
    • Общие переменные окружения и консольной оболочки
  • Установка переменных окружения и оболочки
    • Создание переменной оболочки
    • Создание переменной окружения
  • Отвязка переменный
  • Установка переменных окружения по логину
    • Различие между логин, не-логин, интерактивной и не-интерактивной сессией оболочки
    • Реализация переменных окружения
  • Заключение

Введение

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

Место, в котором оболочка держит пути всех этих настроек, называется окружением. Окружение — это место, которое оболочка определяет каждый раз, когда запускается сессия. В ней заключены переменные, созданные свойствами системы.

Как работает окружение и его переменные

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

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

KEY=value1:value2:...

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

KEY="value with spaces"

Ключами в данных примерах являются переменные. Они могут быть одним из двух типов: переменной окружения и переменной оболочки.

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

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

По правилам — эти типы переменных определяются, используя заглавные символы. Эта особенность помогает пользователям определять контекст употребления переменных.

Вывод переменных оболочки и окружения

Каждая сессия оболочки хранит пути своих собственных переменных и переменных окружения. Их достигнуть можно разными способами.

Можно распечатать список всех переменных окружения используя env или printenv. По умолчанию они должны делать все то же самое:

printenv
XDG_GREETER_DATA_DIR=/var/lib/lightdm-data/rgt
USER=rgt
DESKTOP_SESSION=cinnamon
GNOME_TERMINAL_SCREEN=/org/gnome/Terminal/screen/782e62a7_614a_44c6_a444_cecbdf372225
DEFAULTS_PATH=/usr/share/gconf/cinnamon.default.path
QT_QPA_PLATFORMTHEME=qt5ct
PWD=/home/rgt
HOME=/home/rgt
SSH_AGENT_PID=1365
QT_ACCESSIBILITY=1
XDG_SESSION_TYPE=x11
XDG_DATA_DIRS=/usr/share/cinnamon:/usr/share/gnome:/home/rgt/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share
NVM_IOJS_ORG_MIRROR=https://iojs.org/dist
XDG_SESSION_DESKTOP=cinnamon
LC_ADDRESS=ru_RU.UTF-8
LC_NUMERIC=ru_RU.UTF-8
GTK_MODULES=gail:atk-bridge
PAPERSIZE=a4
TERM=xterm-256color
SHELL=/bin/bash
VTE_VERSION=5202
XDG_SEAT_PATH=/org/freedesktop/DisplayManager/Seat0
XDG_CURRENT_DESKTOP=X-Cinnamon
GPG_AGENT_INFO=/run/user/1000/gnupg/S.gpg-agent:0:1
GNOME_TERMINAL_SERVICE=:1.124
NVM_NODEJS_ORG_MIRROR=https://nodejs.org/dist
XDG_SEAT=seat0
SHLVL=1
LANGUAGE=en_US
LC_TELEPHONE=ru_RU.UTF-8
GDMSESSION=cinnamon
GNOME_DESKTOP_SESSION_ID=this-is-deprecated
LOGNAME=rgt
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
XDG_RUNTIME_DIR=/run/user/1000
XAUTHORITY=/home/rgt/.Xauthority
XDG_SESSION_PATH=/org/freedesktop/DisplayManager/Session0
XDG_CONFIG_DIRS=/etc/xdg/xdg-cinnamon:/etc/xdg
PATH=/home/rgt/.gems/bin:/home/rgt/.npm-global-modules/bin:/home/rgt/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/elixir/bin:/home/rgt/.racket/bin
LC_IDENTIFICATION=ru_RU.UTF-8
PS1=$ 
SESSION_MANAGER=local/rgt-laptop:@/tmp/.ICE-unix/1301,unix/rgt-laptop:/tmp/.ICE-unix/1301
LESSOPEN=| /usr/bin/lesspipe %s
LC_TIME=en_US.UTF-8
_=/usr/bin/printenv

Как правило, этот вывод довольно типичен для обоих команд. Различия между ними в функциональности. Например, в printenv можно запрашивать значения определенной переменной.

$ printenv SHELL
/bin/bash
$ printenv USER
rgt

С другой стороны env позволяет изменять окружение, указывая определение переменной. Например:

env VAR1="value VAR1" command_to_run command_option

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

Если посмотреть на вывод команды printenv, то там есть несколько переменных окружения, которые установлены через нашу файловую систему и процессов без нашего ввода.

Это были переменные окружения, но выглядят как переменные консольной оболочки.

Существует команда set, которую можно использовать для отображения. Если ввести set без дополнительных параметров, будет выводиться список всех переменных консольной оболочки, переменные окружения, локальные переменные и функции оболочки:

$ set | head
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:complete_fullquote:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_COMPLETION_VERSINFO=([0]="2" [1]="8")
BASH_LINENO=()
BASH_SOURCE=()
BASH_VERSINFO=([0]="4" [1]="4" [2]="19" [3]="1" [4]="release" [5]="x86_64-pc-linux-gnu")

Вывод этой команды довольно огромен. Для удобства работы с этими переменными можно использовать пейджеры more или less.

В принципе, отсутствует особая необходимость знать все функции, определенные для bash. Поэтому — можно очистить вывод, указав, что set должен проводить операции в режиме POSIX, которые опускают отображение функций консольной оболочки. Это можно выполнить в дочерней оболочке, чтобы наше текущее окружение оставалось прежним:

(set -o posix; set)

Возникнет список всех определенных переменных окружения и оболочки.

Можно попробовать сравнить этот вывод с выводом команд env или printenv, тем самым получив список только переменных оболочки. Возможно, вывод будет довольно кривым:

comm -23 <(set -o posix; set | sort) <(env | sort)

Отображение будет до сих пор включать несколько переменных окружения, в связи с тем фактом, что set команда выводит значения в кавычках, в то время как команды printenv и env показывают значения, опуская их.

Все это должно давать правильное понимание переменных окружения и оболочки.

Эти переменные используются для всех необходимых вещей. Они обеспечивают альтернативный способ определения постоянных значений для сессии — между процессами, тем самым избегая изменений в файле.

Общие переменные окружения и консольной оболочки

Некоторые переменные окружения и оболочки очень полезны и к ним часто обращаются.
Здесь перечислены общие переменные окружения, которые зачастую используются:

  • SHELL: отображает путь к оболочке, которая занимается интерпретацией тех или иных команд, которые вводит пользователь. Как правило, по умолчанию, это будет интерпретатор bash, но возможно и применение других значений, которые можно указать.

  • TERM: указывает тип терминала, который эмулируется, когда запускается консольная оболочка. Могут эмулироваться различные терминалы для необходимых операционных требований.

  • USER: имя текущего пользователя оболочки.

  • PWD: переменная отображает значение текущей рабочей директории.

  • OLDPWD: предыдущая рабочая директория. Оболочка держит эту переменную для обратного переключения, когда запускается команда cd -.

  • LS_COLORS: определяет цвет кодов, которые используются для вывода команды ls. Чтобы различать типы файлов и директорий.

  • MAIL: отображает электронную почту текущего пользователя.

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

  • LANG: текущий язык и локализационные настройки, включая кодировку символов.

  • HOME: текущая домашняя директория пользователя.

  • _: отображает вывод последней запущенной команды.

В дополнение к этим переменным окружения, имеются некоторые переменные оболочки, которые также будут часто встречаться:

  • BASHOPTS: список опций, которые используются при запуске bash. Это может быть полезно для выяснения, каким конкретно способом будет оперировать консольная оболочка.

  • BASH_VERSION: отображает конкретную версию bash.

  • BASH_VERSINFO: мажорная версия bash.

  • COLUMNS: число колон, используемых для отображения вывода в оболочке.

  • DIRSTACK: директории, которые доступны для команд pushd и popd.

  • HISTFILESIZE: количество линий, хранящиеся в файле истории набранных команд.

  • HISTSIZE: количество линий, которые можно хранить в памяти.

  • HOSTNAME: имя компьютера системы на данный момент.

  • IFS: внутренний разделитель поля в командной строке. По умолчанию — это пробел.

  • PS1: строка приглашения на ввод. Эта переменная используется для того, чтобы определить, как будет выглядеть эта строка. Еще есть вторая строка PS2 которая используется, когда команда многострочная.

  • SHELLOPTS: опции оболочки, которые можно определять с помощью команды set.

  • UID: идентификатор текущего пользователя.

Установка переменных окружения и оболочки

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

Создание переменной оболочки

Определение этой переменной будет происходить в пределах текущей сессии консольной оболочки.

$ TEST_VAR='Hello World!'

Кавычки здесь используются, поскольку значение переменной содержит пробел. В дальнейшем будут использоваться одинарные кавычки — это позволит сделать так, что особые знаки в bash будут правильно отображаться.

Определенная выше переменная теперь доступна в текущей сессии, но в дочернем процессе она будет отсутствовать. Увидеть ее можно при помощи команды grep на вывод set:

$ set | grep TEST_VAR
TEST_VAR='Hello World!'
$ bash                # создание новой дочерней сессии
$ set | grep TEST_VAR # делаем поиск переменной
$                     # вывод пуст

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

$ printenv | grep TEST_VAR
$ # вывод пуст

Чтобы получить значение той или иной переменной, используется следующая конструкция:

$ echo $TEST_VAR
Hello World!

Команда echo делает вывод введенной строки как аргумента. Но, в данном случае применяется знак $, в совокупности с ключом переменной, который возвращает ее значение.

Создание переменной окружения

Чтобы обернуть созданную переменную оболочки в переменную окружения, можно ее экспортировать:

$ export TEST_VAR
$ printenv | grep TEST_VAR
TEST_VAR=Hello World!

Если создать новую дочернюю сессию, то можно увидеть, что в ней также существует эта переменная:

$ bash
$ echo $TEST_VAR
Hello World!

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

$ export NEW_VAR="Testing export"
$ printenv | grep NEW_VAR
NEW_VAR="Testing export"
$ exit # делаем выход из текущего процесса и возвращаемся в родительский
$ echo $NEW_VAR
$ # ничего

Переменные окружения проходят только в дочерние процессы. Переменная NEW_VAR была определена в дочернем окружении переменных. Она была бы также доступна любым другим дочерним сессиям от этой текущей. Вернувшись обратно — в основную сессию, — через команду exit, предыдущее окружение вместе с этой переменной было стерто.

Отвязка переменный

На данный момент переменная TEST_VAR находится в окружении. По необходимости можно делать отвязку переменных.

$ export -n TEST_VAR

Теперь, если посмотреть в окружение, то удаленная переменная там будет отсутствовать.

$ printenv | grep TEST_VAR

При этом она до сих пор существует в переменной оболочки:

$ set | grep TEST_VAR
TEST_VAR='Hello World!'

Если возникает потребность окончательно сделать отвязку переменной из оболочки и окружения, то можно применить команду unset:

$ unset TEST_VAR

Проверить результат команды можно с помощью echo:

$ echo $TEST_VAR

Отсутствует какое-либо возвращаемое значение, потому что переменная была отвязана.

Установка переменных окружения по логину

Как упоминалось выше, множество программ используют переменные окружения, чтобы решать, как конкретно производить операции. И вместо того, чтобы каждый раз делать вручную определение всех переменных, лучше определять переменные автоматически.

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

Различие между логин, не-логин, интерактивной и не-интерактивной сессией оболочки

Bash оболочка читает различные конфигурационные файлы в зависимости от того, какая сессия запущена.

Логин-сессия оболочки запускается по аутентификации пользователя. Если пользователь входит в сессию терминала или через SSH и аутентифицируется, тогда эта сессия оболочки будет определена как логин-оболочка.

Если начинается новая сессия bash-оболочки в пределах аутентифицированной сессии, подобно вызову команды bash из консоли, тогда такая оболочка определяется без логина и сразу запускается.

Помимо этих типов сессий, существуют интерактивный и не-интерактивный подход. Первый прикрепляется к терминалу, второй же — наоборот.

Таким образом сессии оболочки могут различаться по этим двум типам.

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

В независимости от того, какая сессия определена как логин или не-логин, существуют некоторые заявления, по которым происходит инициализация сессии оболочки.

Логин сессия при запуске в первую очередь будет читать конфигурационные заявления из файла /etc/profile. Затем она будет смотреть данные в первом конфигурационном файле и пользовательской домашней директории, чтобы получить особые настройки пользователя.

Читаются исключительно файлы, которые располагаются в ~/.bash_profile, ~/.bash_login и ~/.profile.

В не-логин сессии оболочка читает файл /etc/bash.bashrc и пользовательский ~/.bashrc файл для построения своего окружения.

Не-интерактивные оболочки читают переменную окружения под названием BASH_ENV и читают указанный файл, чтобы определить новое окружение.

Реализация переменных окружения

Как можно увидеть выше, существуют различные отдельные файлы для настроек.

Это обеспечивает гибкость, которая может помочь в особых ситуациях, где будут необходимы конкретные настройки для логин и не-логин оболочки. Хотя, обычно, настройки одинаковы в обоих ситуациях.

Большинство Linux-дистрибутивов производят конфигурацию файлов к источнику не-залогиненых конфигурационных файлов. Это значит, что можно определять окружение переменных внутри них. И они будут прочитаны в обоих случаях.

Обычно в конфигурациях указываются особые переменные окружения для пользовательских нужд. Эти настройки указываются в ~/.bashrc файле.

Внутри этого файла будет уже содержаться определенные данные. Большинство из определений нужны для настройки bash-опций, которые мало связаны с окружением переменных. Переменные внутри файла устанавливаются так же, как и в командной оболочке:

export VARNAME=value

Любое новое окружение переменных может быть добавленно в этот файл. Желательно добавлять новые данные в конец файла — это позволит быстрее их найти в случае, если нужно что-либо изменить. При следующем запуске оболочки, после изменения этого файла, указанные переменные будут прочитаны и пропущены в окружение оболочки. Возможно также и текущую сессию обновить, прочитав файл, введя следующую команду:

$ source ~/.bashrc

Если необходимо установить системные переменные, то можно добавить их в /etc/profile, /etc/bash.bashrc или /etc/environment.

Заключение

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

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

Статья была переведена отсюда.

надо рыться в стэке процесса. Есть хорошие утилиты как memdump, memfetch, memgrep (в портах). Единственное, что все эти тулзы работают на FreeBSD не очень стабильно, memdump грубо говоря делает dd if=/dev/mem of=/<something> что, (видимо из-за отсутствия лока), может привести в вису. У меня например все время подвисает, наверное так ненадо делать ;)

Вот пример как можно узнать PATH через memgrep у процесса top, который я запустил:

узнать PID

Код:

ps axf |grep top
 3846   1  S+     0:00.68 top

Роемся в стеке

Код:

memgrep -p3846 -s -a stack -f s,PATH
Searching 0x7fbfe75c => 0xffffffff…
   found at 0x7fbfec6e
   found at 0x7fbfed3a
   found at 0x7fbfef19
   found at 0x7fbfef44

Теперь можно посмотреть содержимое по найденому адресу, какова длина – я точно не знаю, поэтому взял 256, на угад.

Код:

memgrep -p3846 -d -a 0x7fbfec6e  -l 256
256 bytes starting at 7fbfec6e (+/- 0) as hexint…
7fbfec6e: 48544150 62732f3d 2f3a6e69 3a6e6962 PATH=/sbin:/bin:
7fbfec7e: 7273752f 6962732f 752f3a6e 622f7273 /usr/sbin:/usr/b
7fbfec8e: 2f3a6e69 2f727375 656d6167 752f3a73 in:/usr/games:/u
7fbfec9e: 6c2f7273 6c61636f 6962732f 752f3a6e sr/local/sbin:/u
7fbfecae: 6c2f7273 6c61636f 6e69622f 6f682f3a sr/local/bin:/ho
7fbfecbe: 6f2f656d 2f67656c 3a6e6962 7273752f me/oleg/bin:/usr
7fbfecce: 636f6c2f 6b2f6c61 2f346564 3a6e6962 /local/kde4/bin:
7fbfecde: 7273752f 636f6c2f 6b2f6c61 2f346564 /usr/local/kde4/
7fbfecee: 006e6962 4c49414d 61762f3d 616d2f72 bin.MAIL=/var/ma
7fbfecfe: 6f2f6c69 0067656c 434f4c42 5a49534b il/oleg.BLOCKSIZ
7fbfed0e: 004b3d45 3d445750 6d6f682f 6c6f2f65 E=K.PWD=/home/ol
7fbfed1e: 4b006765 535f4544 49535345 555f4e4f eg.KDE_SESSION_U
7fbfed2e: 003d4449 52554358 5f524f53 48544150 ID=.XCURSOR_PATH
7fbfed3e: 73752f3d 6f6c2f72 2f6c6163 3465646b =/usr/local/kde4
7fbfed4e: 6168732f 692f6572 736e6f63 2f7e3a3a /share/icons::~/
7fbfed5e: 6f63692e 2f3a736e 2f727375 72616873 .icons:/usr/shar

… то что искомо – найдено.

Ничто собсна не мешает задампить всеадресное пространство занимаемое процессом (его найти несложно), а потом ковыряться в его шлаке через strings или hexdump -C.

Добавить комментарий