# Linux

# Разбивка диска

100 GB

5 GB /var

5 GB /tmp

1 GB swap

# Разное

Установка sudo

```
apt-get install sudo
usermod -aG sudo username
```

Шаблон /etc/network/interfaces

```
allow-hotplug enp0s3
iface enp0s3 inet static
address 192.168.1.10
gateway 192.168.1.1
netmask 255.255.255.0
dns-nameservers 8.8.8.8 8.8.4.4
```

Узнать текущий dhcp сервер:

```
sudo dhclient -v
```

Оставить только один шлюз по умолчанию при двух серверах dhcp

```
cat /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug enp0s3
iface enp0s3 inet dhcp
    post-up ip route add default via $(ip route | grep 'default via' | awk '{print $3}') dev enp0s3 2>/dev/null || true

allow-hotplug enp0s8
iface enp0s8 inet dhcp
    post-up ip route add 192.168.0.0/16 via 192.168.51.1
    post-up ip route del default dev enp0s8 2>/dev/null || true

```

Архивация папки рекурсивно

```
zip -r archive.zip myfolder/
```

Копирование файла по scp

```
scp /home/sergey/file sergey@192.168.1.10:~/
scp sergey@192.168.1.10:~/file ~/
```

Закодировать строку в Base64

```
echo "My string" | base64
```

openVPN

```
sudo openvpn <файл_конфига>
```

Временный socks5 VPN

```
ssh -D 8081 -N -f username@your-vpn-server.com
#в случае ключа
ssh -D 8081 -N -f -i ~/.ssh/your_private_key username@your-vpn-server.com
# завершение фонового процесса
ps aux | grep ssh
kill <pid>
```

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

```
sudo apt install tsocks
# в файле /etc/tsocks.conf path полностью убрал, чтобы весь трафик шел в proxy
local = 192.168.1.0/255.255.255.0
server = 127.0.0.1
server_type = 5
server_port = 1080
#
sudo -s
tsocks apt update
tsocks apt upgrade
exit
```

Авторизация по ключу

Шаг 1. Вариант 1. Клиент Windows + Putty на Linux Генерим ключ через PuTTYgen. Сохраняем закрытый ключ, для открытого ключа на сервере Шаг 2. Добавляем закрытый ключ к нужной сессии.

Шаг 1. Вариант 2. Клиент Linux. Ключи будут расположены в .ssh/ Нужен id\_rsa.pub

```
ssh-keygen -t rsa -b 4096
```

Шаг 2. На сервере.

```
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys
#вставить открытый ключ из PuttyGen
chmod 600 ~/.ssh/authorized_keys
```

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

```
sudo nano /etc/ssh/sshd_config
PubkeyAuthentication yes
PasswordAuthentication no
```

Добавить отображение ip адреса в строку приглашения tty

Создаем службу. sudo nano /etc/systemd/system/update-issue.service

```
[Unit]
Description=Update /etc/issue with current IP
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
# ExecStartPre: ждать IP до 60 секунд
ExecStartPre=/bin/bash -c 'for i in $(seq 1 60); do ip=$(hostname -I | awk "{print \$1}"); [ -n "$ip" ] && exit 0; sleep 1; done; exit 1'
ExecStart=/bin/bash -c 'ip=$(hostname -I | awk "{print \$1}"); echo "Debian server — IP: $ip" > /etc/issue;'
TimeoutStartSec=120

[Install]
WantedBy=multi-user.target

```

Создаем и запускаем службу

```
sudo systemctl daemon-reload
sudo systemctl enable --now update-issue.service
```

# Сертификат от LetsEncrypt

Это можно использовать на практике, хотя лучше все-таки купить.

Устанавливаем certbot

```
sudo apt install certbot
```

Запрашиваем сертификаты на основной домен и домены третьего уровня

```
sudo certbot certonly --manual --agree-tos --email bobrovsa@yandex.ru --server https://acme-v02.api.letsencrypt.org/directory --preferred-challenges=dns -d bobrobotirk.ru -d *.bobrobotirk.ru
```

Там будет несколько шагов и затем предложат создать первую DNS запись

```
Please deploy a DNS TXT record under the name:

_acme-challenge.bobrobotirk.ru.

with the following value:

NIr-7aDKMLQcXD5X5pTUI8QHqfrsyhELT-O3RRowj6U

```

Через dns manager создаем запись и ждем, пока данный запрос не вернет созданную запись

```
nslookup -type=txt _acme-challenge.bobrobotirk.ru 8.8.8.8
╤хЁтхЁ:  dns.google
Address:  8.8.8.8

Не заслуживающий доверия ответ:
_acme-challenge.bobrobotirk.ru  text = "NIr-7aDKMLQcXD5X5pTUI8QHqfrsyhELT-O3RRowj6U"
```

Затем в консоли нажимаем Enter, будет предложено создать еще одну запись, создаем ее, аналогично ждем,

```
nslookup -type=txt _acme-challenge.bobrobotirk.ru 8.8.8.8
╤хЁтхЁ:  dns.google
Address:  8.8.8.8

Не заслуживающий доверия ответ:
_acme-challenge.bobrobotirk.ru  text = "4s3TX4mO9wCzwBqEYDXbG2JumjktPKt3MQKNmUwRMe8"
_acme-challenge.bobrobotirk.ru  text = "NIr-7aDKMLQcXD5X5pTUI8QHqfrsyhELT-O3RRowj6U"
```

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

```
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/bobrobotirk.ru/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/bobrobotirk.ru/privkey.pem
This certificate expires on 2025-07-03.
These files will be updated when the certificate renews.

```

Дальше возник вопрос преобразования файлов в .key и .crt Несколько ресурсов просмотрел, но были написаны разные команды, это напрягло. И только на [stackoverflow](https://stackoverflow.com/questions/62823792/how-to-get-crt-and-key-from-cert-pem-and-key-pem) увидел нормальное описание. Идея в том, что расширение .pem не говорит о формате файла сертификата (смешно, но факт). Формат может быть бинарный и текстовый. Для проверки нужно просто просмотреть файл

```
sudo cat /etc/letsencrypt/live/bobrobotirk.ru/fullchain.pem
```

Если файл будет начинаться с -----BEGIN CERTIFICATE-----, то это означает текстовый формат. Иначе 99% DER формат и и нужно соответствующее преобразование. Но бывают и другие форматы. [Список команд](https://stackoverflow.com/questions/13732826/convert-pem-to-crt-and-key) для преобразования.

Для nginx нужен текстовый формат, поэтому в моем случае нужно простое копирование сертификата с изменением расширения)

```
cp cert.pem cert.crt
cp privkey.pem  privkey.key
```

# Окружение

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

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

<table border="1" id="bkmrk-%D0%9F%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 20.9666%;"></col><col style="width: 79.0334%;"></col></colgroup><thead><tr><td class="align-center">Переменная</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>PATH</td><td>перечисление разделенных симво­лом : имен каталогов, где любой командный интерпретатор ищет одноименные запускаемым командам программы</td></tr><tr><td>LANGUAGE и LANG</td><td>Идентификаторы языка, на кото­ром программы стараются общаться с пользователем; например,   
man(1) ищет перевод страницы руководства. Переменная LANGUAGE определяет список языков, в порядке которого определяется язык об­щения. Переменная LANG определяет язык по умолчанию, если в порядке про­смотра LANGUAGE ничего подходящего не найдено.</td></tr><tr><td>LC\_\*</td><td>Другие языковые особенности, отличные от языка сообщений; на­пример, переменная LC\_TIME определяет формат выводы даты и времени. </td></tr><tr><td>EDITOR и VISUAL</td><td>Имя текстового редактора, кото­рый будет вызван другими программами при необходимости редактировать текст.</td></tr><tr><td>PS1</td><td>Приглашение командного интерпретатора и может содержать подстановки</td></tr></tbody></table>

Команды

<table border="1" id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-uns" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 20.3713%;"></col><col style="width: 79.6287%;"></col></colgroup><thead><tr><td class="align-center">Команда</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>unset env\_name</td><td>очищает значение переменной</td></tr><tr><td>env</td><td>список текущих переменных окружения</td></tr></tbody></table>

.profie - параметры в начале сеанса

.bashrc - параметры при каждом запуске

Файлы - стандартный bash. Можно прописать свои alias, например

alias ll='ls -alF'

# Файловая система и alias

Основной принцип: информация есть файл, откуда бы эта информация в систему ни поступала. Т е файл это единица обеспечения доступа к информации, а не единица хранения информации. Т е файлы есть даже для процессов.

Тип файла:

- - — обычный файл
- d — каталог. Содержит список файлов и каталогов.
- b или с — специальные файлы блочного (block) или символьного (character) устрой­ства
- р — именованный канал (pipe)
- s — сокет (socket)
- I — символическая ссылка (link)

<table border="1" id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-fil" style="border-collapse: collapse; width: 100%; height: 61.5938px;"><colgroup><col style="width: 29.1072%;"></col><col style="width: 71.012%;"></col></colgroup><thead><tr style="height: 31.7969px;"><td class="align-center" style="height: 31.7969px;">Команда</td><td class="align-center" style="height: 31.7969px;">Описание</td></tr></thead><tbody><tr><td>type cur\_al</td><td>Алиас приложения, например type passwd</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">file путь</td><td style="height: 29.7969px;">Описание файла.</td></tr><tr><td>stat путь</td><td>Полный список параметров</td></tr><tr><td>ls -li </td><td>  
</td></tr><tr><td>Isof путь</td><td>Кто использует данный файл</td></tr><tr><td>ln fpath linkname</td><td>Создание жесткой ссылки

-s Создание символической ссылки

</td></tr><tr><td>mount</td><td>Просмотр смонтированных файловых систем

source dest смонтировать устройство в ФС

</td></tr></tbody></table>

**Ссылки.**

Жесткие ссылки создаются как еще одна ссылка на inode файла. Увеличивает счетчик ссылок на inode. Жесткая ссылка локальна в пределах файловой системы. Для жестких ссылок права доступа эквивалентны правам целевого файла. Файл физически удаляется только если счетчик ссылок равен 0. Т е технически можно сделать жесткую ссылку и разместить ее в другом месте. Пользователь будет думать, что удалил файл.

Символические ссылки просто ссылаются на файл в операционной системе. Могут существовать после удаления основного файла. Понятия "права доступа" у символических ссылок нет - они берут права основного файла.

**Поиск файлов**

<table border="1" id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-fin" style="border-collapse: collapse; width: 100%; height: 61.5938px;"><colgroup><col style="width: 29.1072%;"></col><col style="width: 71.012%;"></col></colgroup><thead><tr style="height: 31.7969px;"><td class="align-center" style="height: 31.7969px;">Команда</td><td class="align-center" style="height: 31.7969px;">Описание</td></tr></thead><tbody><tr><td>find</td><td>Поиск по имени. Если сначала не указана директория, то поиск в текущей и по точному совпадению. Бессмысленно. Поэтому формат должен быть find директория параметры

```
find ./ tren.txt
```

-name имя файла, точное совпадение. \* ? работает

-size +1G файлы больше 1 ГБ

 -type f отображает только файлы

 Еще есть поиск по правам доступа. Похоже по всем атрибутам можно искать.

</td></tr><tr><td>locate</td><td>Хранит свои данные в базе. Необходимо ее обновить перед поиском, чтобы было быстрее и точно. По умолчанию ищет неполное вхождение (т.е. файл localtest.sh тоже войдет в результат)

```
updatedb
locate test.sh
```

-n 10 ограничивает количество вывода

</td></tr><tr><td>which</td><td>Расположение файла бинарника, на который есть ссылка в PATH

</td></tr></tbody></table>

**Загрузка системы**

/etc/init.d/ - что может запускаться

/etc/rc?.d/ - что запускается на разных уровнях загрузки системы

**Режимы доступа к различным типам файлов**

<table border="1" id="bkmrk-%D0%A2%D0%B8%D0%BF-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0-r-w-x-%D0%9E%D0%B1%D1%8B%D1%87" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25%;"></col><col style="width: 25%;"></col><col style="width: 25%;"></col><col style="width: 25%;"></col></colgroup><thead><tr><td class="align-center">Тип файла</td><td class="align-center">r</td><td class="align-center">w</td><td class="align-center">x</td></tr></thead><tbody><tr><td>Обычный файл</td><td>Чтение</td><td>Запись</td><td>Исполнение</td></tr><tr><td>Каталог</td><td>Чтение списка файлов</td><td>Создание и удаление файлов</td><td>Обращение к файлам внутри каталога</td></tr><tr><td>Файлы устройств, каналов, сокетов</td><td>Право ввода</td><td>Право вывода</td><td>Не существует</td></tr></tbody></table>

**Дополнительные атрибуты**

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

```
chmod g+ws
```

заставит создавать файлы принадлежащие группе, к которой принадлежит папка.

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

# Bash

# Bash общая информация

Консоль работает с текстом, поэтому центральная задача - обработка текста.

<table border="1" id="bkmrk-echo-%D0%9F%D1%80%D0%BE%D1%81%D1%82%D0%BE-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4%D0%B8%D1%82-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 18.631%;"></col><col style="width: 81.4882%;"></col></colgroup><tbody><tr><td>echo</td><td>Просто выводит строку</td></tr><tr><td>printf</td><td>Выводит строку с модификаторами

printf "%s %d" "ff" 22

</td></tr></tbody></table>

Поиск приложения: whereis name

~ - домашняя директория, ~sergey домашняя директория пользователя sergey

cd без аргументов в домашнюю, cd - в предыдущую

Маски для ls

<table border="1" id="bkmrk-%3F-%D0%BE%D0%B4%D0%B8%D0%BD-%D1%81%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB-%2A-%D0%BC%D0%BD%D0%BE%D0%B3" style="border-collapse: collapse; width: 100%; height: 152.781px;"><colgroup><col style="width: 18.631%;"></col><col style="width: 81.4882%;"></col></colgroup><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">?</td><td style="height: 29.7969px;">один символ</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">\*</td><td style="height: 29.7969px;">много символов</td></tr><tr style="height: 63.3906px;"><td style="height: 63.3906px;">\[set\]</td><td style="height: 63.3906px;">Набор символов, в данном случае s,e,t

\[a-c\] a,b,c

\[!0-9\] где нет цифр

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">{}</td><td style="height: 29.7969px;">перечисляются наборы текста, например echo f{io,am}

</td></tr></tbody></table>

Утилиты фильтрации текста

<table border="1" id="bkmrk-cat-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25.6548%;"></col><col style="width: 74.4644%;"></col></colgroup><tbody><tr><td>cat</td><td>перенаправление ввода на вывод

-b - нумеровать только непустые строки;  
-E - показывать символ $ в конце каждой строки;  
-n - нумеровать все строки;  
-s - удалять пустые повторяющиеся строки

cat file1 file2 &gt; file3

</td></tr><tr><td>grep</td><td>поиск строк во вводе

Может анализировать один файл или директорию.

-e несколько регулярных выражений, например -e ... -e ...

-n номер строки, где найдено совпадение

-i, --ignore-case - не учитывать регистр символов;  
-v, --invert-match - вывести только те строки, в которых шаблон поиска не найден;  
-w, --word-regexp - искать шаблон как слово, отделенное пробелами или другими знаками препинания;  
-x, --line-regexp - искать шаблон как целую строку, от начала и до символа перевода строки;  
-c - вывести количество найденных строк;  
-L, --files-without-match - выводить только имена файлов, будут выведены все файлы в которых выполняется поиск;  
-l, --files-with-match - аналогично предыдущему, но будут выведены только файлы, в которых есть хотя бы одно вхождение;  
-m, --max-count - остановить поиск после того как будет найдено указанное количество строк;  
-o, --only-matching - отображать только совпавшую часть, вместо отображения всей строки;  
-h, --no-filename - не выводить имя файла;  
-s, --no-messages - не выводить ошибки чтения файлов;  
-A, --after-content - показать вхождение и n строк после него; Те -A2 строка + 2 строки после  
-B, --before-content - показать вхождение и n строк перед ним;  
-C - показать n строк до и после вхождения;  
-a, --text - обрабатывать двоичные файлы как текст;  
\--exclude - пропустить файлы имена которых соответствуют регулярному выражению;  
\--exclude-dir - пропустить все файлы в указанной директории;  
-I - пропускать двоичные файлы;  
\--include - искать только в файлах, имена которых соответствуют регулярному выражению;  
-r - рекурсивный поиск по всем подпапкам;  
-R - рекурсивный поиск включая ссылки;

```bash
grep -r "поисковой_запрос" /путь/к/директории/
```

</td></tr><tr><td>sort</td><td>сортировка строк

-b - не учитывать пробелы  
-d - использовать для сортировки только буквы и цифры  
-i - сортировать только по ASCII символах  
-n - сортировка строк linux по числовому значению  
-r - сортировать в обратном порядке  
-o - вывести результат в файл  
-u - игнорировать повторяющиеся строки  
-m - объединение ранее отсортированных файлов  
-k - указать столбец по которому нужно сортировать строки, если не задано, сортировка выполняется по всей строке.

ls -l | sort -k9  
-f - использовать в качестве разделителя полей ваш символ вместо пробела.

</td></tr><tr><td>uniq f1 &gt; f2</td><td>Удаляет повторяющиеся строки

</td></tr><tr><td>cut</td><td>извлечение символов из ввода

-b 2-6, 8 отобразит символы от 2 по 6 из строки и 8

-b 5- символы с 5 до конца

-с нечто похожее на -b

-d ':' -f 1,3 считать разделителем : и оставить первый и третий столбцы

</td></tr><tr><td>sed</td><td>операции изменения данных перед выводом

Позволяет вырезать строки, поиск/замена по регулярке, вывод. Крутая штука.

</td></tr><tr><td>tr</td><td>транслирование символов на вводе в другие символы

-d набор - удаление символов

 tr -d '0-9'

-s заменяет последовательность повторяющихся символов в SET1 на один такой символ;

 tr -s ' ' заменит много пробелов на один

</td></tr><tr><td>head</td><td>-n кол-во Выводит первые кол-во строк</td></tr><tr><td>awk</td><td>Считывает построчно данные, выполняет действия и выводит в stdout. Включает целый язык для обработки текста.

awk опции 'условие {действие} условие {действие}'

Опции:

-F, --field-separator - разделитель полей, используется для разбиения текста на колонки;  
-f, --file - прочитать данные не из стандартного вывода, а из файла;  
-v, --assign - присвоить значение переменной, например foo=bar;  
-b, --characters-as-bytes - считать все символы однобайтовыми;  
-d, --dump-variables - вывести значения всех переменных awk по умолчанию;  
-D, --debug - режим отладки, позволяет вводить команды интерактивно с клавиатуры;  
-e, --source - выполнить указанный код на языке awk;  
-o, --pretty-print - вывести результат работы программы в файл;

Действия:

print(строка) - вывод чего либо в стандартный поток вывода;  
printf(строка) - форматированный вывод в стандартный поток вывода;  
system(команда) - выполняет команду в системе;  
length(строка) - возвращает длину строки;  
substr(строка, старт, количество) - обрезает строку и возвращает результат;  
tolower(строка) - переводит строку в нижний регистр;  
toupper(строка) - переводить строку в верхний регистр.

Переменные:

$NF - последняя строка

$(NF-1) - предпоследняя

NR - количество строк с начала

awk -F":" '{print $4}' Возьмет строку и по разделителю : выведет 4 элемент

Поддерживает регулярные выражения

echo -e 'one 1\\n two 2' | awk '{sum+=$2} END {print sum}' сумма элементов.

awk 'NR &lt; 10' log.txt

</td></tr><tr><td>sed</td><td>Тоже очень крутая штука, этим двум командам посвящена целая книга.

</td></tr><tr><td>diff file1 file2</td><td>Различие в строках

</td></tr><tr><td>ndiff</td><td>Различие в результатах сканирования nmap

```
${BIN_PATH}ndiff $BASE_RESULTS $NEW_RESULTS > $NDIFF_RESULTS
```

</td></tr><tr><td>jq</td><td>Утилита для анализа JSON.

jq -r '.first' возвращает значение ключа first

jq '.\[0\].plugins.HTTPServer.string\[0\] определяет следующий элемент:

```json
[
  {
    "plugins": {
      "HTTPServer": {
        "string": [
          "Werkzeug/3.0.1 Python/3.12.3"
        ]
      }
    }
  }
]
```

</td></tr><tr><td>tail -F filename</td><td>Отображения изменения в файле

</td></tr><tr><td>wget</td><td>Загрузка файлов

-q тихий режим

-r рекурсивное скачивание

-np скачивание файлов из данной директории или ниже

-R "index.html" исключить загрузку файлов вида index.html

-P foldername директория сохранения

</td></tr><tr><td>curl</td><td>Must have.

-s тихий режим

-X GET|POST|PUT|DELETE → явный выбор метода

-d "param=value" → данные формы (POST)

```
curl -X POST -H "Content-Type: application/json" \
     -d '{"name":"Alice"}' https://api.example.com/users
```

-H "Header: value" → добавить заголовок

```
curl -H "Authorization: Bearer TOKEN" https://api.example.com
```

-b "name=value" → передать cookie  
-c cookies.txt → сохранить cookie в файл  
-b cookies.txt → использовать cookie из файла

-o file → сохранить в файл

 -u user:pass → basic auth

-k → игнорировать ошибки сертификатов (небезопасно!)  
\--cacert file.crt → указать свой сертификат CA

-w "%{http\_code}\\n" → вывести только HTTP-код

</td></tr><tr><td>find / -name "file.txt"</td><td>Поиск файла

</td></tr></tbody></table>

**Редиректы**

Вывод в файл:

<table id="bkmrk-%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80-%D0%97%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-%D0%9F%D1%80"><thead><tr><th>Оператор</th><th>Значение</th><th>Пример</th></tr></thead><tbody><tr><td>`>`</td><td>Перенаправить вывод (перезаписать файл)</td><td>`echo "hi" > file.txt`</td></tr><tr><td>`>>`</td><td>Перенаправить вывод (добавить в файл)</td><td>`echo "hi" >> file.txt`</td></tr></tbody></table>

Вывод из файла:

<table id="bkmrk-%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80-%D0%97%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-%D0%9F%D1%80-1"><thead><tr><th>Оператор</th><th>Значение</th><th>Пример</th></tr></thead><tbody><tr><td>`<`</td><td>Чтение из файла</td><td>`wc -l < file.txt`</td></tr><tr><td>`<<`</td><td>**Here document** — передаёт блок текста как stdin</td><td>cat &lt;&lt; EOFстрока1строка2EOF</td></tr><tr><td>`<<<`</td><td>**Here string** — передаёт одну строку (или переменную)</td><td>`grep foo <<< "foo bar baz"`</td></tr></tbody></table>

**Фоновые задачи**

Фоновые задачи не получают данные от клавиатуры

<table id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%A7%D1%82%D0%BE-%D0%B4%D0%B5%D0%BB%D0%B0%D0%B5%D1%82-c" style="width: 99.6429%;"><thead><tr><th style="width: 22.294%;">Команда</th><th style="width: 77.706%;">Что делает</th></tr></thead><tbody><tr><td style="width: 22.294%;">`command &`</td><td style="width: 77.706%;">Запускает процесс в фоне</td></tr><tr><td style="width: 22.294%;">`jobs`</td><td style="width: 77.706%;">Показывает список фоновых задач (с job ID: `%1`, `%2` …)</td></tr><tr><td style="width: 22.294%;">`fg %N`</td><td style="width: 77.706%;">Переводит задачу номер N в передний план</td></tr><tr><td style="width: 22.294%;">`bg %N`</td><td style="width: 77.706%;">Возобновляет приостановленную задачу в фоне</td></tr><tr><td style="width: 22.294%;">`Ctrl+Z`</td><td style="width: 77.706%;">Приостанавливает текущий процесс (отправляет в «stopped»)</td></tr><tr><td style="width: 22.294%;">`kill %N`</td><td style="width: 77.706%;">Завершает задачу по её job ID</td></tr><tr><td style="width: 22.294%;">`kill PID`</td><td style="width: 77.706%;">Завершает задачу по PID</td></tr><tr><td style="width: 22.294%;">`kill -9 PID`</td><td style="width: 77.706%;">Жёсткое завершение (если обычный `kill` не помог)</td></tr><tr><td style="width: 22.294%;">`disown %N`</td><td style="width: 77.706%;">«Отвязывает» задачу от текущего терминала (будет жить после выхода)</td></tr><tr><td style="width: 22.294%;">`ps aux</td><td style="width: 77.706%;">grep name`</td></tr><tr><td style="width: 22.294%;">`pkill -f pattern`</td><td style="width: 77.706%;">Завершение процессов по шаблону</td></tr></tbody></table>

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

```
nohup ./myscript.sh &
```

**История ввода команд**

Хранится где-то в сессии, сохраняется в .bash\_history при закрытии сессии. Для просмотра введенных команд в пределах сессии используется утилита fc

<table border="1" id="bkmrk--l-%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BD%D0%B8%D0%B5-15-%D0%BA%D0%BE%D0%BC%D0%B0" style="border-collapse: collapse; width: 100%; height: 368.953px;"><colgroup><col style="width: 31.4882%;"></col><col style="width: 68.631%;"></col></colgroup><tbody><tr style="height: 80.1875px;"><td style="height: 80.1875px;">-l</td><td style="height: 80.1875px;">Последние 15 команд

4 выведет четвертую команду

4 7 команды с 4 по 7

строка - выведет с первого вхождения строки (из 15) и далее

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">-n</td><td style="height: 29.7969px;">без вывода номеров</td></tr><tr style="height: 80.1875px;"><td style="height: 80.1875px;">-e nano</td><td style="height: 80.1875px;">без дальнейших аргументов - редактирование последней команды и затем исполнение

Чтобы не вводить редактор, можно в .bash\_profile добавить переменную

FCEDIT=nano

</td></tr></tbody></table>

!! исполнение предыдущей команды

**Специальные файлы**

.bash\_profile, .bash\_logout, .bashrc Если отсутствуют - используются файл /etc/profile и файлы из /etc/profile.d

**.bash\_profile - сейчас не используется.**

В .profile переменные окружения, .bashrc - скрипты. .bash\_login при входе. Но идея осталась.

Последовательность поиска при авторизации, исполняется первый найденный

- `~/.bash_profile`
- `~/.bash_login`
- `~/.profile`

Исполняет команды при каждом входе в систему.

Итого:

~/.bashrc

Используется в интерактивных (обычных) shell-ах. Сюда кладём то, что нужно только в терминале:

- алиасы (alias ll='ls -lh')
- функции (mkcd() { mkdir -p "$1" &amp;&amp; cd "$1"; })
- настройки PS1 (prompt, цвета)
- shopt и настройки истории (HISTSIZE, HISTCONTROL)
- подключение fzf, автодополнений и прочих «тюнингов»

~/.profile

Используется при login-shell (и не только Bash, а sh/dash/zsh тоже могут его читать). Сюда кладём:

- переменные окружения (PATH, EDITOR, LANG, JAVA\_HOME)
- настройки, которые должны работать и в GUI-программах (например, экспорт GTK\_THEME или XMODIFIERS)
- запуск программ при логине (например, ssh-agent или gnome-keyring-daemon)

~/.bash\_profile или ~/.bash\_login

Сегодня почти не нужны. Если они есть, то Bash не будет читать ~/.profile.

source .profile - перечитать файл

**Alias, options и переменные окружения**

Алиасы для команд. Создание алиасов: alias cdvoy='cd sipp/demo/animation/voyager'

Пробел перед закрывающей ' означает ожидание ввода пользователя.

unalias name - удаление

set +o optionname установить опцию, -o убрать. Фиксированный набор. Не впечатлило.

**Переменные**

Синтаксис: varname='something', использование $varname удаление unset varname

Формат timestamp:

<table border="1" id="bkmrk-%25a%2C-%25a-%D0%90%D0%B1%D0%B1%D1%80%D0%B5%D0%B2%D0%B8%D0%B0%D1%82%D1%83%D1%80%D0%B0-" style="border-collapse: collapse; width: 100%; height: 268.172px;"><colgroup><col style="width: 17.0441%;"></col><col style="width: 82.9559%;"></col></colgroup><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%a, %A</td><td style="height: 29.7969px;">Аббревиатура дня недели, Полное имя дня недели</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%b, %B, %m</td><td style="height: 29.7969px;">Аббревиатура имени месяца, Полное имя месяца, Номер месяца в числовом формате</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%c</td><td style="height: 29.7969px;">Дата и время локали</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%C</td><td style="height: 29.7969px;">Последние 2 цифры года

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%H, %I, %p</td><td style="height: 29.7969px;">Час в 24-часовом формате, Час в 12-часовом формате, эквивалент am/pm</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%d, %e</td><td style="height: 29.7969px;">Цифра дня, цифра дня с пробелом в случае одной цифры</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%D</td><td style="height: 29.7969px;">Дата в американском формате (%m/%d/%y)</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%j</td><td style="height: 29.7969px;">День года 001-366</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">%M</td><td style="height: 29.7969px;">Минута в десятичном представлении</td></tr><tr><td>%n, %t</td><td>Новая строка, табуляция</td></tr><tr><td>%R</td><td>Время в 24-часовом формате</td></tr><tr><td>%S</td><td>Секунда в десятичном формате</td></tr><tr><td>%T</td><td>Время (час:минута:секунда)</td></tr><tr><td>%u</td><td>Номер дня недели в десятичном формате</td></tr><tr><td>%U</td><td>Номер недели в году</td></tr><tr><td>%Y</td><td>Год</td></tr></tbody></table>

Строка приглашения

PS1 - основное приглашение

PS2 - при незавершенной строке

В переменной PS1

<table id="bkmrk-%D0%9A%D0%BE%D0%B4-%D0%A7%D1%82%D0%BE-%D0%BF%D0%BE%D0%BA%D0%B0%D0%B7%D1%8B%D0%B2%D0%B0%D0%B5%D1%82-%5C"><thead><tr><th>Код</th><th>Что показывает</th></tr></thead><tbody><tr><td>`\u`</td><td>Имя текущего пользователя</td></tr><tr><td>`\h`</td><td>Имя хоста (до первой точки)</td></tr><tr><td>`\H`</td><td>Полное имя хоста</td></tr><tr><td>`\w`</td><td>Текущая директория (полный путь, с `~` для home)</td></tr><tr><td>`\W`</td><td>Текущая директория (только имя последней папки)</td></tr><tr><td>`\!`</td><td>Номер текущей команды в истории</td></tr><tr><td>`\#`</td><td>Номер команды (считает все команды с момента запуска shell)</td></tr><tr><td>`\$`</td><td>Отображает `$` для обычного пользователя и `#` для root</td></tr><tr><td>`\t`</td><td>Время в формате `HH:MM:SS`</td></tr><tr><td>`\T`</td><td>Время в формате `HH:MM` (12-часовой формат)</td></tr><tr><td>`\@`</td><td>Время в формате `hh:mm AM/PM`</td></tr><tr><td>`\d`</td><td>Дата (например: `Mon Aug 26`)</td></tr><tr><td>`\n`</td><td>Перенос строки</td></tr><tr><td>`\s`</td><td>Имя используемой оболочки (обычно `bash`)</td></tr><tr><td>`\v`</td><td>Версия Bash (например: `5.2`)</td></tr><tr><td>`\V`</td><td>Версия Bash с патч-уровнем</td></tr><tr><td>`\j`</td><td>Количество фоновых задач, запущенных из shell</td></tr><tr><td>`\!`</td><td>Номер команды в истории</td></tr><tr><td>`\#`</td><td>Номер команды (считает все команды в текущей сессии)</td></tr><tr><td>`\e` или `\033`</td><td>Escape-символ (для цветов/ANSI-последовательностей)</td></tr></tbody></table>

Пример: PS1="\\u@\\h:\\w\\$ "

**PATH**

Просмотр последовательно всех путей.

Добавление в пути: PATH=$PATH":/home/you/bin"

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

hash -r очищает таблицу хэшей путей.

**CDPATH**

Аналог path для директорий поиска при использовании cd.

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

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

<table border="1" id="bkmrk-export-varnames-%D0%9C%D0%BE%D0%B6%D0%B5" style="border-collapse: collapse; width: 100%; height: 147.172px;"><colgroup><col style="width: 31.4882%;"></col><col style="width: 68.631%;"></col></colgroup><tbody><tr><td>env</td><td>Список переменных окружения</td></tr><tr style="height: 37.1875px;"><td style="height: 37.1875px;">export varnames</td><td style="height: 37.1875px;">Может быть список через пробел</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">export varname=value command</td><td style="height: 29.7969px;">Доступность переменной только для определенной команды</td></tr></tbody></table>

source .envfile догружает переменные из файла

Переменные по умолчанию

<table border="1" id="bkmrk-bash_version-%D0%92%D0%B5%D1%80%D1%81%D0%B8%D1%8F-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 21.7263%;"></col><col style="width: 78.3929%;"></col></colgroup><tbody><tr><td>BASH\_VERSION</td><td>Версия</td></tr><tr><td>BASHPID</td><td>  
</td></tr><tr><td>GROUPS</td><td>Группы текущего пользователя</td></tr><tr><td>HOSTNAME</td><td>Имя хоста</td></tr><tr><td>OSTYPE</td><td>  
</td></tr><tr><td>PWD</td><td>Текущая рабочая директория</td></tr><tr><td>RANDOM</td><td>Случайное число от 0 до 32767</td></tr><tr><td>UID</td><td>Идентификатор пользователя</td></tr><tr><td>SHELL</td><td>Полный путь к текущей консоли</td></tr></tbody></table>

**Периферийные функции**

<table border="1" id="bkmrk-sleep-n-%D0%9F%D0%B0%D1%83%D0%B7%D0%B0-%D0%BD%D0%B0-n-%D1%81" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25.9781%;"></col><col style="width: 74.0219%;"></col></colgroup><tbody><tr><td>sleep n</td><td>Пауза на n секунд</td></tr><tr><td>seq 1 10</td><td>Последовательность чисел от 1 до 10</td></tr><tr><td>date '+%Y-%m-%d %H:%M:%S'</td><td>Текущая дата и время</td></tr><tr><td>tail -f fname</td><td>Выводит изменения файла в текущую консоль</td></tr></tbody></table>

# Bash скрипты

При запуске скрипта создается дочерняя консоль, в ней выполняется скрипт и результат возвращается в консоль.

Можно запускать через source, имя (если . в PATH), ./... , и ./ ... &amp; С фоновой задачей аккуратно с объемом вывода, ^C не работает.

**Shebang**

Лучше использовать shebang в начале (указатель, какую консоль использовать)

\#!/bin/bash

<table border="1" id="bkmrk-%23%21%2Fbin%2Fbash--x-%D0%92%D1%8B%D0%B2%D0%BE%D0%B4" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 20.6548%;"></col><col style="width: 79.4644%;"></col></colgroup><tbody><tr><td>\#!/bin/bash -x</td><td>Выводит все команды во время исполнения</td></tr><tr><td>\#!/bin/bash -r</td><td>Блокирует потенциально опасные действия</td></tr></tbody></table>

Параметры можно указывать при запуске скрипта, тогда shebang работать не будет.

**Удобства**

Комментарии #

bash -n script.sh проверка синтаксиса без исполнения скрипта

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

```
#!/bin/bash
...
set -x
...potentially error part...
set +x
```

Операция ; запускает несколько команд независимо от их статуса завершения

```
ls; ps; whoami
```

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

**Перенаправления результата выполнения**

<table border="1" id="bkmrk-%3E-stdout-%D0%B2-%D1%84%D0%B0%D0%B9%D0%BB%2C-%D1%84%D0%B0%D0%B9" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 18.7501%;"></col><col style="width: 81.3691%;"></col></colgroup><tbody><tr><td>&gt;</td><td>Stdout в файл, файл очищается</td></tr><tr><td>&gt;&gt;</td><td>Stdout в файл, файл дополняется</td></tr><tr><td>&amp;&gt; or &gt;&amp;</td><td>Stdout и Stderr в файл, файл очищается</td></tr><tr><td>&amp;&gt;&gt;</td><td>Stdout и Stderr в файл, файл дополняется</td></tr><tr><td>&lt;</td><td>Перенаправление ввода в команду</td></tr><tr><td>&lt;&lt;</td><td>Перенаправление многострочного ввода в команду</td></tr></tbody></table>

Номера потоков

0 - stdin, 1 - stdout, 2 - stderr

```
lzl -l 1> stdout.txt 2>stderr.txt
```

выведет в stderr.txt

**Функции**

```
function functname{
shell commands
}

functname(){
shell commands
}
```

Используют позиционные переменные.

functname "one" $7

Если внутри функции нужна локальная переменная, то local var1="something", иначе используется глобальная переменная.

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

```bash
cd baddir
echo $?
```

По умолчанию возвращается статус вызова последней команды. Либо через return N

```bash
builtin mkdir "/var/first"
es=$?
echo "$OLDPWD --> $PWD"
return $es

```

Желательно возвращать код результата, а не просто цифру.

**Программы для работы с функциями**

<table border="1" id="bkmrk-declare--f-%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D1%84%D1%83" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 16.7263%;"></col><col style="width: 83.3929%;"></col></colgroup><tbody><tr><td>declare</td><td>-f список функций с описанием</td></tr><tr><td>  
</td><td>-F только имена функций</td></tr><tr><td>unset fname</td><td>удалить функцию из памяти</td></tr><tr><td>type fname</td><td>Описание функции</td></tr><tr><td>  
</td><td>-t выведет тип</td></tr></tbody></table>

**Приоритет при вызове из консоли**

1. Aliases
2. Служебные слова типа if, ...
3. Функции
4. Встроенные команды типа cd, type, ...
5. Скрипты и исполняемые команды

**Переменные**

**Строковые (по умолчанию)**

Описывается $varname или ${varname} Например есть переменная UID. Для вывода 0\_ нужно использовать echo ${varname}\_

Удаление переменной unset var1

<table border="1" id="bkmrk-%241%2C-%242%2C-...-%D0%9F%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D0%BE%D0%BD" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 13.631%;"></col><col style="width: 86.4882%;"></col></colgroup><tbody><tr><td>$1, $2, ...</td><td>Позиционные параметры</td></tr><tr><td>$\*</td><td>Строка, содержащая все параметры через пробел</td></tr><tr><td>$@</td><td>Равна N строкам-параметрам. Актуально при передаче в функции.</td></tr><tr><td>$#</td><td>Количество аргументов</td></tr><tr><td>var1=$(cmd)</td><td>Сохранение результата cmd в переменную</td></tr></tbody></table>

Определение глобальной переменной: var1="something"

Обработка значений переменных перед вводом

<table border="1" id="bkmrk-%24%7Bvarname%3A-word-%7D-%D0%95%D1%81" style="border-collapse: collapse; width: 100%; height: 165.781px;"><colgroup><col style="width: 23.9882%;"></col><col style="width: 76.131%;"></col></colgroup><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">${varname:-word }</td><td style="height: 29.7969px;">Если переменная не существует или существует но пустая - вернет word</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">${varname:=word}</td><td style="height: 29.7969px;">Если переменная не существует или существует но пустая - присвоит ей word и вернет word</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">${varname:?message}</td><td style="height: 46.5938px;">Если переменная не существует или существует но пустая - выведет сообщение и завершит скрипт</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">${varname:+word}</td><td style="height: 29.7969px;">Если переменная существует или пустая - вернет word, иначе null</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">${varname:offset:length}</td><td style="height: 29.7969px;">Срез.

count=frogfootman

${count:4} вернет footman

${count:4:4} вернет foot

</td></tr></tbody></table>

Обработка переменных

<table border="1" id="bkmrk-%24%7Bvar%23pattern%7D-%C2%A0-%D0%95%D1%81%D0%BB" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 23.9882%;"></col><col style="width: 76.131%;"></col></colgroup><tbody><tr><td>${var#pattern}

</td><td>Если шаблон совпадает с началом значения переменной - удалить самую короткую часть от начала строки с совпадением и вернуть результат

var="/home/user/docs/file.txt"  
echo ${var#\*/} # home/user/docs/file.txt (отрезан только первый "/")

</td></tr><tr><td>${var##pattern}</td><td>var="/home/user/docs/file.txt"

echo ${var##\*/} # file.txt (отрезано всё до последнего "/")

</td></tr><tr><td>${variable%pattern}</td><td>file="archive.tar.gz"

echo ${file%.\*} # archive.tar

</td></tr><tr><td>${variable%%pattern}</td><td>file="archive.tar.gz"

echo ${file%%.\*} # archive

</td></tr><tr><td>${variable/pattern/string}</td><td>Наибольшее совпадение шаблона с переменной заменяется строкой. Первое совпадение

</td></tr><tr><td>${variable//pattern/ string}</td><td>Наибольшее совпадение шаблона с переменной заменяется строкой. Все совпадения

</td></tr><tr><td>  
</td><td> echo -e ${PATH//:/'\\n'}

выведет разделенное через : значение в значения списком

</td></tr></tbody></table>

Длина значения ${#filename}

**Типизированные**

При помощи declare &lt;param&gt; var

<table border="1" id="bkmrk--a-%D0%9C%D0%B0%D1%81%D1%81%D0%B8%D0%B2--i-%D0%A6%D0%B5%D0%BB%D0%BE%D0%B5-%D1%87" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 23.631%;"></col><col style="width: 76.4882%;"></col></colgroup><tbody><tr><td>-a</td><td>Массив</td></tr><tr><td>-i</td><td>Целое число</td></tr><tr><td>-r</td><td>Переменная только для чтения</td></tr><tr><td>-x</td><td>Переменная экспортируется в окружение</td></tr><tr><td>-f</td><td>Хрень. Возвращает тело функции если она есть.

```bash
myfunc() {
    echo "Hello from myfunc"
}
declare -f myfunc
```

</td></tr><tr><td>-F</td><td>Возвратит имя функции если она есть.</td></tr></tbody></table>

Пример:

```bash
declare -i val3=12 val4=5
declare -i result2
result2=val3*val
echo $result2
```

Выведет 60

Использование let

```
let result="4 * 5"
echo $result
```

Также можно использовать двойные скобки

```
let result=$((4 * 5))
echo $result
```

Есть операции +,-,/,\*,%-модуль, += увеличение на константу, -=

**Массивы**

```
arr = (1 2 3)
echo "${arr[*]}" #весь массив
echo "${arr[0]}" #1
unset ${arr[1]} #удалит 2 из массива
${arr[0]}=10 #перезапишет 1 в 10
```

**Условия**

**Условия завершения программы**

Условие анализируется не на логическом, а на статусе результата (exit status) выполнения программы. 0 - ОК, 1-255 ошибка.

```
if condition; then
statements
[elif condition; then 
statements...]
[else
statements]
fi
```

**Условие анализа строк**

 конструкция \[\], внутри нее возможны условия проверки и сравнения строк.

<table border="1" id="bkmrk-str1-%3D-str2-%D0%A1%D1%82%D1%80%D0%BE%D0%BA%D0%B0-1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 21.3691%;"></col><col style="width: 78.7501%;"></col></colgroup><tbody><tr><td>str1 = str2</td><td>Строка 1 равна строке 2</td></tr><tr><td>str1 != str2</td><td>Строка 1 не равна строке 2</td></tr><tr><td>str1 &gt; str2 str1 &lt; str2</td><td>Больше/меньше</td></tr><tr><td>-n str1</td><td>Строка 1 не пустая</td></tr><tr><td>-z str1</td><td>Строка 1 пустая</td></tr></tbody></table>

**Условия сравнения чисел**

<table border="1" id="bkmrk--lt-%D0%9C%D0%B5%D0%BD%D0%B5%D0%B5-%D1%87%D0%B5%D0%BC--le-%D0%9C%D0%B5" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 21.131%;"></col><col style="width: 78.9882%;"></col></colgroup><tbody><tr><td>-lt</td><td>Менее чем</td></tr><tr><td>-le</td><td>Меньше или равно</td></tr><tr><td>-eq</td><td>Равно</td></tr><tr><td>-ge</td><td>Больше или равно</td></tr><tr><td>-gt</td><td>Больше</td></tr><tr><td>-ne</td><td>Не равно</td></tr></tbody></table>

Можно комбинировать

```bash
if command && [ condition ]; then
```

Логика внутри условия

<table border="1" id="bkmrk-%26%26-%D0%B8%D0%BB%D0%B8--a-%D0%92%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D1%8C-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 21.131%;"></col><col style="width: 78.9882%;"></col></colgroup><tbody><tr><td>&amp;&amp; или -a</td><td>Выполнить команду 1, если статус 0 - выполнить команду 2

```bash
if statement1 && statement2
then
...
fi
```

</td></tr><tr><td>|| или -o</td><td>Выполнить команду 1, если статус не 0 - выполнить команду 2

```bash
if statement1 || statement2
then
...
fi
```

</td></tr><tr><td>!</td><td>Отрицание

</td></tr></tbody></table>

Свойства файла, проверяемые в условиях

<table border="1" id="bkmrk--a-file%2C--e-file-%D0%A4%D0%B0%D0%B9" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 21.012%;"></col><col style="width: 79.1072%;"></col></colgroup><tbody><tr><td>-a file, -e file</td><td>Файл существует</td></tr><tr><td>-d file</td><td>Директория существует</td></tr><tr><td>-f file</td><td>Файл существует и является файлом (не директория или специальный файл)</td></tr><tr><td>-r file, -w file</td><td>Есть право чтения файла, Есть право записи в файл, </td></tr><tr><td>-x file</td><td>Есть право исполнения файла или право поиска в директории</td></tr><tr><td>-s file</td><td>Файл существует и не пустой</td></tr><tr><td>-N file</td><td>Файл был изменен с момента последнего чтения</td></tr><tr><td>-O file, -G file</td><td>Запустивший скрипт является владельцем файла, Группа запустившего скрипт входит в группу файла</td></tr><tr><td>file1 -nt file2</td><td>file1 новее file2 (сравниваются время модификации)</td></tr><tr><td>file1 -ot file2</td><td>file1 старее file2 (сравниваются время модификации)</td></tr></tbody></table>

**Цикл for**

Итерируется только по полному списку.

```
for name [in list]; do
    statements that can use $name...
    done
```

Если list не определен, то используется $@

Есть переменная IFS

```bash
IFS=:
for dir in $PATH
  do
    ls -ld $dir
  done
```

break выход из цикла, continue продолжить следующую итерацию

**Цикл while**

```bash
while condition; do
statements...
done
```

Для бесконечного цикла можно использовать while true; do

Просмотр каждой строки вывода программы (например ping)

```bash
#!/bin/bash

ping 172.16.10.1 | while read -r line; do
  echo Пинг $line | awk '{print $1 $6}'
  done

```

**Цикл until**

```bash
until command; do
statements...
done

```

**Case**

```bash
for filename in "$@"; do
  pnmfile=${filename%.*}.ppm
  case $filename in
    *.jpg ) exit 0 ;;
    *.tga ) tgatoppm $filename > $pnmfile ;;
    *.xpm ) xpmtoppm $filename > $pnmfile ;;
    *.pcx ) pcxtoppm $filename > $pnmfile ;;
    *.tif ) tifftopnm $filename > $pnmfile ;;
    *.gif ) giftopnm $filename > $pnmfile ;;
    * ) echo "procfile: $filename is an unknown graphics file." 
        exit 1 ;;
  esac
  outfile=${pnmfile%.ppm}.new.jpg
  pnmtojpeg $pnmfile > $outfile
  rm $pnmfile
done
```

**Опции исполнения**

shift смещает переменные влево.

```
myshift.sh
echo $1 $2
shift
echo $1 $2
Использование: ./myshift.sh one two
Вывод:
one two
two
```

Вариант скрипта, считывающего параметры со случайным размещением параметров

```bash
while [ -n "$(echo $1 | grep '-')" ]; do
  case $1 in
    -a ) aopt=$2
         shift;;
    -b ) bopt="True"
        ;;
    -c ) copt=$2
        shift;;
    *  ) echo 'usage: alice [-a] [-b] [-c] args...'
         exit 1
  esac
  shift
done
```

Опция -b является одинарной, -a и -c параметризованные.

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

```
echo "First arg"
read -r farg
echo "First arg is ${farg}"
```

**Вызов другого скрипта**

Если есть права на исполнение и правильный shebang то вызов по имени. Скрипт исполняется в подпроцессе.

```bash
#!/bin/bash
echo "Это первый скрипт"

./script2.sh   # вызов второго скрипта
```

Если директория script2.sh не в PATH то нужен полный путь.

Для выполнения скрипта в одном процессе, все переменные передаются:

```bash
#!/bin/bash
echo "Это первый скрипт"

source script2.sh
```

Также можно передавать аргументы.

# Примеры скриптов

**Задача:** Есть файл формата кол-во альбомов TAB исполнитель. Нужно вывести первые N исполнителей. Первый аргумент имя файла, второй (необязательный) N. По умолчанию 10.

```bash
filename=${1:?'Нужно определить имя файла в первом аргументе!'}
maxcount=${2:-10}
sort -nr $filename | head -n $maxcount
```

**Задача:** Обертка для команды ls, которая выводит информацию в человекочитаемом виде.

```bash
if [ ! -e "$1" ]; then
    echo "Файл $1 не существует"
    exit 1
fi
if [ -d "$1" ]; then
    echo -n "$1 директория, в которой "
    if [ ! -x "$1" ]; then
       echo -n "нельзя "
    fi
    echo "проводить поиск."
elif [ -f "$1" ]; then
    echo "$1 обычный файл."
else
    echo "$1 специальный файл."
fi
if [ -O "$1" ]; then
    echo 'Вы владелец этого файла'
else
    echo 'Вы не являетесь владельцем этого файла.'
fi
if [ -r "$1" ]; then
    echo 'У вас есть право чтения этого файла.'
fi
if [ -w "$1" ]; then
    echo 'У вас есть право записи этого файла.'
fi
if [ -x "$1" -a ! -d "$1" ]; then
    echo 'У вас есть право исполнения этого файла.'
fi

```

**Задача:** На входе скрипта два параметра: текстовый идентификатор и адрес сайта. Если не хватает параметров - ошибка и выход. Пропинговать адрес, сохранить данные в файл в виде Идентификатор, адрес, результат пинг (Да/Нет), дата и время.

```bash
#!/bin/bash

PINGRESULT='ping_result.csv'

ident=${1:?'Нужно определить идентификатор хоста в первом аргументе!'}
myhost=${2:?'Нужно определить адрес хоста во втором аргументе!'}


if [ ! -e "$PINGRESULT" ]; then
    touch $PINGRESULT
fi

if [ -d "$PINGRESULT" ]; then
    echo "$PINGRESULT это директория. Удалите ее или измените имя файла."
        exit 1
fi

declare -i res=$(ping $myhost -c 1 | grep transmitted | awk '{print $4}')
if [ $res -eq "1" ]; then
    isaval="Доступен"
else
    isaval="Не доступен"
fi
echo ${ident},${myhost},${isaval},$(date '+%Y-%m-%d %H:%M:%S') > $PINGRESULT
```

**Задача:** Отправить сообщение пользователю telegram

```bash
#!/bin/bash
#sudo apt update && sudo apt install -y jq
#usage ./tgsender "My message"

BOT_TOKEN="..."
# 📢 ID канала или ID user
CHAT_ID="..."

MESSAGE=${1:?"Необходимо определить сообщение в первом аргументе"}

# Запрос в Telegram API
res=$(curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
     -d chat_id="${CHAT_ID}" \
     -d text="${MESSAGE}" \
     -d parse_mode="Markdown"\
     | jq -r '.ok')

if [ "${res}" = "true" ]; then
  echo "Ok"
else
  echo "Error"
fi
```

# Регулярные выражения

<div id="bkmrk-%D0%9C%D0%B5%D1%82%D0%B0%D1%81%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB%D1%8B">**Метасимволы**</div><div id="bkmrk-%5E-%D0%BD%D0%B0%D1%87%D0%B0%D0%BB%D0%BE-%24-%D0%BA%D0%BE%D0%BD%D0%B5%D1%86-%5B%5D-"><table border="1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25.2682%;"></col><col style="width: 74.7318%;"></col></colgroup><tbody><tr><td>^</td><td>начало</td></tr><tr><td>$</td><td>конец</td></tr><tr><td>\[\]</td><td>список символов

\[1-6\] перечисление, может комбинироваться порядок роли не играет \[0-5a-fkl\]

\[- если дефис в начале, то как символ

^ Если в начале, то инверсия правила. Если не в начале, то обычный символ

</td></tr><tr><td>|</td><td>или. Желательно экранировать остальное скобками, например gr(e|a)y

внутри могут быть полноценные регулярные выражения

</td></tr><tr><td>.</td><td>Один любой символ, кроме новой строки \\n.</td></tr><tr><td>?</td><td>0 или 1 вхождение предшествующего элемента</td></tr><tr><td>+</td><td>1 и более вхождений предшествующего элемента</td></tr><tr><td>\*</td><td>0 и более вхождений предшествующего элемента</td></tr><tr><td>\\w</td><td>Любая цифра или буква

</td></tr><tr><td>\\W</td><td>все, кроме буквы или цифры</td></tr><tr><td>\\d</td><td>Любая цифра \[0-9\]</td></tr><tr><td>\\D</td><td>все, кроме цифры</td></tr><tr><td>\\xFF</td><td>шестнадцатеричное число</td></tr><tr><td>\\s</td><td>Любой пробельный символ</td></tr><tr><td>\\S</td><td>любой непробельный символ</td></tr><tr><td>\\b</td><td>Граница слова</td></tr><tr><td>\\A</td><td>начало текста</td></tr><tr><td>\\t, \\n, \\r</td><td>Символ табуляции, новой строки и возврата каретки соответственно</td></tr><tr><td>{n,m}</td><td>От n до m вхождений

{n} ровно n вхождений

{n,} от n вхождений

{,m} — от 0 до m

Иногда нужно экранировать \\{

</td></tr></tbody></table>

</div><div id="bkmrk-%D0%9C%D0%BE%D0%B4%D0%B8%D1%84%D0%B8%D0%BA%D0%B0%D1%82%D0%BE%D1%80%D1%8B">**Модификаторы**</div><div id="bkmrk-%28%3Fi%3A%D1%87%D1%82%D0%BE-%D1%82%D0%BE%29-%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D0%B2%D0%B0%D0%BB"><table border="1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 25.6548%;"></col><col style="width: 74.4644%;"></col></colgroup><tbody><tr><td>(?i:что-то)</td><td>интервальный модификатор, отключающий поиск с учетом регистра для "что-то"</td></tr><tr><td>(?:что-то)</td><td>Атомарная группировка проходит как обычно, но когда процесс поиска выходит за пределы конструкции (за закрывающую круглую скобку), все сохраненные состояния удаляются.</td></tr><tr><td>(?P&lt;word&gt;...)</td><td>Именованная группа, в данном случае имя - word

m.group('word') - пример обращения к группе

</td></tr></tbody></table>

</div><div id="bkmrk-%D0%9E%D0%BF%D0%B5%D1%80%D0%B5%D0%B6%D0%B0%D1%8E%D1%89%D0%B0%D1%8F-%D0%BF%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D0%BA%D0%B0">**Опережающая проверка**</div><div id="bkmrk-%28%3F%3C%3D...%29-%D0%94%D0%BE%D0%BB%D0%B6%D0%BD%D0%BE-%D1%81%D0%BE%D0%B2%D0%BF"><table border="1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 26.012%;"></col><col style="width: 74.1072%;"></col></colgroup><tbody><tr><td>(?&lt;=...)</td><td>Должно совпасть слева (Позитивная ретроспективная проверка), в Python - fixed length. </td></tr><tr><td>(?&lt;!...)</td><td>Не должно совпасть слева (Негативная ретроспективная проверка), в Python не поддерживается.</td></tr><tr><td>(?=...)</td><td>Должно совпасть справа (Позитивная опережающая проверка).</td></tr><tr><td>(?!...)</td><td>Не должно совпасть справа (Негативная опережающая проверка).</td></tr><tr><td>\\g&lt;...&gt;</td><td>группы при обратных ссылках, g&lt;1&gt; группа 1 </td></tr></tbody></table>

</div><div id="bkmrk-"></div><div id="bkmrk--1"></div><div id="bkmrk-.%2A%3F%C2%A0-%22%D0%BB%D0%B5%D0%BD%D0%B8%D0%B2%D1%8B%D0%B9%22-%D0%BF%D0%BE%D0%B8%D1%81%D0%BA">.*? "ленивый" поиск</div><div id="bkmrk-%D0%90%D1%82%D1%80%D0%B8%D0%B1%D1%83%D1%82%D1%8B">Атрибуты</div><div id="bkmrk-%5Cp%7B%D0%B0%D1%82%D1%80%D0%B8%D0%B1%D1%83%D1%82%7D">\p{атрибут}</div><div id="bkmrk-%5Cp%7Bl%7D-%5Cp%7Bletter%7D-%E2%80%93-%D1%81">\p{L} \p{Letter} – символы, считающиеся буквами</div><div id="bkmrk-%5Cp%7Bm%7D-%5Cp%7Bmark%7D-%E2%80%93-%D1%80%D0%B0%D0%B7">\p{M} \p{Mark} – различные символы, существующие не самостоятельно, а лишь в сочетании с другими базовыми символами (диакритические знаки, рамки и т. д.)</div><div id="bkmrk-%5Cp%7Bz%7D-%5Cp%7Bseparator%7D-">\p{Z} \p{Separator} – символы, выполняющие функции разделителей, но не имеющие собственного визуального представления (разнообразные пробелы и т. д.)</div><div id="bkmrk-%5Cp%7Bs%7D-%5Cp%7Bsymbol%7D-%E2%80%93-%D1%80">\p{S} \p{Symbol} – различные декоративные элементы и знаки</div><div id="bkmrk-%5Cp%7Bn%7D-%5Cp%7Bnumber%7D-%E2%80%93-%D1%86">\p{N} \p{Number} – цифры</div><div id="bkmrk-%5Cp%7Bp%7D-%5Cp%7Bpunctuation">\p{P} \p{Punctuation} – знаки препинания</div><div id="bkmrk-%5Cp%7Bc%7D-%5Cp%7Bother%7D-%E2%80%93-%D0%BF%D1%80">\p{C} \p{Other} – прочие символы (редко используется при работе с обычным текстом)</div><div id="bkmrk-%D0%9F%D0%BE%D1%85%D0%BE%D0%B6%D0%B5%2C-%D1%87%D1%82%D0%BE-python-%D0%BD">Похоже, что python не поддерживает</div><div id="bkmrk--2">  
</div><div id="bkmrk--3">  
</div><div class="align-center" id="bkmrk-python">**Python**</div><div id="bkmrk-%D0%A4%D0%BB%D0%B0%D0%B3%D0%B8-%D0%BF%D1%80%D0%B8-%D0%BE%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B5">Флаги при обработке</div><div id="bkmrk-re.compile%28%27...%27%2C-re">re.compile('...', re.IGNORECASE) - игнор регистра</div><div id="bkmrk-re.verbose-re.x-allo">re.VERBOSE re.X Allow inline comments and extra whitespace. </div><div id="bkmrk-re.ignorecase-re.i-d">re.IGNORECASE re.I Do case-insensitive matches.</div><div id="bkmrk-re.multiline-re.m-al">re.MULTILINE re.M Allow anchors (^ and $) to match the beginnings and ends of lines instead of matching the beginning and end of the whole text.</div><div id="bkmrk-re.dotall-re.s-allow">re.DOTALL re.S Allow dot (.) to match any character, including a newline. (The default behavior of dot is to match anything, except for a newline.)</div><div id="bkmrk--4">  
</div><div id="bkmrk-%D0%9D%D1%8E%D0%B0%D0%BD%D1%81%D1%8B-python">Нюансы python</div><div id="bkmrk-%D0%92-%D0%B2%D0%BE%D0%B7%D0%B2%D1%80%D0%B0%D1%89%D0%B0%D0%B5%D0%BC%D0%BE%D0%BC-%D0%BA%D0%BE%D1%80%D1%82%D0%B5">В возвращаемом кортеже группа 0 - максимальная группа. Группа определяется скобками. Поэтому если внутри есть () для формирования регулярки, то необходимо всю регулярку брать в скобки.</div><div id="bkmrk-%D0%92%D0%BC%D0%B5%D1%81%D1%82%D0%BE-r%27gr%28e%7Ca%29y%27-%D0%BC">Вместо r'gr(e|a)y' можно использовать f-строки, {} будет означать переменную</div><div id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%3A%C2%A0">Пример: </div><div id="bkmrk-m-%3D-re.finditer%28r%27gr">m = re.finditer(r'gr(e|a)y', 'gray grey')</div><div id="bkmrk-%C2%A0-%C2%A0-for-ma-in-m%3A"> for ma in m:</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-print%28ma.gro"> print(ma.groups())</div><div id="bkmrk-%23%28%27a%27%2C%29">\#('a',)</div><div id="bkmrk-%23%28%27e%27%2C%29">\#('e',)</div><div id="bkmrk-m-%3D-re.finditer%28r%27%28g">m = re.finditer(r'(gr(e|a)y)', 'gray grey')</div><div id="bkmrk-%C2%A0-%C2%A0-for-ma-in-m%3A-1"> for ma in m:</div><div id="bkmrk-%C2%A0-%C2%A0-%C2%A0-%C2%A0-print%28ma.gro-1"> print(ma.groups())</div><div id="bkmrk-%23%28%27gray%27%2C-%27a%27%29">\#('gray', 'a')</div><div id="bkmrk-%23%28%27grey%27%2C-%27e%27%29">\#('grey', 'e')</div><div id="bkmrk--5">  
</div><div id="bkmrk-%D0%9F%D0%BE-%D1%83%D0%BC%D0%BE%D0%BB%D1%87%D0%B0%D0%BD%D0%B8%D1%8E-%D1%81%D0%B8%D0%BC%D0%B2%D0%BE%D0%BB%D1%8B">По умолчанию символы повторения жадные (greedy). Если нужно отключить жадность, достаточно добавить знак вопроса после символов повторения: match = re.search(r'&lt;.*?&gt;', line)</div><div id="bkmrk--6">  
</div><div id="bkmrk-re.sub%28pattern%2C-repl">re.sub(pattern, repl, string, count=0) Заменить в строке string все непересекающиеся шаблоны pattern на repl</div>

# Grep, awk, sed

Общий синтаксис для sed и awk: command \[options\] script filename

Если скрипт определяется в команде, то одинарные кавычки.

-f filename имя скрипта

Каждая инструкция скрипта состоит из блока шаблона и процедуры. Шаблон отделяется /. Строки скрипта интерпретируются последовательно, если найдено соответствие шаблону - исполняется процедура. Применяется каждая строка скрипта. Sed выводит строку, поведение awk нужно определять в скрипте. В скриптовом файле шаблон пишется без кавычек

```
cat sedscr
s/ MA/, Massachusetts/
s/ PA/, Pennsylvania/
s/ CA/, California/
s/ VA/, Virginia/
s/ OK/, Oklahoma/
```

**grep**

Регулярки пишутся в двойных кавычках

grep "\[PV\]A" testone.txt

grep "10\\{1,2\\}1" testone.txt

grep по умолчанию не понимает спецсимволы типа \\d,... \\d интерпретируется как символ d. Чтобы понимал:

```bash
echo "123 abc" | grep -P '\d'
```

<table border="1" id="bkmrk-%D0%9E%D0%BF%D1%86%D0%B8%D1%8F-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5--e-%D0%9C%D0%BD" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22.7977%;"></col><col style="width: 77.3215%;"></col></colgroup><thead><tr><td class="align-center">Опция</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>-P</td><td>Регулярка в стиле Perl

</td></tr><tr><td>-o</td><td>Показать только совпадения

</td></tr></tbody></table>

**sed**

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

<table border="1" id="bkmrk-%D0%9E%D0%BF%D1%86%D0%B8%D1%8F-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5--e-%D0%9C%D0%BD-1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22.7977%;"></col><col style="width: 77.3215%;"></col></colgroup><thead><tr><td class="align-center">Опция</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>-e</td><td>Многострочный скрипт в командной строке,

sed -e ’s/ MA/ Massachusetts/’ -e ’s/ PA/ Pennsylvania/’ list

Но проще через ;

sed ’s/ MA/ Massachusetts/; s/ PA/ Pennsylvania/’ list

</td></tr></tbody></table>

Правила для скрипта

<table border="1" id="bkmrk-%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%BE-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-s-%D0%97" style="border-collapse: collapse; width: 100%; height: 275.766px;"><colgroup><col style="width: 33.4826%;"></col><col style="width: 66.5174%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">Правило</td><td class="align-center" style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 80.1875px;"><td style="height: 80.1875px;">s</td><td style="height: 80.1875px;">Замена, файл меняется

sed 's/ MA/ Massachusetts/' testone.txt

<span style="color: rgb(224, 62, 45);">! случай 's/ MA/, Massachusetts/' не сработал, получилось без запятой. Какой-то нюанс.</span>

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">-n 's/.../.../p'</td><td style="height: 29.7969px;">Выводится только соответствующие шаблону, файл не меняется.

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">'s/.../.../g'</td><td style="height: 29.7969px;">Все вхождения в строке

</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">'s/что/на что/число'</td><td style="height: 46.5938px;">Заменить каждое вхождение с номером Число.

's/foo/bar/2' - второе вхождение foo

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">sed 's/что/на что/w имяфайла'</td><td style="height: 29.7969px;">сохранить после замены в имяфайла

</td></tr><tr><td>/Sebastopol/s/CA/California/g</td><td>Замена CA на California если есть слово Sebastopol

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">'/.../d'</td><td style="height: 29.7969px;">Удаление

</td></tr></tbody></table>

**awk**

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

$0 полная строка

$1, $2, ... номера полей. <span style="color: rgb(224, 62, 45);">Вне awk скрипта $ - параметр bash!</span>

Различные форматы применяются последовательно, например

```bash
awk '/VA/ { print $1}' testone.txt
```

Для строк, в которых есть VA, будет выведено первое поле.

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

```bash
{LastState = $1}
```

<table border="1" id="bkmrk-%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5--f" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22.7977%;"></col><col style="width: 77.3215%;"></col></colgroup><thead><tr><td class="align-center">Параметр</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>-F</td><td>Разделитель, например

awk -F, '{ print $1}' testone.txt

</td></tr><tr><td> </td><td> </td></tr></tbody></table>

Правила для скрипта формата {}

<table border="1" id="bkmrk-%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%BE-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-pri" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22.7977%;"></col><col style="width: 77.3215%;"></col></colgroup><thead><tr><td class="align-center">Правило</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>print</td><td>Вывод данных

awk '{ print $1 }' testone.txt

awk '{ print "Name: " $1}' testone.txt

</td></tr><tr><td>;</td><td>Разделитель команд внутри

awk '{ print $1; print $2 }' testone.txt

</td></tr><tr><td>  
</td><td></td></tr></tbody></table>

Правила для скрипта формата //

<table border="1" id="bkmrk-%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%BE-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-%2Fso" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22.7977%;"></col><col style="width: 77.3215%;"></col></colgroup><thead><tr><td class="align-center">Правило</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>/some/</td><td>Поиск с выводом соответствия шаблону

awk ’/some/’ list

</td></tr><tr><td> </td><td> </td></tr></tbody></table>

Пример совмещения задач

```bash
! /bin/sh
awk -F, '{print $4 ", " $0}' $* |
sort |
awk -F, '
$1 == LastState {print "\t" $2}
$1 != LastState {LastState = $1; print $1; print "\t" $2}
'
```

Вывод

```
 CA
         Amy Wilde
 Massachusetts
         Eric Adams
         John Daggett
         Sal Carpenter
 OK
         Orville Thomas
 PA
         Terry Kalkas
 VA
         Alice Ford
         Hubert Sims
```

# HAProxy

**Step 1:** Understand Your Goal HAProxy is usually placed in front of:

- Kubernetes API Server (6443) → if you want HA for multiple masters.
- Kubernetes Services / Ingress → if you want HAProxy as a load balancer for apps exposed on worker nodes.

Since you mentioned only one master ([192.168.0.106](https://192.168.0.106/)), I’ll assume:

- You want HAProxy ([192.168.0.104](https://192.168.0.104/)) to forward external traffic to services running in the cluster (through NodePorts or Ingress).

**Step 2:** Find a Service to Expose On your cluster, check services:

```
kubectl get svc -A
```

For example, if you have nginx service exposed as NodePort 30080, it’ll be reachable at: [http://192.168.0.106:30080](http://192.168.0.106:30080)

**Step 3:** Install HAProxy On the HAProxy VM ([192.168.0.104](https://192.168.0.104/)):

```
sudo apt update
sudo apt install haproxy -y
```

**Step 4:** Configure HAProxy

Edit config:

```
sudo nano /etc/haproxy/haproxy.cfg
```

Example for forwarding traffic from port 80 → nginx NodePort 30080:

```
global
  log /dev/log local0
  log /dev/log local1 notice
  daemon
  maxconn 2048

defaults 
  log global
  mode http
  option httplog
  option dontlognull
  timeout connect 5s
  timeout client 50s
  timeout server 50s

frontend http_front
  bind *:80
  default_backend nginx_backend

backend nginx_backend
  balance roundrobin
  server k8s-master 192.168.0.106:30080 check
```

**Step 5:** Restart HAProxy

```
sudo systemctl restart haproxy
sudo systemctl enable haproxy
```

Now, when you access: [http://192.168.0.104/](http://192.168.0.104/) HAProxy will forward to your Kubernetes service ([192.168.0.106:30080](https://192.168.0.106:30080/)). If you later add multiple worker nodes, you can just add them to the backend:

```
backend nginx_backend
  balance roundrobin
  server k8s-worker1 192.168.0.107:30080 check
  server k8s-worker2 192.168.0.108:30080 check
```

**5. Optional – Multiple Apps**

If you want HAProxy to serve multiple apps (different hostnames or paths), you can extend the config:

```
frontend http_front
  bind *:80
  acl host_app1 hdr(host) -i app1.local
  acl host_app2 hdr(host) -i app2.local
  use_backend app1_backend if host_app1
  use_backend app2_backend if host_app2
  default_backend app1_backend

backend app1_backend
  server k8s-master 192.168.0.106:30080 check

backend app2_backend
  server k8s-master 192.168.0.106:30081 check
```

This way HAProxy acts like a simple Ingress Controller replacement.

# Tmux

Интересный эмулятор мультитерминала. Запуск в Kali:

```
$ tmux
```

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

[![изображение.png](http://bobrobotirk.ru/uploads/images/gallery/2025-10/scaled-1680-/WnEizobrazenie.png)](http://bobrobotirk.ru/uploads/images/gallery/2025-10/WnEizobrazenie.png)

Каждое окно может делиться на панели. Панели - отдельные терминалы.

Для управления используется интересный подход: сначала нажимается Ctrl+b затем командная комбинация:

<table border="1" id="bkmrk-%2C-%D0%9F%D0%B5%D1%80%D0%B5%D0%B8%D0%BC%D0%B5%D0%BD%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D0%BE%D0%BA%D0%BD%D0%BE" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 28.3929%;"></col><col style="width: 71.7263%;"></col></colgroup><tbody><tr><td>,</td><td>Переименовать окно</td></tr><tr><td>c</td><td>Создать новое окно</td></tr><tr><td>0...</td><td>Переключиться в заданное окно</td></tr><tr><td>%</td><td>Разделить окно на панели вертикально</td></tr><tr><td>" (двойная кавычка)</td><td>Разделить окно на панели горизонтально</td></tr><tr><td>Клавиши со стрелками</td><td>Переключиться на панель</td></tr><tr><td>\[, затем стрелки. Esc - выход из этого режима</td><td>Прокрутка истории</td></tr><tr><td>Колесико мыши</td><td>Прокрутка истории</td></tr></tbody></table>

# Логгирование

##### **Демоны логгирования**

WAF между логгером и фаером.

<table border="1" id="bkmrk-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%BD%D1%8B%D0%B5-rsyslog%2C-j" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50%;"></col><col style="width: 50%;"></col></colgroup><tbody><tr><td>системные</td><td>rsyslog, journalctl</td></tr><tr><td>ИБ</td><td>auditd</td></tr><tr><td>WAF</td><td>modsecurity</td></tr></tbody></table>

##### **Системное логгирование**

Есть rsyslog и journalctl. journalctl постепенно заменяет rsyslog.

**Journalctl**

<table border="1" id="bkmrk-sudo-journalctl-%D0%9F%D1%80%D0%BE%D1%81" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50%;"></col><col style="width: 50%;"></col></colgroup><tbody><tr><td>sudo journalctl</td><td>Просмотреть системный журнал</td></tr><tr><td>sudo journalctl -u ssh</td><td>Логи конкретной службы</td></tr><tr><td>sudo journalctl -f</td><td>Последние логи (как tail -f) </td></tr><tr><td>sudo journalctl -p err</td><td>Только ошибки</td></tr></tbody></table>

Journalctl по умолчанию сохраняет логи в ОП

**Rsyslog**

Rsyslog нужен, если:

- нужны обычные текстовые файлы /var/log/auth.log, /var/log/syslog;
- необходима ротацию логов через logrotate;
- нужно отправлять логи на внешний syslog-сервер (например, в SIEM).

Установка:

```
sudo apt install rsyslog
sudo systemctl enable --now rsyslog
```

Типы логов, настраивается.

<div class="table-scroll" id="bkmrk-%D0%9D%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B6%D1%83%D1%80%D0%BD%D0%B0%D0%BB%D0%B0-%D0%A0%D0%B0%D1%81"><table align="center" border="1" cellpadding="1" cellspacing="1" style="width: 94.6429%; height: 301.766px;"><thead><tr style="height: 29.7969px;"><th style="width: 44.8363%; height: 29.7969px;">Название журнала</th><th style="width: 55.1637%; height: 29.7969px;">Расположение</th></tr></thead><tbody><tr style="height: 29.7969px;"><td style="width: 44.8363%; height: 29.7969px;">Аутентификация

</td><td style="width: 55.1637%; height: 29.7969px;">/var/log/auth.log</td></tr><tr style="height: 29.7969px;"><td style="width: 44.8363%; height: 29.7969px;">Ядро

</td><td style="width: 55.1637%; height: 29.7969px;">/var/log/kern.log</td></tr><tr style="height: 29.7969px;"><td style="width: 44.8363%; height: 29.7969px;">Демоны

</td><td style="width: 55.1637%; height: 29.7969px;">/var/log/daemon.log</td></tr><tr style="height: 29.7969px;"><td style="width: 44.8363%; height: 29.7969px;">Запуск

</td><td style="width: 55.1637%; height: 29.7969px;">/var/log/boot.log</td></tr><tr style="height: 46.5938px;"><td style="width: 44.8363%; height: 46.5938px;">Обновления

</td><td style="width: 55.1637%; height: 46.5938px;">/var/log/apt/history.log  
/var/log/dpkg.log</td></tr><tr style="height: 29.7969px;"><td style="width: 44.8363%; height: 29.7969px;">Cron

</td><td style="width: 55.1637%; height: 29.7969px;">/var/log/cron.log</td></tr><tr style="height: 29.7969px;"><td style="width: 44.8363%; height: 29.7969px;">Драйверы</td><td style="width: 55.1637%; height: 29.7969px;">/var/log/dmesg</td></tr></tbody></table>

</div>Размещение настроечных файлов

<table border="1" id="bkmrk-%2Fetc%2Frsyslog.conf-%D0%9E%D1%81" style="border-collapse: collapse; width: 100%; height: 331.75px;"><colgroup><col style="width: 25.0359%;"></col><col style="width: 74.9641%;"></col></colgroup><tbody><tr style="height: 46.5938px;"><td style="height: 46.5938px;">/etc/rsyslog.conf</td><td style="height: 46.5938px;">Основной файл настроек. Дополнительные файлы настроек подключаются в алфавитном порядке из /etc/rsyslog.d/\*.conf</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">/etc/rsyslog.d/50-default.conf</td><td style="height: 29.7969px;">Создается при установке. Базовые правила. Можно комментировать. Например ```
auth,authpriv.*         /var/log/auth.log
*.*;auth,authpriv.none  -/var/log/syslog
mail.*                  -/var/log/mail.log
cron.*                  -/var/log/cron.log
```

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Рекомендованные файлы</td><td style="height: 29.7969px;">  
</td></tr><tr style="height: 82.9844px;"><td style="height: 82.9844px;">10-base.conf</td><td style="height: 82.9844px;">Базовые глобальные настройки ```
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
```

</td></tr><tr style="height: 82.9844px;"><td style="height: 82.9844px;">20-remote.conf</td><td style="height: 82.9844px;">Отправка логов на удалённый сервер ```
*.* @@syslog.example.com:514
```

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">30-ssh.conf</td><td style="height: 29.7969px;">Логи от sshd ```
if ($programname == 'sshd' or $programname == 'sudo') then /var/log/security.log
& stop
```

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">40-sudo.conf</td><td style="height: 29.7969px;">Логи sudo ```
if ($programname == 'sudo') then /var/log/sudo.log
```

</td></tr><tr><td>99-local.conf</td><td>Локальные мелкие правки, фильтры, формат вывода</td></tr></tbody></table>

**Структура логов:** timestamp hostname program identifier log level message content. Можно добавлять хост, с которого было отправлено сообщение.

**Типы событий:**

- kernel: загрузка модулей, аппаратные ошибки,
- authentication: попытки входа, изменения паролей
- service and daemon: работа системных служб и демонов. Запуск служб, ошибки и предупреждения
- network: подключение сетевых интерфейсов, обнаружение сетевых атак, изменения настроек сети
- system: общая системная информация о работе операционной системы. Запуск системы, события загрузки, ошибки файловых систем.
- logging: события журналирования. Создание и ротация журналов, ошибки записи в журналы.

**Конфигурация:** /etc/rsyslog.conf (/etc/syslog.conf). Каждая строка в формате &lt;фильтр&gt; &lt;действие&gt;. Фильтр определяет, какие сообщения должны быть обработаны, а действие определяет, что делать с этими сообщениями (например, записать их в определенный файл журнала). Пример конфигурации syslog:

```
# Маркировать сообщения ядра, которые требуют прямого вывода в файл /dev/console
kern.warning;*.err;authpriv.none;mail.crit       /dev/console

# Записывать все сообщения уровня emergency и выше в файл /var/log/emergency.log
*.emerg                         /var/log/emergency.log

# Записывать все сообщения уровня info и выше в файл /var/log/messages
*.info;mail.none;authpriv.none;cron.none                /var/log/messages

# Записывать все сообщения уровня notice и выше в файл /var/log/messages
*.notice                        /var/log/messages

# Записывать все сообщения уровня warning и выше в файл /var/log/messages
*.warn                          /var/log/messages

# Записывать все сообщения уровня error и выше в файл /var/log/errors.log
*.err                           /var/log/errors.log

# Записывать все сообщения уровня crit и выше в файл /var/log/critical.log
*.crit                          /var/log/critical.log

# Записывать все сообщения уровня alert и выше в файл /var/log/alert.log
*.alert                         /var/log/alert.log

# Записывать все сообщения уровня debug и выше в файл /var/log/debug.log
*.debug                         /var/log/debug.log

# Записывать все сообщения от локальных7 в файл /var/log/local7.log
local7.*                        /var/log/local7.log
```

Для изменения формата сообщений изменяем файл sudo nano /etc/rsyslog.conf

```
$template RFC3164fmt,"<%PRI%>%TIMESTAMP% %HOSTNAME% %syslogtag%%msg%\n" #добавляем шаблон
auth,authpriv.*                 /var/log/auth.log;RFC3164fmt #привязываем шаблон к лог файлу
```

Приоритет syslog считается по формуле facility \* 8 + severity. Facility имеет заданные значения, и определяет к какому типу относится событие. Например, facility #0 относится к событиям ядра, а #4 - к событиям систем безопасности.

Severity отвечает за уровень критичности событий, где 0 это максимум(emergency), а уровень 7 - малоинформативные данные, используемые для отладки(debug).

В нашем примере число 38 получается за счет facility 4 и severity 6 (info).

**Журнал аутентификации**

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

Типы событий в журнале auth.log

**Успешная аутентификация** (Successful Authentication). Это может быть вход через SSH, консоль, графический интерфейс и т. д.

```
Feb 11 14:23:45 server sshd[1234]: Accepted password for user123 from 192.168.1.100 port 12345 ssh2
```

**Неудачная аутентификация** (Failed Authentication). Это может быть вызвано неправильным паролем, недействительным пользователем и т. д.

```
Feb 11 14:30:10 server sshd[1234]: Failed password for invaliduser from 192.168.1.50 port 54321 ssh2
```

**Изменение пароля** (Password Changes). Это позволяет отслеживать изменения паролей и управлять безопасностью.

```
Feb 11 15:45:32 server passwd[5678]: password for user123 has been changed
```

**Изменение параметров аутентификации** (Authentication Configuration Changes).

```
Feb 11 16:12:20 server login[9876]: PAM (login) session opened for user123 by (uid=0)
```

**Запуск или остановка служб аутентификации** (Start/Stop of Authentication Services).

```
Feb 11 17:00:05 server systemd: Starting OpenSSH server daemon...
```

**События, связанные с аутентификацией.** Проблемы с файлами авторизации, недоступность источников аутентификации, ...

```
Feb 11 18:30:15 server su: pam_unix(su:session): session opened for user456 by user123(uid=1000)
```

**Журнал ядра**

/var/log/kern.log Содержит сообщения ядра Linux, такие как сообщения об ошибках, предупреждения и другую важную информацию о работе ядра.

Типы событий

**Сообщения ядра** (Kernel Messages). Основные сообщения. Загрузка ядра, проблемах с аппаратным обеспечением, ошибках файловых систем, сетевых событиях, а также другие системные события, связанные с работой ядра.

```
Feb 11 08:30:15 server kernel: [    0.000000] Linux version 5.10.0-14-generic (buildd@lcy01-amd64-017) (gcc (Ubuntu 10.3.0-1ubuntu1) 10.3.0, GNU ld (GNU Binutils for Ubuntu) 2.36.1) #15-Ubuntu SMP Fri Dec 31 14:49:08 UTC 2021 (Ubuntu 5.10.0-14.15-generic 5.10.21)
```

**Сообщения о загрузке и выгрузке модулей ядра** (Kernel Module Loading and Unloading). Полезно для отслеживания загрузки различных драйверов и модулей ядра.

```
Feb 11 08:31:45 server kernel: [    3.141592] Module 'nvidia-drm' is loaded.
```

**Сообщения об ошибках ввода-вывода** (I/O Errors). Если возникают проблемы с вводом-выводом, такие как ошибки чтения или записи на диске, это может быть отражено в kern.log. Помогает отслеживать проблемы с дисками и файловыми системами.

```
Feb 11 09:12:20 server kernel: [  405.837234] ata1: EH complete
```

**Сообщения об ошибках памяти** (Memory Errors). Например ошибка ECC (Error-Correcting Code) или ошибка доступа к памяти.

```
Feb 11 09:55:10 server kernel: [ 1025.556789] EDAC MC0: 1 CE error on CPU0 memory controller (channel 0 /channel 1 = 0x1/0x0): 0x0000 (corrected)
```

**Сообщения о загрузке и выгрузке ядра** (Kernel Boot and Shutdown Messages). Включает сообщения о запуске служб и драйверов. Это полезно для отслеживания процесса загрузки и выявления проблем.

```
Feb 11 10:00:05 server kernel: [    0.000000] Initializing cgroup subsys cpuset
```

**Другие системные сообщения ядра** (Other Kernel System Messages). В kern.log также могут записываться другие системные сообщения, связанные с работой ядра, такие как события управления питанием, управления процессором и т.д.  
Feb 11 10:15:30 server kernel: \[ 765.320123\] ACPI: Battery Slot \[BAT0\] (battery present)

**Журнал демонов**

/var/log/daemon.log Сообщения системных служб и демонов, таких как сервисы SSH, FTP, DHCP и другие.

Типы событий

**Запуск и остановка демонов** (Daemon Start and Stop).

```
Feb 11 08:30:15 server systemd[1]: Started OpenSSH server daemon.
```

**Сообщения об ошибках служб** (Service Error Messages). Могут содержать информацию о проблемах с конфигурацией, недоступности ресурсов и других критических ситуациях.

```
Feb 11 09:45:20 server apache2[1234]: [error] (28)No space left on device: Cannot create SSLMutex
```

**Информационные сообщения** (Informational Messages). Содержат отчеты о выполненных действиях, успешных операциях и других системных событиях.

```
Feb 11 10:00:05 server cron[5678]: (CRON) INFO (Running @reboot jobs)
```

**Сообщения об ошибках в работе демонов** (Daemon Error Messages). Содержат информацию о сбоях, падениях и других аномальных ситуациях, происходящих в процессе выполнения служб.

```
Feb 11 11:15:30 server postfix[9876]: fatal: parameter "smtpd_sender_login_maps": file too large
```

**Сообщения о подключении и отключении к демонам** (Connection and Disconnection Messages). Отслеживают активность пользователя или других систем, пытающихся взаимодействовать с демоном.

```
Feb 11 12:30:45 server sshd[2345]: Accepted publickey for user123 from 192.168.1.100 port 12345 ssh2: RSA SHA256:abc123
```

**Другие системные сообщения** (Other System Messages). Не подпадающие под вышеперечисленные категории, но относящиеся к работе демонов и служб в системе. Это может включать сообщения о перезагрузках, обновлениях и других системных событиях.

```
Feb 11 13:45:10 server systemd[1]: Reloading.
```

**Журнал запуска**

/var/log/boot.log Содержит информацию о процессе загрузки системы, включая загрузку ядра и инициализацию устройств.

Типы событий

**Сообщения ядра** (Kernel Messages). Сообщения, генерируемые ядром Linux во время загрузки. Информация об оборудовании, обнаруженном ядром, настройка ядра, загрузка драйверов и другие системные события, происходящие во время загрузки.

```
[    0.000000] Linux version 5.4.0-91-generic (buildd@lgw01-amd64-035) (gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)) #102-Ubuntu SMP Fri Aug 13 23:50:30 UTC 2021 (Ubuntu 5.4.0-91.102-generic 5.4.143)
```

**Сообщения об инициализации системных служб** (Service Initialization Messages). Запуск системных служб и демонов, которые запускаются во время загрузки операционной системы.

```
[    4.105167] systemd[1]: Started Forward Password Requests to Wall Directory Watch.
```

**Сообщения об ошибке загрузки** (Boot Error Messages).

```
[FAILED] Failed to start Network Time Synchronization.
```

**Информация о загрузочных скриптах и конфигурации** (Boot Scripts and Configuration Information). Выполнение загрузочных скриптов, настройках загрузчика и других параметрах конфигурации, применяемых при загрузке системы.

```
[    0.820671] ACPI: Core revision 20200326
```

**Сообщения об успешном завершении загрузки** (Boot Completion Messages). В конце файла boot.log обычно содержится информация о завершении процесса загрузки. Это может включать сообщения об успешном запуске всех требуемых служб и демонов.

```
[    5.091481] systemd[1]: Started Daily apt download activities.
```

**Другие системные сообщения** (Other System Messages). Например, изменения параметров ядра или настройках безопасности.

```
[    6.220215] random: crng init done
```

**Журнал обновлений**

APT (Advanced Package Tool), основной лог: /var/log/apt/ Файл журнала: /var/log/apt/history.log Старые логи обновления: /var/log/apt/term.log

**Структура записи:**

<table border="1" id="bkmrk-start-date-commandli" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 37.2025%;"></col><col style="width: 62.9167%;"></col></colgroup><tbody><tr><td>Start-Date</td><td>  
</td></tr><tr><td>Commandline:</td><td>Commandline: apt install some-package

Commandline: apt remove some-package

Commandline: apt upgrade

Commandline: apt-get autoremove

</td></tr><tr><td>Requested-By</td><td>Requested-By: user123 (1000)</td></tr><tr><td>&lt;Type of operation&gt;</td><td>Install: some-package:amd64 (version)

Remove: some-package:amd64 (version),

Upgrade: some-package:amd64 (old-version -&gt; new-version),

</td></tr><tr><td>End-Date</td><td>  
</td></tr></tbody></table>

Пример установки пакета:

```
Start-Date: 2023-08-15  15:30:45
Commandline: apt install some-package
Requested-By: user123 (1000)
Install: some-package:amd64 (version), another-package:amd64 (version),
End-Date: 2023-08-15  15:31:10
```

DPKG (Debian Package Manager)

Пример dpkg.log:

```
2024-02-10 15:21:05 status installed firefox:amd64 100.0.0-0ubuntu1
2024-02-10 15:21:05 status installed firefox-locale-en:amd64 100.0.0-0ubuntu1
```

Запись в логе содержит статус установки, времени и дате операции, версии и архитектуре установленных пакетов.

Журнал установки: /var/log/dpkg.log

Каждая запись в этом файле содержит дату и время операции (например, 2023-08-15 15:38:55), за которыми следует описание операции и подробности о пакете. Например, "installed" указывает на установку пакета, "remove" на удаление, "upgrade" на обновление, "configure" на настройку, а "status" отображает текущее состояние пакета. Далее указывается имя пакета, его архитектура (например, amd64), его версия и новая версия (если применимо). В случае ошибок установки или зависимостей также выводятся соответствующие сообщения об ошибках. Пример ошибки:

```
2023-08-15 15:38:55 configure some-package:amd64 version <none>
dpkg: dependency problems prevent configuration of some-package:
some-package depends on another-package (>= required-version); however:
Package another-package is not installed.
```

YUM (Yellowdog Updater Modified) и DNF (Dandified YUM) (Fedora, CentOS и др.)

Основной лог: /var/log/yum.log

Каждая запись начинается со времени лога - например, Oct 05 18:00:19. После временной метки указывается действие, например, "Installed" для установки пакета, "Erased" для удаления и т. д. Затем идет имя пакета, его версия и, если применимо, старая версия (для обновлений). В случае ошибки указывается сообщение об ошибке.

Pacman, используемый (Arch Linux)

Основной лог: /var/log/pacman.log

В каждой записи \[YYYY-MM-DD HH:MM\] обозначает дату и время операции. После этого указывается действие, такое как "installed" для установки пакета, "removed" для удаления и т. д. Затем идет имя пакета, его версия и, если применимо, старая версия (для обновлений). В случае ошибок указывается сообщение об ошибке.

**Журнал cron**

/var/log/cron.log Запланированные задания, успешные и неудачные выполнения, сообщения или ошибки, связанные с выполнением этих заданий.

```
Feb 10 15:00:01 DEVICE_NAME CRON[1234]: (root) CMD (/usr/bin/php /path/to/script.php)
```

**Журнал драйверов**

/var/log/dmesg Сообщения, сгенерированные ядром во время загрузки системы, информация о оборудовании и драйверах.

```
[    0.000000] Linux version 5.4.0-91-generic (buildd@lcy01-amd64-016) (gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)) #102-Ubuntu SMP Fri Nov 5 16:31:28 UTC 2021 (Ubuntu 5.4.0-91.102-generic 5.4.154)
[    0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-5.4.0-91-generic root=UUID=12345678-1234-1234-1234-1234567890ab ro quiet splash
[    0.000000] KERNEL supported cpus:
[    0.000000]   Intel GenuineIntel
[    0.000000]   AMD AuthenticAMD
[    0.000000]   Hygon HygonGenuine
[    0.000000]   Centaur CentaurHauls
[    0.000000]   zhaoxin   Shanghai
[    0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x008: 'MPX bounds registers'
...
[    1.234567] sd 0:0:0:0: [sda] Attached SCSI disk
[    1.345678] EXT4-fs (sda1): mounted filesystem with ordered data mode. Opts: (null)
[    1.456789] random: crng init done
[    1.567890] random: 7 urandom warning(s) missed due to ratelimiting
```

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

##### **ИБ аудит**

auditd — демон аудита, используется для отслеживания и регистрации действий в системе (запуск процессов, доступ к файлам и директориям, изменения конфигурации). Записывает обычно в */var/log/audit/*. Основные файлы, используемые auditd:

- **audit.log** — основной журнал аудита. Подробная информация о событиях (время, тип, идентификатор процесса, действие, выполненное пользователем, и другие детали).
- **audit.log.\[N\]** — это архивные файлы аудита, которые содержат старые записи аудита. Например, audit.log.1

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

```
```bash
type=SYSCALL msg=audit(1625525281.877:123): arch=c000003e syscall=2 success=yes exit=0 a0=7ffdf44d1eab a1=0 a2=1 a3=0 items=2 ppid=29942 pid=29943 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=2 comm="ls" exe="/bin/ls" key="audit-wazuh-r"
```<button class="copy-code-btn st-button_style_none copy-code-btn__code-block" title="Скопировать код" type="button"><svg fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19.2 3H10.2C9.2073 3 8.4 3.8073 8.4 4.8V8.4H4.8C3.8073 8.4 3 9.2073 3 10.2V19.2C3 20.1927 
                      3.8073 21 4.8 21H13.8C14.7927 21 15.6 20.1927 15.6 19.2V15.6H19.2C20.1927 15.6 21 14.7927 21 13.8V4.8C21 3.8073 
                      20.1927 3 19.2 3ZM4.8 19.2V10.2H13.8L13.8018 19.2H4.8ZM19.2 13.8H15.6V10.2C15.6 9.2073 14.7927 8.4 13.8 8.4H10.2V4.8H19.2V13.8Z" fill="#777777"></path></svg>
                  </button>
```

<div class="table-scroll" id="bkmrk-%D0%9F%D0%BE%D0%BB%D0%B5-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-type-%D0%A2"><table border="1" cellpadding="1" cellspacing="1" style="width: 94.7619%;"><thead><tr><th style="width: 24.6106%;">Поле</th><th style="width: 75.3894%;">Описание</th></tr></thead><tbody><tr><td style="width: 24.6106%;">type</td><td style="width: 75.3894%;">Тип события аудита, например, SYSCALL, CWD, EXECVE, SOCKET, и т. д.</td></tr><tr><td style="width: 24.6106%;">msg</td><td style="width: 75.3894%;">Общее сообщение аудита</td></tr><tr><td style="width: 24.6106%;">audit(timestamp:serial)</td><td style="width: 75.3894%;">Временная метка и серийный номер события аудита</td></tr><tr><td style="width: 24.6106%;">arch</td><td style="width: 75.3894%;">Архитектура процессора</td></tr><tr><td style="width: 24.6106%;">syscall</td><td style="width: 75.3894%;">Имя системного вызова, связанного с событием</td></tr><tr><td style="width: 24.6106%;">success</td><td style="width: 75.3894%;">Успешность выполнения системного вызова</td></tr><tr><td style="width: 24.6106%;">exit</td><td style="width: 75.3894%;">Код завершения системного вызова</td></tr><tr><td style="width: 24.6106%;">a0-a3  
 </td><td style="width: 75.3894%;">Регистры системного вызова</td></tr><tr><td style="width: 24.6106%;">items</td><td style="width: 75.3894%;">Количество элементов в событии аудита.</td></tr><tr><td style="width: 24.6106%;">ppid</td><td style="width: 75.3894%;">Идентификатор родительского процесса</td></tr><tr><td style="width: 24.6106%;">pid</td><td style="width: 75.3894%;">Идентификатор процесса</td></tr><tr><td style="width: 24.6106%;">auid</td><td style="width: 75.3894%;">Идентификатор аудита пользователя.</td></tr><tr><td style="width: 24.6106%;">uid</td><td style="width: 75.3894%;">Идентификатор пользователя.</td></tr><tr><td style="width: 24.6106%;">gid</td><td style="width: 75.3894%;">Идентификатор группы.</td></tr><tr><td style="width: 24.6106%;">euid/suid/fsuid</td><td style="width: 75.3894%;">Эффективный, фактический и файловый идентификаторы пользователя.</td></tr><tr><td style="width: 24.6106%;">egid/sgid/fsgid</td><td style="width: 75.3894%;">Эффективный, фактический и файловый идентификаторы группы.</td></tr><tr><td style="width: 24.6106%;">tty  
 </td><td style="width: 75.3894%;">Терминал, связанный с процессом</td></tr><tr><td style="width: 24.6106%;">ses</td><td style="width: 75.3894%;">Идентификатор сеанса аудита</td></tr><tr><td style="width: 24.6106%;">comm</td><td style="width: 75.3894%;">Имя исполняемого файла</td></tr><tr><td style="width: 24.6106%;">exe</td><td style="width: 75.3894%;">Путь к исполняемому файлу</td></tr><tr><td style="width: 24.6106%;">key</td><td style="width: 75.3894%;">Ключ, связанный с событием аудита</td></tr></tbody></table>

</div>Это общий формат, и поля могут варьироваться в зависимости от типа события и конфигурации auditd.

**Типы событий auditd**

**SYSCALL**: системные вызовы, которые выполняются в системе, такие как открытие файлов, чтение и запись данных, создание процессов и многое другое.

```
type=SYSCALL msg=audit(1625525281.877:123): arch=c000003e syscall=2 success=yes exit=0 a0=7ffdf44d1eab a1=0 a2=1 a3=0 items=2 ppid=29942 pid=29943 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=2 comm="ls" exe="/bin/ls" key="audit-wazuh-r"
```

Лог показывает успешное (success=yes) выполнение системного вызова (syscall=2), который в данном случае может быть open(), поскольку syscall=2 обычно связан с открытием файлов. Процесс с pid=29943, запущенный пользователем с auid=1000, выполняет команду ls (comm="ls" exe="/bin/ls") в текущем терминале (tty=pts0).

**CWD**: изменение текущего рабочего каталога процесса.

```
type=CWD msg=audit(1625525281.877:123):  cwd="/home/user"
```

Лог указывает на текущий рабочий каталог процесса, который в данном случае - /home/user.

**EXECVE**: запуск нового исполняемого файла с помощью системного вызова execve.

```
type=EXECVE msg=audit(1625525281.877:123): argc=3 a0="/bin/ls" a1="-la" a2="/etc"
```

Лог показывает запуск нового процесса с помощью системного вызова execve(). Процесс запускается с аргументами командной строки: /bin/ls -la /etc.

**SOCKET**: сетевые операции, такие как создание сокетов, отправка и получение данных через сокеты и т. д.

```
type=SOCKET msg=audit(1625525281.877:123): saddr=192.168.1.200 sport=12345 daddr=192.168.1.2 dport=22
```

Лог показывает на сетевое событие, где произошло установление соединения между IP-адресами 192.168.1.100 и 192.168.1.2 на портах 12345 и 22 соответственно.

**FILE**: действия с файлами и директориями, такие как открытие, чтение, запись, удаление и изменение атрибутов файлов.

type=SYSCALL msg=audit(1625525281.877:123): arch=c000003e syscall=2 success=yes exit=0 a0=7ffdf44d1eab a1=0 a2=1 a3=0 items=2 ppid=29942 pid=29943 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=2 comm="ls" exe="/bin/ls" key="audit-wazuh-r" name="/etc/passwd"

В этой записи показана успешная попытка доступа к файлу /etc/passwd с помощью системного вызова open(). Процесс с pid=29943, запущенный пользователем с auid=1000, выполняет команду ls, которая пытается получить доступ к файлу /etc/passwd.

**USER\_AUTH**: аутентификационные события, такие как входы пользователей в систему, смены паролей и другие операции аутентификации.

type=USER\_AUTH msg=audit(1625525281.877:123): pid=12345 uid=0 auid=1000 ses=2 subj=unconfined\_u:unconfined\_r:unconfined\_t:s0-s0:c0.c1023 msg='op=PAM:authentication acct="user123" exe="/usr/sbin/sshd" hostname=192.168.1.2 addr=192.168.1.3 terminal=ssh res=success'

Лог отображает успешную аутентификацию пользователя. Пользователь с идентификатором uid=0 (обычно root) успешно прошел аутентификацию с помощью SSH.

**USER\_ROLE\_CHANGE**: изменения роли пользователя.

type=USER\_ROLE\_CHANGE msg=audit(1625525281.877:123): pid=12345 uid=0 auid=1000 ses=2 subj=unconfined\_u:unconfined\_r:unconfined\_t:s0-s0:c0.c1023 msg='pam: default-context=unconfined\_u:unconfined\_r:unconfined\_t:s0-s0:c0.c1023 exe="/usr/sbin/sshd" hostname=192.168.1.2 addr=192.168.1.3 terminal=ssh res=success'

Лог указывает на успешное изменение роли пользователя. Пользователь с auid=1000 (идентификатор аудита пользователя) изменил свою роль. В данном случае, изменение роли прошло успешно (res=success).

**LOGIN/LOGOUT**: вход и выход пользователей из системы.

type=USER\_LOGIN msg=audit(1625525281.877:123): pid=12345 uid=0 auid=1000 ses=2 subj=unconfined\_u:unconfined\_r:unconfined\_t:s0-s0:c0.c1023 msg='op=login id=1000 exe="/usr/sbin/sshd" hostname=192.168.1.2 addr=192.168.1.3 terminal=ssh res=success'

Лог показывает успешный вход пользователя в систему. Пользователь с uid=0 (обычно root) успешно вошел в систему с использованием SSH.

**PROCTITLE**: изменения заголовков процесса.

type=PROCTITLE msg=audit(1625525281.877:123): proctitle="/bin/ls -la /etc"

Запись показывает аргументы командной строки процесса. Процесс с pid=123 запускает команду /bin/ls -la /etc.

**SYSTEM\_BOOT/SHUTDOWN**: события загрузки и выключения системы.

type=SYSTEM\_BOOT msg=audit(1625525281.877:123): kernel time=1234567890

Эта запись указывает на событие загрузки системы. Время загрузки ядра равно 1234567890, в формате UNIX времени.

**Отличия auditd от журналов в Linux**

1. auditd предназначен для аудита событий безопасности, а именно для мониторинга и анализа активности на системе с целью обнаружения и предотвращения угроз безопасности, когда как обычные журналы, такие как `/var/log/syslog`, обычно используются для отслеживания работы системы, включая запуск и остановку служб, сообщения о состоянии аппаратного обеспечения и другие системные события.
2. auditd обычно настраивается администратором системы для мониторинга конкретных событий безопасности в соответствии с требованиями безопасности системы, в то время как обычные журналы логов создаются и используются системой по умолчанию для регистрации различных событий и действий на системе.

**Механизмы защиты auditd**

**Проверка подписи конфигурационных файлов**

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

- Генерация ключей для подписи. Сначала необходимо сгенерировать ключи для подписи конфигурационных файлов. Эти ключи будут использоваться для создания и проверки цифровых подписей файлов. Команда генерирует ключ с именем auditd\_signing\_key

```
keyctl add key auditd_signing_key "asymmetric" "trusted:kernel" "new 0" @u
```

- Подписание конфигурационных файлов. Команда подписывает файл /etc/audit/audit.rules с использованием созданного ключа и сохраняет подпись в файл /etc/audit/audit.rules.sig.

```
openssl dgst -sha256 -sign /etc/audit/private/auditd_signing_key -out /etc/audit/audit.rules.sig /etc/audit/audit.rules
```

- Настройка проверки подписи.   
    ```
    auditctl -b 1024 -S verify -k auditd_conf -F subj_type=auditd_t
    ```

Команда настраивает проверку подписи для конфигурационных файлов auditd. Она устанавливает ограничение длины подписи до 1024 байт, определяет тип события verify, связывает событие с ключевой меткой auditd\_conf и определяет тип подсубъекта auditd\_t для указания на процесс auditd.

**Ограничение доступа к сокетам и файлам**

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

```
# Разрешить доступ к сокетам auditd
sudo semanage permissive -a auditd_t
# Разрешить доступ к файлам auditd
sudo semanage fcontext -a -t auditd_exec_t "/путь/к/auditd(/.*)?"
sudo restorecon -R -v /путь/к/auditd
semanage permissive -a auditd_t
```

semanage: утилита для управления SELinux.

  
permissive: параметр указывает, что типу процесса разрешается выполнение в режиме "поощряемой допускающей политики", что означает, что SELinux регистрирует попытки доступа, но не блокирует их.  
-a auditd\_t: указывает, что типу процесса auditd\_t (тип, используемый для auditd) разрешается выполнение в режиме "поощряемой допускающей политики".  
 semanage permissive -a auditd\_t позволяет разрешить типу процесса auditd\_t выполняться в режиме "поощряемой допускающей политики", что позволяет SELinux регистрировать попытки доступа к ресурсам, связанным с auditd, но не блокирует их.

semanage fcontext -a -t auditd\_exec\_t "/путь/к/auditd(/.\*)?"

fcontext: команда для управления контекстами файлов.  
-a: параметр указывает на добавление нового контекста файла.  
-t auditd\_exec\_t: указывает новый контекст файла как auditd\_exec\_t, что означает, что файл должен быть выполнимым и относиться к типу auditd\_t.  
"/путь/к/auditd(/.\*)?": это регулярное выражение, которое указывает на применение этого контекста ко всем файлам и директориям внутри /путь/к/auditd.  
Эта команда добавляет новый контекст файла для всех файлов и директорий в указанном пути (/путь/к/auditd) и его поддиректориях. Это обеспечивает правильный контекст SELinux для файлов, используемых auditd.

restorecon -R -v /путь/к/auditd

restorecon: команда для восстановления контекста SELinux файлов и директорий.  
-R: рекурсивно применяет команду ко всем файлам и поддиректориям указанного пути.  
-v: подробный вывод, который показывает изменения, внесенные командой.  
Эта команда рекурсивно восстанавливает контекст SELinux для всех файлов и директорий в указанном пути (/путь/к/auditd) и его поддиректориях, учитывая новый контекст, установленный ранее с помощью semanage fcontext.

**Отслеживание изменений конфигурации**  
auditd может логировать события, связанные с изменениями его собственной конфигурации, что позволяет обнаруживать попытки изменения параметров аудита без разрешения. Создайте файл правил аудита, который будет отслеживать изменения в конфигурационных файлах auditd. Например, файл с именем auditd-config.rules и запишите в него правило:

-w /etc/audit/audit.rules -p wa -k auditd\_config

В этом примере:

-w /etc/audit/audit.rules: указывает путь к конфигурационному файлу audit.rules, который нужно отслеживать.  
-p wa: определяет, какие операции нужно отслеживать. В данном случае, w означает запись (write), a - изменение атрибутов (attribute).  
-k auditd\_config: присваивает ключевую метку (tag) событию для легкого поиска.  
Загрузите правила аудита в систему с помощью утилиты auditctl:

sudo auditctl -R /etc/audit/audit.rules

Выполните некоторые изменения в конфигурационном файле audit.rules, чтобы убедиться, что отслеживание работает, например, добавьте или удалите правило аудита. Затем просмотрите журналы аудита, чтобы убедиться, что изменения были зафиксированы:

sudo ausearch -k auditd\_config

Эта команда выведет все события аудита, связанные с ключевой меткой auditd\_config, которая была определена в нашем правиле отслеживания изменений конфигурации auditd.

**Правила аудита**

Правила аудита (audit rules) определяют, какие события и действия в операционной системе должны записаны в журнал аудита. Может быть определено:

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

Правила аудита обычно хранятся в файле конфигурации audit.rules.

**Ключи правил**

<table border="1" id="bkmrk--a-%D0%94%D0%B5%D0%B9%D1%81%D1%82%D0%B2%D0%B8%D0%B5-%D0%B4%D0%BB%D1%8F-%D1%81%D0%BE%D0%B1%D1%8B" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 12.4082%;"></col><col style="width: 87.5918%;"></col></colgroup><tbody><tr><td>-a</td><td>Действие для события, соответствующего правилу. Возможные значения включают:

- always: Применять правило всегда.
- never: Никогда не применять правило.
- exit: Применять правило только при выходе из процесса.
- creat,open,openat,truncate,ftruncate,unlink,mkdir,rmdir

</td></tr><tr><td>-F</td><td>Условия, которым должны соответствовать аудитируемые события. Ключ -F может повторяться.

```
-F path=/etc/passwd: Определяет путь к файлу.
-F perm=w: Определяет разрешение (например, запись в файл).
-F uid=0: Определяет идентификатор пользователя.
-F arch=b64: 64-битные приложения
-F key=file_modification: Присваивает ключевую метку событию для легкого поиска.
```

</td></tr><tr><td>-S</td><td>Системные вызовы, которые должны быть аудиторованы.

- open: Отслеживать системные вызовы открытия файлов.
- execve: Отслеживать системные вызовы выполнения новых программ.

</td></tr><tr><td>-C</td><td>Условия по контексту, таким как идентификатор пользователя (UID) или группы (GID).

-C uid=0: Определяет аудитируемые события для пользователя с UID 0 (root).  
-C gid=adm: Определяет аудитируемые события для группы с именем "adm".

</td></tr><tr><td>-k</td><td>Присваивает ключевую метку событию, что позволяет идентифицировать события.</td></tr><tr><td>  
</td><td>  
</td></tr><tr><td>  
</td><td>  
</td></tr><tr><td>  
</td><td>  
</td></tr></tbody></table>

**Примеры правил**

Отслеживание доступа к файлам

```
-a always,exit -F path=/etc/shadow -F perm=r -k sensitive_files_access
```

Отслеживание запуска привилегированных команд

```
-a always,exit -F arch=b64 -S execve -C uid=0 -k privileged_commands
```

Отслеживание изменений файловой системы

```
-a always,exit -F arch=b64 -S creat,open,openat,truncate,ftruncate,unlink,mkdir,rmdir -F key=file_modification
```

**Пример настройки правил в целом**

Мы уже рассматривали анализ логов auditd ранее в курсе. Теперь давайте рассмотрим стандартную конфигурацию audtid, где уже собрано большинство полезных правил: [https://github.com/Neo23x0/auditd/blob/master/audit.rules](https://github.com/Neo23x0/auditd/blob/master/audit.rules)

Первым делом мы удаляем все существующие правила командой `<samp>-D</samp>`:

![](https://ucarecdn.com/6a09ff2d-d683-4769-a894-e660c8392bc1/)

Затем настраиваем размер буфера в байтах:

![](https://ucarecdn.com/c998805c-7df5-4ce3-9f39-b704a4b792e8/)

Этот параметр очень важен в сочетании со следующим, `<samp>-f</samp>`. Параметр -f (failure) отвечает за поведение системы в случае отказа модуля auditd, в значении 0 при отказе auditd ничего не происходит, в значении 1 выводится сообщение об ошибке, и в значении 2 система прекращает работу.

![](https://ucarecdn.com/84aa5c6b-904d-4687-962d-ccf95dfe88e4/)

Параметр `<samp>-i</samp>` позволяет игнорировать некоторые ошибки, например отсутствие файлов, которые мы указали в правилах мониторинга.

Следующим шагом идет мониторинг доступа к логам и исполняемым файлам auditd. Поскольку в логах могут содержаться очень чувствительные данные (например, пароли, или команды, выполняющиеся в cron от имени администратора) необходимо отслеживать обращения к логам auditd, как успешные, так и неуспешные.

![](https://ucarecdn.com/e248efca-c400-4356-be85-dcb2bad8c120/)

Давайте рассмотрим правила мониторинга файлов на этом примере.

Формат правила для мониторинга файлов и директорий выглядит следующим образом:

```
```css
auditctl -w путь_к_файлу -p разрешения -k имя_ключа
```<button class="copy-code-btn st-button_style_none copy-code-btn__code-block" title="Скопировать код" type="button"><svg fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19.2 3H10.2C9.2073 3 8.4 3.8073 8.4 4.8V8.4H4.8C3.8073 8.4 3 9.2073 3 10.2V19.2C3 20.1927 
                      3.8073 21 4.8 21H13.8C14.7927 21 15.6 20.1927 15.6 19.2V15.6H19.2C20.1927 15.6 21 14.7927 21 13.8V4.8C21 3.8073 
                      20.1927 3 19.2 3ZM4.8 19.2V10.2H13.8L13.8018 19.2H4.8ZM19.2 13.8H15.6V10.2C15.6 9.2073 14.7927 8.4 13.8 8.4H10.2V4.8H19.2V13.8Z" fill="#777777"></path></svg>
                  </button>
```

- `путь_к_файлу` — путь к файлу или наблюдаемой директории*.*
- `-p` (permissions) — фильтр разрешений доступа, которые мы будем мониторить, а именно: 
    - `r` — доступ на чтение файлов или директорий;
    - `w` — доступ на запись;
    - `x` — выполнение;
    - `a` — изменение аттрибутов.
- `-k` — ключ, с которым auditd запишет это событие в лог. Полезен для быстрого поиска и фильтрации событий.

Итого, мы отслеживаем запись(w), чтение(r) и изменение аттрибутов(a) для каталогов `/var/log/audit` и `/var/audit`.

Продолжим:

![](https://ucarecdn.com/afb53725-bc08-4c8e-8f3f-0277034e1f22/)

Здесь мы отслеживаем изменение конфигурации auditd.

![](https://ucarecdn.com/4aa5176d-2960-44aa-b679-c76e28f6b5a6/)

А здесь выполнение (ключ `-x`, execute) команд, которые могут повлиять на конфигурацию auditd: auditctl, auditd, augenrules.

Продолжим изучать файл конфигурации auditd:

![](https://ucarecdn.com/1e869582-3f42-4962-a0d4-58b0507770fd/)

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

```
```r
auditctl  -a действие,фильтр [ -F arch=архитектура_системы -S имя_системного_вызова] -F поле=значение  -k имя_ключа
```<button class="copy-code-btn st-button_style_none copy-code-btn__code-block" title="Скопировать код" type="button"><svg fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19.2 3H10.2C9.2073 3 8.4 3.8073 8.4 4.8V8.4H4.8C3.8073 8.4 3 9.2073 3 10.2V19.2C3 20.1927 
                      3.8073 21 4.8 21H13.8C14.7927 21 15.6 20.1927 15.6 19.2V15.6H19.2C20.1927 15.6 21 14.7927 21 13.8V4.8C21 3.8073 
                      20.1927 3 19.2 3ZM4.8 19.2V10.2H13.8L13.8018 19.2H4.8ZM19.2 13.8H15.6V10.2C15.6 9.2073 14.7927 8.4 13.8 8.4H10.2V4.8H19.2V13.8Z" fill="#777777"></path></svg>
                  </button>
```

Где:

- ***Действие и фильтр** определяют, какое событие будет регистрироваться*. *Действие(action) может быть либо* `always` , либо `never`. Параметр *filter* определяет какой фильтр событий ядра будет применяться к данному правилу. Возможные значения фильтра: `task`, `exit`, `user`, and `exclude`. В большинстве случаев используется фильтр `exit`, когда событие записывается при завершении системного вызова.
- ***system\_call**(имя\_системного\_вызова)* позволяет применить фильтр на основании системного вызова. Мы можем указать несколько системных вызовов используя ключ -S.
- ***-F*** — *фильтр, позволяющий нам отслеживать определенные события, которые нам интересны*. Фильтров может быть несколько, они указываются в формате "ключ"\[!=&gt;&lt;\]"значение", например, path=/usr/bin/ls или auid&gt;=1000.
- **-k имя\_ключа** — *опциональный, но очень полезный параметр, позволяющий добавить метку для отслеживаемого события*.

Итак, на примере выше мы мониторим события запуска (-F perm=x) команд auditd (ausearch, aureport, и т.д) после их выполнения (exit) и помечаем эти события меткой audittools.

> *Использование ключа -a never позволяет исключать события из мониторинга. Необходимо учитывать, что правила читаются сверху вниз, поэтому правила-исключения лучше помещать в начало файла конфигурации auditd.*

Далее идут исключения:

![](https://ucarecdn.com/f84b31ad-dc4f-41fe-9535-54686ef8477f/)

Мы видим два типа исключений -a never и -a always,exclude.

![](https://ucarecdn.com/bb55acf7-630c-41d6-a3ac-2a0cc54b1b7c/)

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

![](https://ucarecdn.com/063c056d-9f37-4431-8712-eff9b91ff540/)

Мониторинг доступа к базам данных пользователей системы и паролям, а также к файлу sudoers.

![](https://ucarecdn.com/f2c3b2ba-a790-4720-a77e-e0dc5250ac45/)

Отслеживание выполнения команды passwd.

![](https://ucarecdn.com/66752cd0-025b-4ebf-977d-2ce1eb9ce4b9/)

С помощью мониторинга системного вызова *connect* мы можем отслеживать сетевые соединения, и даже ловить remote shell.

В примере выше мы получим запись в лог, если процесс bash успешно откроет сетевое подключение.

> Мониторинг всех сетевых подключений может генерировать огромное количество событий, особенно на серверах, которые предоставляют публичные сервисы, например, веб-сервера. Такие правила необходимо использовать с осторожностью, особенно, если у нас включен параметр -f=2 который вешает систему при переполнении очереди событий auditd.

Идем дальше.

![](https://ucarecdn.com/76a3eb1d-d381-485c-9521-9f407ec39a9a/)

Тут мы отслеживаем неуспешные(succes=0) попытки доступа к системным директориям.

 ![](https://ucarecdn.com/1a339a91-0215-48b0-990b-8599ff6b1092/)

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

![](https://ucarecdn.com/31548367-7b01-48ca-81d5-ce4eca161df6/)

Запуск программ sssd пользователями.

> В описании правил можно часто встретить auid!=4294967295. Это число транслируется системой в -1, и означает, что UID еще не установлен системой. Вместо 4294967295 можно использовать число "-1" или значение "unset".

![](https://ucarecdn.com/91e96db2-6b72-40fe-a189-9afc04af8620/)

Отслеживание запуска команд от пользователя www-data. Применимо к веб-серверам, euid(effective uid) может отличаться. Правило может помочь обнаружить взлом веб-сервера(например, пользователь www-data запускает bash), но также может генерировать большое количество ложно-положительных срабатываний, если от имени веб-сервера запускаются какие-либо скрипты.

![](https://ucarecdn.com/c12f4397-b6e5-43bf-a19f-ad2e3d62e9ca/)

Отслеживание выполнения команд от пользователя root(euid=0). Это правило может генерировать очень много событий, следует использовать с осторожностью.

![](https://ucarecdn.com/4ee37f4f-893b-46ea-a052-31baf855b8e1/)

Удаление файлов пользователями системы(auid&gt;=1000).

И, последней строчкой мы можем добавить правило, запрещающее дальнейшее изменение праил auditd. За это отвечает ключ -e (enable). Значение 0 позволяет выключить мониторинг, значение 1 - включить, а значение 2 делает конфигурацию неизменяемой.

![](https://ucarecdn.com/aaf7a902-420a-4b43-b8be-c57966175b65/)

# Nasm

# Структура программы и память

[Интересный учебник](https://metanit.com/assembler/nasm/5.1.php)

[Еще интересный ресурс](https://nweb42.com/books/assembler)

**Типы памяти**

**Регистровая память** Самый быстрый способ хранения данных. Процессоры имеют набор регистров, которые могут использоваться для хранения данных.

**Оперативная память (RAM)** Это основное место хранения данных программы. В отличие от регистров, доступ к памяти менее быстрый, но объём значительно больше. Оперативная память делится на несколько сегментов:

Данные (Data)  
Стек (Stack)  
Код (Code)  
В Assembler можно использовать сегментирование для организации памяти.

Стек  
Стек — структура данных, в которой операции записи и чтения выполняются по принципу “последним пришёл — первым ушёл” (LIFO). Стек используется для хранения локальных переменных, адресов возврата при вызове функций, а также для управления процессом вызова процедур.

Куча  
Куча (heap) — это область памяти, в которой динамически выделяются блоки памяти во время выполнения программы. Работа с кучей требует явного управления памятью (например, с помощью системных вызовов операционной системы).

Структура программы

```
section .data
	msg db 	"hello, world", 0
section .bss
section .text
	global main
main:
	mov	rax, 1
	mov	rdi, 1
	mov rsi, msg
	mov	rdx, 12
	syscall
	mov	rax, 60
	mov	rdi, 0
	syscall
```

**Страницы:**

- .data
- .bss
- .txt

**.data**

Переменные: &lt;varname&gt; &lt;type&gt; &lt;value&gt;  
Константы: &lt;constant\_name&gt; equ &lt;value&gt;

**.bss**

Неинициализированные данные. resb - байт, resw - слово, resd - двойное слово, resq - двойное длинное слово

**.txt** Программа.

**Карта памяти:**

<div drawio-diagram="226"><img src="http://bobrobotirk.ru/uploads/images/drawio/2025-11/drawing-1-1762881634.png" alt=""/></div>

Адреса в памяти:

```
section .data
    code_msg    db "Code (.text):   0x%lx", 10, 0
    data_msg    db "Data (.data):   0x%lx", 10, 0  
    heap_msg    db "Heap (break):   0x%lx", 10, 0
    stack_msg   db "Stack (rsp):    0x%lx", 10, 0
    diff_msg    db "Stack - Heap:   %ld bytes", 10, 0

section .text
    global main
    extern printf

main:
    push rbp
    mov rbp, rsp
    
    ; Адрес кода (самой функции main)
    lea rax, [rel main]
    mov rdi, code_msg
    mov rsi, rax
    call printf
    
    ; Адрес данных
    mov rdi, data_msg
    mov rsi, code_msg        ; любая метка из .data
    call printf
    
    ; Адрес кучи (break)
    mov rax, 12              ; sys_brk
    mov rdi, 0
    syscall
    mov rdi, heap_msg
    mov rsi, rax
    call printf
    
    ; Адрес стека (rsp)
    mov rdi, stack_msg
    mov rsi, rsp
    call printf
    
    ; Разница между стеком и кучей
    mov rax, 12              ; снова получаем break
    mov rdi, 0
    syscall
    mov rbx, rax             ; heap в rbx
    mov rax, rsp             ; stack в rax
    sub rax, rbx             ; stack - heap
    
    mov rdi, diff_msg
    mov rsi, rax
    call printf
    
    pop rbp
    mov rax, 0
    ret
```

Примерный результат выполнения:

Code (.text): 0x400500  
Data (.data): 0x600800   
Heap (break): 0x1ae20000  
Stack (rsp): 0x7fffffffe010  
Stack - Heap: 2147358720 bytes (примерно 2GB)

Выделение и освобождение стека:

```
section .text
    global main

main:
    push rbp
    mov rbp, rsp

    ; Текущий rsp
    mov r12, rsp

    ; Выделяем 1KB в стеке
    sub rsp, 1024
    ; rsp УМЕНЬШИЛСЯ!
    ; r12 (старый rsp) > rsp (новый rsp)
    ; Восстанавливаем
    mov rsp, rbp
    pop rbp
    ret
```

Выделение и освобождение кучи:

```
section .text
    global main

main:
    ; Текущий break
    mov rax, 12
    mov rdi, 0
    syscall
    mov r12, rax             ; сохраняем старый break
    
    ; Увеличиваем кучу на 1KB
    mov rax, 12
    mov rdi, r12
    add rdi, 1024
    syscall
    mov r13, rax             ; новый break
    
    ; r13 > r12 - куча ВЫРОСЛА!
    
    ret
```

Если куча и стек встречаются, то SEGFAULT или ENOMEM при попытке выделить память. Предотвращение столкновений:

```
# Посмотреть текущие лимиты
ulimit -a

# Ограничить стек для теста
ulimit -s 1024  # 1MB стек
./program

# Ограничить кучу
ulimit -d 65536  # 64MB куча

# Просмотр карты памяти для процесса
cat /proc/.../maps
```

Проверка в коде:

```
safe_stack_allocation:
    mov rax, rsp
    sub rax, desired_size
    cmp rax, [heap_upper_bound]
    jl .stack_heap_collision
    ; Иначе безопасно
    sub rsp, desired_size
    ret

.stack_heap_collision:
    ; Обработка нехватки памяти
    mov rax, -1
    ret
```

**Динамическое выделение памяти**

Выделяется через системные вызовы или стандартные библиотеки (например, malloc из libc). При старте программы выделяется куча только под текущие потребности приложения. Затем при наличии динамических элементов размер увеличивается.

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

**Работа с кучей.**

Получение текущего адреса кучи (heap):

```
Для x32:
mov eax, 45
mov ebx, 0
int 0x80        ; В eax вернётся текущий адрес конца кучи 

Для x64:
mov rax, 12
mov rdi, 0
syscall         ; В rax вернётся текущий адрес конца кучи 
```

Кучу можно использовать через sys\_brk Базовое использование sys\_brk:

```
section .data
    success_msg db "Heap: allocated %d bytes at address 0x%lx", 10, 0
    error_msg db "Heap: allocation failed!", 10, 0

section .bss
    initial_break resq 1
    current_break resq 1

section .text
    global main
    extern printf

main:
    push rbp
    mov rbp, rsp
    
    ; 1. Получаем текущую границу кучи
    mov rax, 12                 ; sys_brk
    mov rdi, 0
    syscall
    mov [initial_break], rax
    mov [current_break], rax
    
    ; 2. Выделяем 16KB памяти
    mov rdi, rax
    add rdi, 16384              ; 16 * 1024 = 16384 байт
    mov rax, 12                 ; sys_brk
    syscall
    
    ; 3. Проверяем успешность
    cmp rax, [current_break]
    jle .error
    
    mov [current_break], rax    ; сохраняем новую границу
    
    ; 4. Используем выделенную память
    mov rdi, [initial_break]    ; начало выделенной области
    
    ; Записываем некоторые данные
    mov byte [rdi], 'H'
    mov byte [rdi + 1], 'e'
    mov byte [rdi + 2], 'l'
    mov byte [rdi + 3], 'l'
    mov byte [rdi + 4], 'o'
    mov byte [rdi + 5], 0
    
    ; Выводим информацию
    mov rdi, success_msg
    mov rsi, 16384              ; размер
    mov rdx, [initial_break]    ; адрес
    mov rax, 0
    call printf
    
    jmp .exit

.error:
    mov rdi, error_msg
    mov rax, 0
    call printf

.exit:
    pop rbp
    mov rax, 0
    ret
```

Ключевые принципы

- Всегда проверяйте успешность выделения памяти
- Выравнивайте запросы по границам страниц (4KB)
- Отслеживайте использование чтобы вовремя увеличивать кучу
- Используйте умные стратегии выделения (пулы, slab-аллокаторы)

**Работа с сегментами памяти.**

```
section .data
    msg db "Hello from allocated memory in x64!", 10
    msg_len equ $ - msg

section .bss
    allocated_ptr resq 1  ; 64-битный указатель

section .text
global _start

_start:
    ; Выделяем 2 страницы памяти с помощью mmap
    mov rax, 9          ; sys_mmap
    xor rdi, rdi        ; адрес = NULL (ядро выбирает)
    mov rsi, 8192       ; размер: 2 страницы (8192 байта)
    mov rdx, 0x7        ; PROT_READ | PROT_WRITE | PROT_EXEC
    mov r10, 0x22       ; MAP_PRIVATE | MAP_ANONYMOUS
    mov r8, -1          ; без файла
    xor r9, r9          ; смещение = 0
    syscall
    
    test rax, rax
    js error            ; если ошибка (отрицательное значение)
    
    mov [allocated_ptr], rax
    
    ; Копируем строку в выделенную память
    mov rdi, rax        ; назначение
    mov rsi, msg        ; источник
    mov rcx, msg_len    ; длина
    rep movsb           ; копируем байты
    
    ; Выводим строку из выделенной памяти
    mov rax, 1          ; sys_write
    mov rdi, 1          ; stdout
    mov rsi, [allocated_ptr]
    mov rdx, msg_len
    syscall
    
    ; Освобождаем память
    mov rax, 11         ; sys_munmap
    mov rdi, [allocated_ptr]
    mov rsi, 8192
    syscall
    
    ; Выход
    mov rax, 60         ; sys_exit
    xor rdi, rdi        ; код 0
    syscall

error:
    ; Обработка ошибки
    mov rax, 60         ; sys_exit
    mov rdi, 1          ; код ошибки 1
    syscall
```

Работа с сегментами считается сложнее, но эффективнее в контексте выделения и освобождения.

Выполнение кода из кучи. Это называется JIT-компиляция (Just-In-Time) или динамическое генерирование кода.

```
section .data
    success_msg db "Executing code from heap!", 10, 0
    after_exec_msg db "Back from JIT code! Return value: %d", 10, 0

section .bss
    code_buffer resb 4096       ; буфер для кода

section .text
    global main
    extern printf, mprotect

main:
    push rbp
    mov rbp, rsp
    
    ; 1. Выделяем память под код (уже есть code_buffer)
    
    ; 2. Записываем машинный код в буфер
    mov rdi, code_buffer
    
    ; Генерируем простую функцию: mov rax, 42; ret
    mov byte [rdi], 0x48        ; mov rax, 42
    mov byte [rdi + 1], 0xC7
    mov byte [rdi + 2], 0xC0
    mov byte [rdi + 3], 0x2A
    mov byte [rdi + 4], 0x00
    mov byte [rdi + 5], 0x00
    mov byte [rdi + 6], 0x00
    mov byte [rdi + 7], 0xC3    ; ret
    
    ; 3. Делаем память исполняемой
    mov rax, 10                 ; sys_mprotect
    mov rdi, code_buffer        ; адрес
    and rdi, ~0xFFF             ; выравниваем до границы страницы
    mov rsi, 4096               ; размер
    mov rdx, 7                  ; PROT_READ|PROT_WRITE|PROT_EXEC
    syscall
    test rax, rax
    jnz .error
    
    ; 4. Вызываем код из кучи!
    mov rdi, success_msg
    call printf
    
    mov rax, code_buffer        ; получаем указатель на функцию
    call rax                    ; ВЫЗЫВАЕМ КОД ИЗ КУЧИ!
    
    ; 5. rax содержит возвращаемое значение (42)
    mov rsi, rax
    mov rdi, after_exec_msg
    call printf
    
    jmp .exit

.error:
    ; Обработка ошибки
.exit:
    pop rbp
    mov rax, 0
    ret
```

**Важные моменты безопасности**

1\. mprotect обязателен, без PROT\_EXEC флага будет SEGFAULT:

```
; Без этого - SEGFAULT!
mov rax, 10                 ; sys_mprotect
mov rdi, code_addr
mov rsi, size
mov rdx, 7                  ; PROT_READ|PROT_WRITE|PROT_EXEC
syscall
```

2\. W^X политика

В современных системах часто включена политика W^X (Write XOR Execute):

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

```
section .data
    code_bytes db 0xB8, 0x01, 0x00, 0x00, 0x00, 0xC3  ; mov eax,1; ret

section .text
global _start

_start:
    ; Выделяем память с правами READ|WRITE
    mov rax, 9          ; mmap
    xor rdi, rdi        ; адрес
    mov rsi, 4096       ; размер
    mov rdx, 0x3        ; PROT_READ | PROT_WRITE (но НЕ PROT_EXEC!)
    mov r10, 0x22       ; MAP_PRIVATE | MAP_ANONYMOUS
    mov r8, -1
    xor r9, r9
    syscall
    mov rbx, rax        ; сохраняем адрес
    
    ; Копируем код в выделенную память
    mov rdi, rax
    mov rsi, code_bytes
    mov rcx, 6
    rep movsb
    
    ; Пытаемся выполнить код - ЭТО ВЫЗОВЕТ SEGFAULT!
    ; call rbx  ; ← segmentation fault!
    
    ; Сначала меняем права на READ|EXECUTE
    mov rax, 10         ; mprotect
    mov rdi, rbx        ; адрес
    mov rsi, 4096       ; размер
    mov rdx, 0x5        ; PROT_READ | PROT_EXECUTE
    syscall
    
    ; Теперь можно выполнять
    call rbx            ; работает!
```

# Отладчики и дизассемблеры

#### **objdump**

Простейший дизассемблер, есть по умолчанию в linux.

-d только секцию кода, -D все секции. -M intel в формате intel.

```
objdump -d -M intel strswap
```

В секции .data пытается разобрать данные на команды.

#### **GDB**

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

Загрузка программы для отладки: gdb &lt;program&gt;

Консоль gdb:

<table border="1" id="bkmrk-%D0%9F%D1%80%D0%BE%D1%81%D0%BC%D0%BE%D1%82%D1%80-list-%D0%92%D1%8B%D0%B2%D0%B5%D1%81%D1%82" style="border-collapse: collapse; width: 100%; height: 582.938px;"><colgroup><col style="width: 15.9036%;"></col><col style="width: 15.5432%;"></col><col style="width: 68.5532%;"></col></colgroup><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Просмотр</td><td style="height: 29.7969px;"></td><td style="height: 29.7969px;"></td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">list</td><td style="height: 29.7969px;"></td><td style="height: 29.7969px;">Вывести 10 строк кода, повтор команды выводит следующие строки.

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">&lt;число&gt;</td><td style="height: 29.7969px;">Вывод конкретной строки.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">x/s</td><td style="height: 29.7969px;">&lt;адрес памяти&gt;</td><td style="height: 29.7969px;">адрес, по которому размещена строка</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">x/c</td><td style="height: 29.7969px;">&lt;адрес памяти&gt;</td><td style="height: 29.7969px;">вывод одного символа</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">x/13с</td><td style="height: 29.7969px;">&lt;адрес памяти&gt;</td><td style="height: 29.7969px;">x/13c 0x000000 выведет 13 символов в виде строк</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">x/13d</td><td style="height: 29.7969px;">&lt;адрес памяти&gt;</td><td style="height: 29.7969px;">вывод 13 символов в виде чисел</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">x/13x</td><td style="height: 29.7969px;">&lt;адрес памяти&gt;</td><td style="height: 29.7969px;">13 шестнадцатеричных символов</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">x/s </td><td style="height: 29.7969px;">&lt;имя ссылки&gt;</td><td style="height: 29.7969px;">x/s &amp;msg</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Запуск</td><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">  
</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">run</td><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">Выполнение загруженного приложения</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">disassemble</td><td style="height: 29.7969px;">&lt;link&gt;</td><td style="height: 29.7969px;">Дизассемблирование метки</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">break</td><td style="height: 29.7969px;">&lt;link&gt;</td><td style="height: 29.7969px;">Точка останова</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">info</td><td style="height: 29.7969px;">registers</td><td style="height: 29.7969px;">вывод значений регистров</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">step</td><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">следующий шаг </td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">continue</td><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">продолжение выполнения</td></tr><tr><td>print p</td><td>&lt;registr&gt;</td><td>Вывод значения регистра

```
print $rax
```

</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Дополнительно</td><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">  
</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">quit</td><td style="height: 29.7969px;">  
</td><td style="height: 29.7969px;">Выход</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">set</td><td style="height: 46.5938px;">&lt;парам&gt;</td><td style="height: 46.5938px;">Настройка параметра.

 set disassembly-flavor intel Установка формата отображения Intel

</td></tr></tbody></table>

# Компоновка и линковка

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

**Точки входа.**

Точка входа определяется значением global &lt;что-то&gt; Варианты точек входа.

<table border="1" id="bkmrk-%D0%A2%D0%B8%D0%BF-%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B-%D0%A0%D0%B5%D0%BA%D0%BE%D0%BC%D0%B5" style="width: 96.4286%; border-collapse: collapse; border-color: rgb(0, 0, 0); border-style: solid;"><thead><tr><th style="width: 31.4779%; border-color: rgb(0, 0, 0);">Тип программы</th><th style="width: 39.1555%; border-color: rgb(0, 0, 0);">Рекомендуемая точка входа</th><th style="width: 29.3666%; border-color: rgb(0, 0, 0);">Компиляция</th></tr></thead><tbody><tr><td style="width: 31.4779%; border-color: rgb(0, 0, 0);">Самостоятельная Linux</td><td style="width: 39.1555%; border-color: rgb(0, 0, 0);">\_start</td><td style="width: 29.3666%; border-color: rgb(0, 0, 0);">ld</td></tr><tr><td style="width: 31.4779%; border-color: rgb(0, 0, 0);">С использованием libc</td><td style="width: 39.1555%; border-color: rgb(0, 0, 0);">main</td><td style="width: 29.3666%; border-color: rgb(0, 0, 0);">gcc</td></tr><tr><td style="width: 31.4779%; border-color: rgb(0, 0, 0);">GUI Windows</td><td style="width: 39.1555%; border-color: rgb(0, 0, 0);">WinMain</td><td style="width: 29.3666%; border-color: rgb(0, 0, 0);">Visual Studio</td></tr><tr><td style="width: 31.4779%; border-color: rgb(0, 0, 0);">DLL Windows</td><td style="width: 39.1555%; border-color: rgb(0, 0, 0);">DllMain</td><td style="width: 29.3666%; border-color: rgb(0, 0, 0);">Visual Studio</td></tr><tr><td style="width: 31.4779%; border-color: rgb(0, 0, 0);">Ядро ОС</td><td style="width: 39.1555%; border-color: rgb(0, 0, 0);">kmain</td><td style="width: 29.3666%; border-color: rgb(0, 0, 0);">специальный линкер</td></tr><tr><td style="width: 31.4779%; border-color: rgb(0, 0, 0);">Пользовательская</td><td style="width: 39.1555%; border-color: rgb(0, 0, 0);">любое имя</td><td style="width: 29.3666%; border-color: rgb(0, 0, 0);">ld -e имя</td></tr></tbody></table>

**Варианты компиляции**

**Команды консоли**

```bash
nasm -f elf64 hello.asm -o hello.o
ld -o hello hello.o
```

В случае использования точки входа \_start

**make файл**

```
hello: hello.o
	gcc -o hello hello.o -no-pie
hello.o: hello.asm
	nasm -f elf64 -g -F dwarf hello.asm -l hello.lst
```

Компилирование происходит командой make,

**Процедура поиска библиотек**

Директивой extern printf говорится компилятору: "я знаю где эта функция, делай все остальное".

```
nm -D /lib/x86_64-linux-gnu/libc.so.6 # просмотр списка функций в библиотеке
# Какие библиотеки использует программа
ldd program

# Посмотреть неразрешенные символы в объектном файле
nm -u program.o

# Посмотреть символы в исполняемом файле
nm program | grep printf
```

**Объединение нескольких файлов.**

**Вариант 1** - директива include. Она фактически вставляет текст одного файла в другой файл. Например есть файл sum.asm который мы будем включать в файл hello.asm.

```
section .text
; Функция возвращает сумму чисел
; Принимает два параметра:
; rdi - первое число 
; rsi - второе число
; Результат функции возвращается через регистр rax
sum:
    mov rax, rdi      ; результат в rax
    add rax, rsi
    ret
```

Тогда hello.asm

```
global _start
 
section .text
%INCLUDE "sum.asm"
_start:
    mov rdi, 33
    mov rsi, 44
     
    call sum
    mov rdi, rax         ; для проверки результата помещаем сумму из RAX в RDI
    mov rax, 60
    syscall
```

**Вариант 2.** Раздельная компиляция. В этом случае доступные извне метки (функции, данные) объявляются с помощью директивы global. Файл sum.asm:

```
global sum  ; делаем функцию sum доступной извне
 
section .text
; Функция возвращает сумму чисел
; Принимает два параметра:
; rdi - первое число 
; rsi - второе число
; Результат функции возвращается через регистр rax
sum:
    mov rax, rdi      ; результат в rax
    add rax, rsi
    ret
```

Файл hello.asm

```
global _start
 
extern sum      ; функция sum расположена где-то во вне
 
section .text
_start:
    mov rdi, 33
    mov rsi, 44
     
    call sum
    mov rdi, rax         ; для проверки результата помещаем сумму из RAX в RDI
    mov rax, 60
    syscall
```

Сначала скомпилируем файл sum.asm:

```
nasm -f elf64 sum.asm -o sum.o
```

Затем скомпилируем файл hello.asm:

```
nasm -f elf64 hello.asm -o hello.o
```

В итоге у нас получится два разных объектных файла - sum.o и hello.o. Скомпонуем их в один исполняемый файл:

```
ld hello.o sum.o -o hello
```

# Инструкции

**Арифметика и логика**

**Mov** копирование значений.

```
mov destination, source
```

destination: регистр или память. Source: регистр, память, число. Одновременно не может из памяти в память. Должны совпадать по размерам. Для расширения нулями меньших регистров (только регистр - регистр):

```
movsxd dest, source     ; если dest - 64-разрядный операнд и source - 32-разрядный
movsx dest, source      ; для всех остальных комбинаций операндов
```

Однако с знаком будут проблемы. Для беззнакового расширения нулями movzx:

```
mov al, 5
movzx rdi, cx 
```

Есть относительная адресация, то есть

```
mov eax, [ebx + 8]      ; EAX = значение по адресу EBX + 8 Надо разобраться
mov [esi + ecx*4], edx  ; Записать EDX по адресу ESI + ECX*4
```

Однако разность \[esi - 4\] может не работать.

Значение регистра AL помещается в самый младший байт регистра RDI. Остальные байты (7 байт) регистра RDI заполняются нулями. Если не сделать заполнение нулями, то в старший из 8 байт попадет байт аргумента, дальше - некое непотребство.

```
movzx rsi, byte [cura] ; если cura это байт 
```

**lea** загрузка/вычисление адреса. Есть адресная арифметика.

**Add / sub** сложение и вычитание

```
add operand1, operand2  ; operand1 = operand1 + operand2
inc rdi    ; rdi = rdi + 1
sub rdi, rsi    ; rdi = rdi - rsi
dec rdi    ; rdi = rdi - 1
```

**<span class="b">mul</span> и <span class="b">imul</span>** умножает два целых числа. <span class="b">imul</span> умножает числа со знаком, а <span class="b">mul</span> - беззнаковые числа. Обе инструкции принимают один операнд - регистр или адрес в памяти, который умножается на значение в регистре RAX. Результат помещается в регистры RAX/RDX

```
mul operand8   ; если операнд 8-разрядный, результат в AX
mul operand16  ; если операнд 16-разрядный, результат в DX:AX
mul operand32  ; если операнд 32-разрядный, результат в EDX:EAX
mul operand64  ; если операнд 64-разрядный, результат в RDX:RAX
```

В AX/EAX/RAX помещается младшая часть результата, а в DX/EDX/RDX - старшая.

```
global _start
 
section .text
_start:
    mov rdi, 2
    mov rax, 4
    mul rdi         ; RAX = RAX * RDI
    mov rdi, rax    ; RDI = RAX = 8
    mov rax, 60
    syscall
```

**<span class="b">div</span> и <span class="b">idiv</span>.** <span class="b">idiv</span> делит два числа со знаком, а <span class="b">div</span> - беззнаковые числа.

- Если операнд 8-разрядный, то <span class="b">div</span> делит регистр <span class="b">AX</span> на операнд, помещая частное в <span class="b">AL</span>, а остаток (по модулю) в <span class="b">AH</span>.
- Если операнд 16-разрядный, то инструкция <span class="b">div</span> делит 32-разрядное число в <span class="b">DX:AX</span> на операнд, помещая частное в <span class="b">AX, а остаток в DX.</span>
- Если операнд 32-разрядный, <span class="b">div</span> делит 64-битное число в <span class="b">EDX:EAX</span> на операнд, помещая частное в <span class="b">EAX</span>, а остаток в <span class="b">EDX</span>.
- И если операнд 64-разрядный, <span class="b">div</span> делит 128-битное число в <span class="b">RDX:RAX</span> на операнд, помещая частное в <span class="b">RAX</span>, а остаток в <span class="b">RDX</span>.

```
global _start
 
section .text
_start:
    mov rax, 0  ; обнуляем регистр
    mov ax, 22  ; 16-разрядный регистр
    mov bl, 5   ; 8-разрядный регистр
    div bl      ; AX/BL = AL =4 (результат), AH = 2 (остаток)
    movzx rdi, al   ; RDI = 4
    mov rax, 60
    syscall
```

При этом в x86-64 нельзя разделить два числа одинаковой разрядности, например, одно 8-разрядное на другое 8-разрядное. Если знаменатель представляет собой 8-битное значение, числитель должен быть 16-битным значением. Если же нужно разделить одно 8-битное значение без знака на другое, то необходимо дополнить числитель нулями до 16 бит, загрузив числитель в регистр AL, а затем переместив 0 в регистр AH. Отсутствие расширения AL до нуля перед выполнением div может привести к тому, что x86-64 выдаст некорректный результат.

- Если нужно разделить два беззнаковых 16-разрядных числа, то надо расширить регистр AX (который содержит числитель) до регистра DX. Для этого достаточно загрузить 0 в регистр DX.
- Если нужно разделить одно беззнаковое 32-битное значение на другое, перед делением надо расширить регистр EAX нулями до EDX (загрузив 0 в EDX).
- И чтобы разделить одно 64-битное число на другое, перед делением нужно расширить RAX нулями до RDX (поместив 0 в RDX).

Логические операции.

and, or, xor, not, neg,

and reg1 reg2 reg1 = reg1 and reg2

Есть сдвиг и вращение

**Переходы.**

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

JMP - безусловный переход.

```
jmp метка
jmp регистр
jmp адрес_в_памяти
```

Переход по метке:

```
global _start
section .text
_start:
    mov rdi, 11         ; RDI = 11
    jmp exit            ; переходим к метке exit
    mov rdi, 22         ; не выполняется
exit:                   ; метка exit
    mov rax, 60         ; 60 - номер системного вызова exit
    syscall             
```

Переход по адресу в регистре:

```
global _start
section .text
_start:
    mov rbx, exit       ; в регистр RBX помещаем адрес метки exit
    mov rdi, 22         ; RDI = 22
    jmp rbx             ; переходим к адресу из регистра RBX
    mov rdi, 33         ; не выполняется
exit:                   ; метка exit
    mov rax, 60         ; 60 - номер системного вызова exit
    syscall
```

Переход к адресу в памяти. Переменная должна быть <span class="b">qword</span>, четверичное слово, которое занимает 64 бит.

```
global _start
section .text
_start:
    mov rdi, 23         ; RDI = 23
    jmp [exitPtr]       ; переходим к адресу из exitPtr
    mov rdi, 33         ; не выполняется
exit:                   ; метка exit
    mov rax, 60         ; 60 - номер системного вызова exit
    syscall             ; выполняем системный вызов exit  
exitPtr: dq exit      ; переменная exitPtr хранит адрес метки exit  
```

**Условный переход**

В регистре eflags 4 бита используются для проверки состояния исполнения предыдущей команды и перехода. Инструкции, выполняющие математические или логические операции (add, sub, and, or, xor и not) влияют на установку флагов, а инструкции загрузки данных типа mov или lea не влияют.

<table border="1" id="bkmrk-%D0%A4%D0%BB%D0%B0%D0%B3-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 8.55422%;"></col><col style="width: 44.2169%;"></col><col style="width: 9.75904%;"></col><col style="width: 37.4699%;"></col></colgroup><thead><tr><td>Флаг</td><td>Описание</td><td>Команда</td><td>Описание</td></tr></thead><tbody><tr><td>CF</td><td>Флаг переноса. Беззнаковое переполнение (сумма с переносом или вычитании с заимствованием).</td><td>jc</td><td>переход к метке, если флаг переноса установлен</td></tr><tr><td> </td><td> </td><td>jnc</td><td>переход, если флаг переноса НЕ установлен</td></tr><tr><td>  
</td><td>  
</td><td>clc</td><td>сброс флага переноса</td></tr><tr><td>  
</td><td>  
</td><td>setc</td><td>установка флага переноса</td></tr><tr><td>OF</td><td>Флаг переполнения. Переполнение со знаком</td><td>jo</td><td>переход к метке, если флаг переполнения установлен</td></tr><tr><td>  
</td><td>  
</td><td>jno</td><td>переход к метке, если флаг переполнения не установлен</td></tr><tr><td>SF</td><td>Флаг знака. Если старший бит результата установлен. То есть флаг знака отражает состояние старшего бита результата.</td><td>js</td><td>переход к метке, если флаг знака установлен</td></tr><tr><td>  
</td><td>  
</td><td>jns</td><td>переход к метке, если флаг знака не установлен</td></tr><tr><td>ZF</td><td>Флаг нуля. Если результат вычисления дает 0</td><td>jz</td><td>переход к метке, если флаг нуля установлен</td></tr><tr><td>  
</td><td>  
</td><td>jnz</td><td>переход к метке, если флаг нуля не установлен</td></tr><tr><td>  
</td><td>Сохранение/восстановление состояния</td><td>  
</td><td>  
</td></tr><tr><td>  
</td><td>Порядок битов для обоих операций:

1. 1. Флаг переноса (CF)
    2. Всегда равен 1
    3. Флаг паритетности (PF)
    4. Всегда равен 0
    5. Дополнительный флаг переноса (AF)
    6. Всегда равен 0
    7. Флаг нуля (ZF)
    8. Флаг знака (SF)

Биты 1, 3, и 5 не используются.

</td><td>lahf</td><td>копирует флаги состояния из регистра eflags в регистр ah</td></tr><tr><td>  
</td><td>  
</td><td>sahf</td><td>сохраняет флаги состояния из регистра ah в регистр eflags</td></tr></tbody></table>

Пример перехода

```
global _start
section .text
_start:
    mov al, 255
    add al, 3       ; AL = AL + 3
    jc carry_set    ; если флаг переноса установлен, переход к метке carry_set
    mov rdi, 2      ; если флаг переноса не установлен, RDI = 2
    jmp exit
carry_set:          ; если флаг переноса установлен
    mov rdi, 4      ; RDI = 4
exit:               ; метка exit
    mov rax, 60
    syscall
```

**Сравнение** cmp (от compare) сравнивает значения и устанавливает флаги. Результат сравнения используется для условного перехода.

```
cmp left_operand, right_operand
```

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

Cmp вычитает второй из первого и устанавливает флаги кода условия на основе результата вычитания. Cmp не сохраняет результат вычитания. Аналогичные команды для получения результатов сравнения:

<table border="1" id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-je-" style="border-collapse: collapse; width: 100%; height: 495.735px;"><colgroup><col style="width: 13.1769%;"></col><col style="width: 86.8231%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Команда</td><td style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">je / jz</td><td style="height: 29.7969px;">проверяет ZF == 1 выполняет переход, если оба операнда равны.</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">jne / jnz</td><td style="height: 29.7969px;">проверяет ZF == 0 выполняет переход, если оба операнда не равны.</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">ja / jnbe</td><td style="height: 46.5938px;">проверяет одновременно СF == 0 и ZF == 0. Переход, если первый операнд больше второго. Оба операнда беззнаковые.</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">jae / jnb</td><td style="height: 46.5938px;">проверяет СF == 0 Переход, если первый операнд больше или равен второму. Оба операнда беззнаковые. Аналогичен инструкции jnc</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">jb / jnae</td><td style="height: 46.5938px;">проверяет условие СF == 1 и выполняет переход, если первый операнд меньше второго. Оба операнда беззнаковые. Аналогичен инструкции jc.</td></tr><tr style="height: 63.3906px;"><td style="height: 63.3906px;">jbe / jna</td><td style="height: 63.3906px;">проверяет одновременно два условия СF == 1 и ZF == 1 (достаточно, чтобы выполнялось хотя бы одно из этих условий). Выполняет переход, если первый операнд меньше или равен второму. Оба операнда беззнаковые.</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">jg / jnle</td><td style="height: 46.5938px;">проверяет одновременно два условия SF == OF и ZF == 0 (оба условия должны быть истинными). Выполняет переход, если первый операнд больше второго. Оба операнда со знаком.</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">jge / jnl</td><td style="height: 46.5938px;">проверяет условие SF == OF и выполняет переход, если первый операнд больше или равен второму. Оба операнда со знаком.</td></tr><tr style="height: 46.5938px;"><td style="height: 46.5938px;">jl / jnge</td><td style="height: 46.5938px;">проверяет условие SF != OF (флаги SF и OF не должны быть равны) и выполняет переход, если первый операнд меньше второго. Оба операнда со знаком.</td></tr><tr style="height: 63.3906px;"><td style="height: 63.3906px;">jle / jng</td><td style="height: 63.3906px;">проверяет одновременно два условия SF != OF и ZF == 1 (достаточно, чтобы выполнялось хотя бы одно из этих условий). Выполняет переход, если первый операнд меньше или равен второму. Оба операнда со знаком.</td></tr></tbody></table>

Через / - одинаковые операторы, и машинный код одинаковый.

**Условное копирование.** В зависимости от сравнения загрузить в регистр некоторое значение.

<table border="1" id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-cmo" style="border-collapse: collapse; width: 100%; height: 495.735px;"><colgroup><col style="width: 26.3957%;"></col><col style="width: 73.6043%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Команда</td><td style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">cmovc / cmovb / cmovnae</td><td style="height: 29.7969px;">копирует значение, если флаг переноса CF = 1</td></tr><tr><td>cmovnc / cmovnb / cmovae</td><td>копирует значение, если флаг переноса CF = 0</td></tr><tr><td>cmovz / cmove</td><td>копирует значение, если флаг нуля ZF = 1</td></tr><tr><td>cmovnz / cmovne</td><td>копирует значение, если флаг нуля ZF = 0</td></tr><tr><td>cmovs</td><td>копирует значение, если флаг знака SF = 1</td></tr><tr><td>cmovns</td><td>копирует значение, если флаг знака SF = 0</td></tr><tr><td>cmovo</td><td>копирует значение, если флаг переполнения OF = 1</td></tr><tr><td>cmovno</td><td>копирует значение, если флаг переполнения OF = 0</td></tr></tbody></table>

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

<table border="1" id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-cmo-1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 26.2936%;"></col><col style="width: 73.7064%;"></col></colgroup><thead><tr><td>Команда</td><td>Описание</td></tr></thead><tbody><tr><td>cmova</td><td>копирует значение, если первый операнд больше второго (CF=0, ZF=0)</td></tr><tr><td>cmovnbe</td><td>копирует значение, если первый операнд не меньше и не равен второму (CF=0, ZF=0)</td></tr><tr><td>cmovae / cmovnc / cmovnb</td><td>копирует значение, если первый операнд больше или равен второму (CF=0)</td></tr><tr><td>cmovnb / cmovnc / cmovae</td><td>копирует значение, если первый операнд не меньше второго (CF=0)</td></tr><tr><td>cmovb / cmovc / cmovnae</td><td>копирует значение, если первый операнд меньше второго (CF=1)</td></tr><tr><td>cmovnae / cmovc / cmovb</td><td>копирует значение, если первый операнд не больше и не равен второму (CF=1)</td></tr><tr><td>cmovbe</td><td>копирует значение, если первый операнд меньше или равен второму (CF=1 или ZF=1)</td></tr><tr><td>cmovna</td><td>копирует значение, если первый операнд не больше второго (CF=1 или ZF=1)</td></tr></tbody></table>

Инструкции сравнения чисел со знаком:

<table border="1" id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-cmo-2" style="border-collapse: collapse; width: 100%; height: 238.375px;"><colgroup><col style="width: 26.2936%;"></col><col style="width: 73.7064%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Команда</td><td style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">cmovg</td><td style="height: 29.7969px;">копирует значение, если первый операнд больше второго (SF=OF или ZF=0)</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">cmovnle</td><td style="height: 29.7969px;">копирует значение, если первый операнд не меньше и не равен второму (SF=OF или ZF=0)</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">cmovge</td><td style="height: 29.7969px;">копирует значение, если первый операнд больше или равен второму (SF=OF)</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">cmovnl</td><td style="height: 29.7969px;">копирует значение, если первый операнд не меньше второго (SF=OF)</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">cmovl</td><td style="height: 29.7969px;">копирует значение, если первый операнд меньше второго (SF != OF)</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">cmovnge</td><td style="height: 29.7969px;">копирует значение, если первый операнд не больше и не равен второму (SF != OF)</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">cmovle</td><td style="height: 29.7969px;">копирует значение, если первый операнд меньше или равен второму (SF != OF или ZF=1)</td></tr><tr><td>cmovng</td><td>копирует значение, если первый операнд не больше второго (SF != OF или ZF=1)</td></tr></tbody></table>

И две общие инструкции как для чисел со знаком, так и для беззнаковых чисел:

cmove: копирует значение, если первый операнд равен второму (ZF=1). Аналогичен инструкции cmovz

cmovne: копирует значение, если первый операнд не равен второму (ZF=0). Аналогичен инструкции cmovnz

Первый параметр этих инструкций (куда копируем) представляет либо регистр, либо переменную (16, 32 или 64-битные). Второй параметр (что копируем) - регистр общего назначения(также 16, 32 или 64-битные).

```
global _start
 
section .text
_start:
    mov al, 255
    mov bl, 3
    add al, bl          ; складываем AL и BL
     
    mov rcx, 2          ; вариант, если флаг переноса сброшен (CF = 0)
    mov rdx, 4          ; вариант, если флаг переноса установлен (CF = 1)
 
    cmovnc rdi, rcx     ; Если CF = 0
    cmovc rdi, rdx      ; Если CF = 1
    mov rax, 60
    syscall
```

**Стек**

<table border="1" id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-pus" style="border-collapse: collapse; width: 100%; height: 238.375px;"><colgroup><col style="width: 26.2936%;"></col><col style="width: 73.7064%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Команда</td><td style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">push</td><td style="height: 29.7969px;">Кладёт значение в стек push eax</td></tr><tr><td>pop</td><td>Извлекает значение из стека pop ebx</td></tr><tr><td>enter</td><td>Создаёт стековый фрейм enter 16, 0 </td></tr><tr><td>leave</td><td>Удаляет стековый фрейм</td></tr></tbody></table>

**Циклы**

Простой цикл:

```
global _start
 
section .text
_start:
    mov rcx, 5
    mov rdi, 0
loop:
    add rdi, 2      ; RDI = RDI + 2 
    dec rcx         ; RCX = RCX - 1 
    jnz loop        ; если флаг нуля НЕ установлен, переход обратно к метке loop         
    mov rax, 60
    syscall
```

Встроенный цикл:

<table border="1" id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9E%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5-loo" style="border-collapse: collapse; width: 100%; height: 135.984px;"><colgroup><col style="width: 26.2936%;"></col><col style="width: 73.7064%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Команда</td><td style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 46.5938px;"><td style="height: 46.5938px;">loop</td><td style="height: 46.5938px;">уменьшает на 1 число в регистре RCX и переходит к определенной метке, если RCX не равен нулю. </td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">loope</td><td style="height: 29.7969px;">продолжает цикл, если установлен флаг нуля</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">loopne</td><td style="height: 29.7969px;">повторяет цикл, если флаг нуля не установлен</td></tr><tr><td>jrcxz</td><td>проверяет значение RCX, и если оно рано 0, то переходит к определенной метке.</td></tr></tbody></table>

Пример использования на Linux:

```
global _start
 
section .text
_start:
    mov rcx, 5      ; регистр-счетчик
    mov rdi, 0
mainloop:           ; цикл
    add rdi, 2      ; некоторые действия цикла
    loop mainloop   ; уменьшаем rcx на 1, переходим к mainloop, если rcx не содержит 0
 
    mov rax, 60
    syscall
```

Пример для jrcxz

```
global _start
  
section .text
_start:
    mov rcx, 5      ; регистр-счетчик
    mov rdi, 1
mainloop:           ; цикл
    jrcxz exit      ; если rcx = 0, то переход к метке exit
    add rdi, 2      ; некоторые действия цикла
    loop mainloop  ; уменьшаем значение в rcx на 1, переходим к метке mainloop, если rcx не содержит 0
 
exit:
    mov rax, 60
    syscall
```

**Вложенные циклы**

Для вложенных циклов нужно сохранять значение внешнего счётчика (например, в стеке). `Пример: Таблица умножения (5x5)`

```
```x86asm
section .text  
    global _start  

_start:  
    mov ebx, 1       ; Внешний счётчик (строки)  

outer_loop:  
    mov ecx, 1       ; Внутренний счётчик (столбцы)  

inner_loop:  
    ; Вычисляем произведение EBX * ECX  
    mov eax, ebx  
    mul ecx          ; EAX = EBX * ECX  

    ; Здесь можно вывести EAX (пропущено для краткости)  

    ; Увеличиваем внутренний счётчик  
    inc ecx  
    cmp ecx, 5  
    jle inner_loop  

    ; Увеличиваем внешний счётчик  
    inc ebx  
    cmp ebx, 5  
    jle outer_loop  

    ; sys_exit(0)  
    mov eax, 1  
    xor ebx, ebx  
    int 0x80  
```<button class="copy-code-btn st-button_style_none copy-code-btn__code-block" title="Скопировать код" type="button"><svg fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19.2 3H10.2C9.2073 3 8.4 3.8073 8.4 4.8V8.4H4.8C3.8073 8.4 3 9.2073 3 10.2V19.2C3 20.1927 
                      3.8073 21 4.8 21H13.8C14.7927 21 15.6 20.1927 15.6 19.2V15.6H19.2C20.1927 15.6 21 14.7927 21 13.8V4.8C21 3.8073 
                      20.1927 3 19.2 3ZM4.8 19.2V10.2H13.8L13.8018 19.2H4.8ZM19.2 13.8H15.6V10.2C15.6 9.2073 14.7927 8.4 13.8 8.4H10.2V4.8H19.2V13.8Z" fill="#777777"></path></svg>
                  </button>
```

**Оптимизация циклов в ассемблере**

1\. Разворот цикла (Loop Unrolling) Уменьшение числа итераций за счёт повторения тела цикла внутри одной итерации.

Пример: Сумма элементов массива (4 элемента за итерацию)

```
```x86asm
section .data  
    arr dd 1, 2, 3, 4, 5, 6, 7, 8  
    len equ ($ - arr) / 4  ; 8 элементов  

section .text  
    global _start  

_start:  
    mov esi, arr        ; Указатель на массив  
    mov ecx, len / 4    ; Количество итераций (8 / 4 = 2)  
    xor eax, eax        ; Сумма = 0  

sum_loop:  
    add eax, [esi]      ; Элемент 1  
    add eax, [esi + 4]  ; Элемент 2  
    add eax, [esi + 8]  ; Элемент 3  
    add eax, [esi + 12] ; Элемент 4  
    add esi, 16         ; Сдвиг на 4 элемента (4 * 4 байта)  
    loop sum_loop  

    ; Проверка остатка (если len не кратен 4)  
    mov ecx, len % 4  
    jz done  

remainder_loop:  
    add eax, [esi]  
    add esi, 4  
    loop remainder_loop  

done:  
    ; EAX = сумма
```<button class="copy-code-btn st-button_style_none copy-code-btn__code-block" title="Скопировать код" type="button"><svg fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19.2 3H10.2C9.2073 3 8.4 3.8073 8.4 4.8V8.4H4.8C3.8073 8.4 3 9.2073 3 10.2V19.2C3 20.1927 
                      3.8073 21 4.8 21H13.8C14.7927 21 15.6 20.1927 15.6 19.2V15.6H19.2C20.1927 15.6 21 14.7927 21 13.8V4.8C21 3.8073 
                      20.1927 3 19.2 3ZM4.8 19.2V10.2H13.8L13.8018 19.2H4.8ZM19.2 13.8H15.6V10.2C15.6 9.2073 14.7927 8.4 13.8 8.4H10.2V4.8H19.2V13.8Z" fill="#777777"></path></svg>
                  </button>
```

<table border="1" id="bkmrk-%D0%9F%D1%80%D0%B5%D0%B8%D0%BC%D1%83%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%B0-%D0%9D%D0%B5%D0%B4%D0%BE%D1%81%D1%82%D0%B0" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 57.3215%;"></col><col style="width: 42.7977%;"></col></colgroup><thead><tr><td>Преимущества</td><td>Недостатки</td></tr></thead><tbody><tr><td>- Уменьшение накладных расходов на проверку условия.
- Лучшее использование конвейера процессора.

</td><td>- Увеличение размера кода.
- Сложность обработки остатков.

</td></tr></tbody></table>

2\. Замена loop на dec + jnz

Инструкция loop медленнее, чем связка dec + jnz т к процессоры лучше оптимизируют dec + jnz. Пример:

```
```x86asm
mov ecx, 100  

; Медленнее:  
; loop_label:  
;     ...  
;     loop loop_label  

; Быстрее:  
loop_label:  
    ...  
    dec ecx  
    jnz loop_label
```<button class="copy-code-btn st-button_style_none copy-code-btn__code-block" title="Скопировать код" type="button"><svg fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19.2 3H10.2C9.2073 3 8.4 3.8073 8.4 4.8V8.4H4.8C3.8073 8.4 3 9.2073 3 10.2V19.2C3 20.1927 
                      3.8073 21 4.8 21H13.8C14.7927 21 15.6 20.1927 15.6 19.2V15.6H19.2C20.1927 15.6 21 14.7927 21 13.8V4.8C21 3.8073 
                      20.1927 3 19.2 3ZM4.8 19.2V10.2H13.8L13.8018 19.2H4.8ZM19.2 13.8H15.6V10.2C15.6 9.2073 14.7927 8.4 13.8 8.4H10.2V4.8H19.2V13.8Z" fill="#777777"></path></svg>
                  </button>
```

3\. Вынос инвариантов из цикла. Вычисление константных выражений до цикла. Пример:

```
```x86asm
; Плохо:  
mov ecx, 100  
loop_start:  
    mov eax, [esi]  
    add eax, 10       ; 10 — инвариант  
    mov [edi], eax  
    add esi, 4  
    add edi, 4  
    loop loop_start  

; Лучше:  
mov ecx, 100  
mov ebx, 10           ; Вынесли инвариант  
loop_start:  
    mov eax, [esi]  
    add eax, ebx  
    mov [edi], eax  
    add esi, 4  
    add edi, 4  
    loop loop_start
```<button class="copy-code-btn st-button_style_none copy-code-btn__code-block" title="Скопировать код" type="button"><svg fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19.2 3H10.2C9.2073 3 8.4 3.8073 8.4 4.8V8.4H4.8C3.8073 8.4 3 9.2073 3 10.2V19.2C3 20.1927 
                      3.8073 21 4.8 21H13.8C14.7927 21 15.6 20.1927 15.6 19.2V15.6H19.2C20.1927 15.6 21 14.7927 21 13.8V4.8C21 3.8073 
                      20.1927 3 19.2 3ZM4.8 19.2V10.2H13.8L13.8018 19.2H4.8ZM19.2 13.8H15.6V10.2C15.6 9.2073 14.7927 8.4 13.8 8.4H10.2V4.8H19.2V13.8Z" fill="#777777"></path></svg>
                  </button>
```

4\. Использование регистров вместо памяти. Минимизация обращений к памяти внутри цикла. Пример:

```
```x86asm
; Плохо:  
mov ecx, 100  
loop_start:  
    mov eax, [esi]  
    add eax, [edi]    ; Чтение из памяти  
    mov [esi], eax  
    add esi, 4  
    add edi, 4  
    loop loop_start  

; Лучше:  
mov ecx, 100  
loop_start:  
    mov eax, [esi]  
    mov ebx, [edi]    ; Загрузили в регистр  
    add eax, ebx  
    mov [esi], eax  
    add esi, 4  
    add edi, 4  
    loop loop_start
```<button class="copy-code-btn st-button_style_none copy-code-btn__code-block" title="Скопировать код" type="button"><svg fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19.2 3H10.2C9.2073 3 8.4 3.8073 8.4 4.8V8.4H4.8C3.8073 8.4 3 9.2073 3 10.2V19.2C3 20.1927 
                      3.8073 21 4.8 21H13.8C14.7927 21 15.6 20.1927 15.6 19.2V15.6H19.2C20.1927 15.6 21 14.7927 21 13.8V4.8C21 3.8073 
                      20.1927 3 19.2 3ZM4.8 19.2V10.2H13.8L13.8018 19.2H4.8ZM19.2 13.8H15.6V10.2C15.6 9.2073 14.7927 8.4 13.8 8.4H10.2V4.8H19.2V13.8Z" fill="#777777"></path></svg>
                  </button>
```

5\. Устранение зависимостей данных. Параллельное выполнение независимых операций. Пример:

```
```x86asm
; Плохо (зависимость по EAX):  
mov ecx, 100  
loop_start:  
    add eax, [esi]  
    add eax, [edi]    ; Ждёт завершения предыдущего ADD  
    mov [esi], eax  
    add esi, 4  
    add edi, 4  
    loop loop_start  

; Лучше:  
mov ecx, 100  
loop_start:  
    mov ebx, [esi]  
    add ebx, [edi]    ; Независимая операция  
    mov [esi], ebx  
    add esi, 4  
    add edi, 4  
    loop loop_start
```<button class="copy-code-btn st-button_style_none copy-code-btn__code-block" title="Скопировать код" type="button"><svg fill="none" height="24" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M19.2 3H10.2C9.2073 3 8.4 3.8073 8.4 4.8V8.4H4.8C3.8073 8.4 3 9.2073 3 10.2V19.2C3 20.1927 
                      3.8073 21 4.8 21H13.8C14.7927 21 15.6 20.1927 15.6 19.2V15.6H19.2C20.1927 15.6 21 14.7927 21 13.8V4.8C21 3.8073 
                      20.1927 3 19.2 3ZM4.8 19.2V10.2H13.8L13.8018 19.2H4.8ZM19.2 13.8H15.6V10.2C15.6 9.2073 14.7927 8.4 13.8 8.4H10.2V4.8H19.2V13.8Z" fill="#777777"></path></svg>
                  </button>
```

6\. Инструкции SIMD (SSE/AVX) Обработка нескольких данных одной командой. Пример

```x86asm
section .data  
    arr1 dd 1.0, 2.0, 3.0, 4.0  
    arr2 dd 5.0, 6.0, 7.0, 8.0  

section .text  
    global _start  

_start:  
    mov ecx, 4  
    mov esi, arr1  
    mov edi, arr2  

loop_start:  
    movaps xmm0, [esi]  ; Загрузка 4 float  
    movaps xmm1, [edi]  
    addps xmm0, xmm1    ; Параллельное сложение  
    movaps [esi], xmm0  
    add esi, 16  
    add edi, 16  
    sub ecx, 1  
    jnz loop_start
```

# Разное

**Представление чисел** По умолчанию десятичное. Другие форматы:

<table border="1" id="bkmrk-%D0%9F%D1%80%D0%B5%D1%84%D0%B8%D0%BA%D1%81-%D0%9F%D0%BE%D1%81%D1%82%D1%84%D0%B8%D0%BA%D1%81-%D0%9E%D0%BF%D0%B8" style="border-collapse: collapse; width: 100%; height: 119.188px;"><colgroup><col style="width: 33.3333%;"></col><col style="width: 33.3333%;"></col><col style="width: 33.3333%;"></col></colgroup><thead><tr style="height: 29.7969px;"><td style="height: 29.7969px;">Префикс

</td><td style="height: 29.7969px;">Постфикс</td><td style="height: 29.7969px;">Описание</td></tr></thead><tbody><tr style="height: 29.7969px;"><td style="height: 29.7969px;">0b</td><td style="height: 29.7969px;">b</td><td style="height: 29.7969px;">Двоичное</td></tr><tr style="height: 29.7969px;"><td style="height: 29.7969px;">0x</td><td style="height: 29.7969px;">h</td><td style="height: 29.7969px;">Шестнадцатеричное</td></tr></tbody></table>

в

# Регистры

Запись в часть 64-битного регистра, например в регистр AL, влияет только на биты этой части. В случае AL загрузка 8-битного значения изменяет младшие 8 битов RAX, оставляя остальные 48 бит без изменений.

Виды регистров

<table border="1" id="bkmrk-%D0%9D%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%A0%D0%B0%D0%B7%D1%80%D1%8F%D0%B4-%D0%A2%D0%B8%D0%BF-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 11.2028%;"></col><col style="width: 8.22507%;"></col><col style="width: 15.1371%;"></col><col style="width: 65.435%;"></col></colgroup><thead><tr><td>Название</td><td>Разряд</td><td>Тип</td><td>Назначение</td></tr></thead><tbody><tr><td>RAX

EAX

AX

AH, AL

</td><td>64

32

16

8

</td><td>Универсальный</td><td>(Accumulator): для арифметических операций</td></tr><tr><td>RBX

EBX

BX

BH, BL

</td><td>64

32

16

8

</td><td>Универсальный</td><td>(Base pointer): указатель на базу стека внутри функции</td></tr><tr><td>RCX

ECX

CX

CH, CL

</td><td>64

32

16

8

</td><td>Универсальный</td><td>(Counter): для хранения счетчика цикла</td></tr><tr><td>RDX

EDX

DX

DH, DL

</td><td>64

32

16

8

</td><td>Универсальный</td><td>(Data): для арифметических операций и операций ввода-вывода</td></tr><tr><td>R8-R15

R8D-R15D

R8W-R15W

R8B-R15B

</td><td>64

32

16

8

</td><td>Универсальный</td><td>8 универсальных регистров</td></tr><tr><td>RSP

ESP

SP

</td><td>64

32

16

</td><td>Указатели</td><td>(Stack pointer): указатель на верхушку стека</td></tr><tr><td>RBP

EBP

BP

</td><td>64

32

16

</td><td>Указатели</td><td>(Base pointer): указатель на базу стека внутри функции</td></tr><tr><td>RSI

ESI

SI

</td><td>64

32

16

</td><td>Индексы</td><td>(Source index): указатель на источник при операциях с массивом</td></tr><tr><td>RDI

EDI

DI

</td><td>64

32

16

</td><td>Индексы</td><td>(Destination index): указатель на место назначения в операциях с массивами</td></tr><tr><td>RFLAGS

</td><td></td><td>Флаги</td><td>Биты состояния процессора после предыдущей операции</td></tr><tr><td>RIP

EIP

</td><td>64

32

</td><td>Специальный</td><td>Счетчик команд</td></tr><tr><td>ST0 - ST7

</td><td>80

</td><td>Специальные</td><td>Регистры для работы с числами с плавающей точкой</td></tr><tr><td>YMM0-YMM15

XMM0 - XMM15

</td><td>256

128

</td><td>  
</td><td>Каждый регистр можно настроить как четыре 32-битных регистра с плавающей точкой; два 64-битных регистра двойной точности с плавающей точкой; или шестнадцать 8-битных, восемь 16-битных, четыре 32-битных, два 64-битных или один 128-битный целочисленный регистр.

</td></tr></tbody></table>

Регистр флагов RFLAGS:

<table class="tab" id="bkmrk-%D0%91%D0%B8%D1%82-%D0%98%D0%BC%D1%8F-%D0%BD%D0%B0%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-0" style="width: 100%;"><tbody><tr class="tabhead"><td style="width: 5.7211%;">Бит

</td><td style="width: 6.43127%;">Имя

</td><td style="width: 87.8476%;">назначение

</td></tr><tr><td style="width: 5.7211%;">0

</td><td style="width: 6.43127%;">CF

</td><td style="width: 87.8476%;">Флаг переноса (Carry flag):казывает, был ли при сложении перенос или заимствование при вычитании. Используется в качестве входных данных для инструкций сложения и вычитания.

</td></tr><tr><td style="width: 5.7211%;">2

</td><td style="width: 6.43127%;">PF

</td><td style="width: 87.8476%;">Флаг четности: устанавливается, если младшие 8 битов результата содержат четное число единиц.

</td></tr><tr><td style="width: 5.7211%;">4

</td><td style="width: 6.43127%;">AF

</td><td style="width: 87.8476%;">Флаг настройки: указывает, был ли при сложении перенос или заимствование при вычитании младших 4 битов.

</td></tr><tr><td style="width: 5.7211%;">6

</td><td style="width: 6.43127%;">ZF

</td><td style="width: 87.8476%;">Флаг нуля (Zero flag): устанавливается, если результат операции равен нулю

</td></tr><tr><td style="width: 5.7211%;">7

</td><td style="width: 6.43127%;">SF

</td><td style="width: 87.8476%;">Флаг знака (Sign flag): устанавливается, если результат операции отрицательный.

</td></tr><tr><td style="width: 5.7211%;">8

</td><td style="width: 6.43127%;">TF

</td><td style="width: 87.8476%;">Флаг прерывания выполнения (Trap flag): используется при одношаговой отладке.

</td></tr><tr><td style="width: 5.7211%;">9

</td><td style="width: 6.43127%;">IF

</td><td style="width: 87.8476%;">Флаг разрешения прерывания: установка этого бита разрешает аппаратные прерывания.

</td></tr><tr><td style="width: 5.7211%;">10

</td><td style="width: 6.43127%;">DF

</td><td style="width: 87.8476%;">Флаг направления: контролирует направление обработки. Если не установлен, то порядок от самого младшего до самого старшего адреса. Если установлен, то порядок обратный - от самого старшего до самого младшего адреса.

</td></tr><tr><td style="width: 5.7211%;">11

</td><td style="width: 6.43127%;">OF

</td><td style="width: 87.8476%;">Флаг переполнения (Overflow flag): если устанавлен, то операция привела к переполнению со знаком.

</td></tr><tr><td style="width: 5.7211%;">12-13

</td><td style="width: 6.43127%;">IOPL

</td><td style="width: 87.8476%;">Уровень привилегий ввода-вывода (I/O privilege level): уровень привилегий текущего выполняемого потока. IOPL 0 — это режим ядра, а 3 — пользовательский режим.

</td></tr><tr><td style="width: 5.7211%;">14

</td><td style="width: 6.43127%;">NT

</td><td style="width: 87.8476%;">Флаг вложенной задачи (Nested task flag): управляет цепочкой прерываний.

</td></tr><tr><td style="width: 5.7211%;">16

</td><td style="width: 6.43127%;">RF

</td><td style="width: 87.8476%;">Флаг возобновления (Resume flag): используется для обработки исключений во время отладки.

</td></tr><tr><td style="width: 5.7211%;">17

</td><td style="width: 6.43127%;">VM

</td><td style="width: 87.8476%;">Флаг режима виртуальной машины 8086: если установлен, режим совместимости с 8086 активен. Этот режим позволяет запускать некоторые приложения MS-DOS в контексте операционной системы в защищенном режиме.

</td></tr><tr><td style="width: 5.7211%;">18

</td><td style="width: 6.43127%;">AC

</td><td style="width: 87.8476%;">Флаг проверки выравнивания (Alignment check flag): если установлен, проверка выравнивания памяти активна. Например, если установлен флаг AC, сохранение 16-битного значения по нечетному адресу вызывает исключение проверки выравнивания. Процессоры x86 могут выполнять невыровненный доступ к памяти, когда этот флаг не установлен, но количество требуемых командных циклов может увеличиться.

</td></tr><tr><td style="width: 5.7211%;">19

</td><td style="width: 6.43127%;">VIF

</td><td style="width: 87.8476%;">Флаг виртуального прерывания (Virtual interrupt flag): виртуальная версия флага IF в виртуальном режиме 8086..

</td></tr><tr><td style="width: 5.7211%;">20

</td><td style="width: 6.43127%;">VIP

</td><td style="width: 87.8476%;">Флаг ожидания виртуального прерывания: Устанавливается, когда прерывание находится в состоянии ожидания в виртуальном режиме 8086.

</td></tr><tr><td style="width: 5.7211%;">21

</td><td style="width: 6.43127%;">ID

</td><td style="width: 87.8476%;">Флаг ID: если этот бит установлен, то поддерживается инструкция cpuid. Эта инструкция возвращает идентификатор процессора и информацию о его функциях.

</td></tr></tbody></table>

# Задачи

Задача 1.

<details id="bkmrk-%D0%9D%D0%B0%D0%BF%D0%B8%D1%88%D0%B8%D1%82%D0%B5-%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%83%2C-"><summary>Вычислить выражение: (a + b) \* c - d, где a=5, b=3, c=4, d=8. Результат вывести в консоль.</summary>

<div>global main</div><div>extern printf</div><div>section .data</div><div> cura dd 5</div><div> curb dd 3</div><div> curc dd 4</div><div> curd dd 8</div><div> mymsg db "%d",10,0</div><div>section .text</div><div>main: </div><div> mov eax, 0 </div><div> add eax, [cura] </div><div> add eax, [curb] </div><div> mov edx, [curc] </div><div> mul dword [curc]; in ax (a+b)*c </div><div> sub eax, [curd] </div><div> mov rsi, rax ; print results </div><div> mov rax, 0 </div><div> mov rdi, mymsg </div><div> call printf </div><div> ret</div></details>Проверяет: Работу с регистрами, базовые арифметические операции.

Задача 2.

<details id="bkmrk-%D0%9F%D0%B5%D1%80%D0%B5%D0%B2%D0%B5%D1%80%D0%BD%D1%83%D1%82%D1%8C-%D1%81%D1%82%D1%80%D0%BE%D0%BA%D1%83-%22"><summary>Перевернуть строку "Hello!" и вывести. Не использовать внешние функции кроме системных вызовов.</summary>

<div>global _start</div><div>section .data</div><div>msg db "abcdefg",10,0</div><div>msg_full_len equ $-msg</div><div>msg_half_len equ (msg_full_len - 2)/2</div><div>section .text</div><div>_start:</div><div> ; mirroring string</div><div> mov rcx, msg_half_len</div><div> mov r8, msg_full_len - 3</div><div> mov r9, 0</div><div> mov rsi, msg</div><div>loop:</div><div> mov bl, [rsi + r8]</div><div> mov dl, [rsi + r9]</div><div> mov [rsi + r9], bl</div><div> mov [rsi + r8], dl</div><div> dec r8</div><div> inc r9</div><div> dec rcx</div><div> jnz loop</div><div> ; printing string</div><div> mov rax, 1</div><div> mov rdi, 1</div><div> mov rsi, msg</div><div> mov rdx, msg_full_len-1</div><div> syscall</div><div> mov rax, 60</div><div> mov rdi, 0</div><div> syscall</div></details>Проверяет: Работу с памятью, циклами, обработку строк.

Задача 3.

<details id="bkmrk-%D0%92-%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B5-%D1%87%D0%B8%D1%81%D0%B5%D0%BB-%5B7%2C-"><summary>В массиве чисел \[7, 2, 9, 1, 5\] найти максимальный элемент и вывести его.</summary>

global main  
extern printf

section .data  
 nums db 7, 2, 9, 1, 5  
 nums\_len equ $-nums  
 infostr db "Max number: %d",10,0

section .text  
main:  
 mov rcx, nums\_len  
 mov rax, 0  
mainloop:  
 cmp byte \[nums + rcx-1\], al  
 ja new\_max  
 dec rcx  
 jnz mainloop  
 jmp progend  
new\_max:  
 mov al, \[nums + rcx -1\]  
 dec rcx  
 jnz mainloop  
progend:  
 mov rsi, rax  
 mov rax, 0   
 mov rdi, infostr   
 call printf   
 mov rax, 60  
 syscall

</details>Проверяет: Работу с массивами, условные переходы.

Задача 4.

<details id="bkmrk-%D0%A0%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D1%83%D0%B9%D1%82%D0%B5-%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D0%B2%D0%BD"><summary>Реализуйте рекурсивную функцию вычисления факториала для n=5</summary>

extern printf

section .data  
 msg db "Factorial: %d",10,0  
 fact equ 5

section .text  
 global main

main:  
 mov rcx, fact  
 mov rax, 1  
 call factorial

 mov rsi, rax  
 mov rax, 0   
 mov rdi, msg   
 call printf   
 mov rax, 60  
 xor rdi, rdi  
 syscall

factorial:  
 mul rcx  
 dec rcx  
 cmp rcx, 1  
 jnz factorial  
 ret

</details>Проверяет: Понимание стека, рекурсии, соглашений о вызовах.

Задача 5.

# Строки и вывод данных

**Завершение программы**

```
    mov rax, 60
    mov rdi, 0
    syscall
```

При использовании gcc можно

```
    ret
```

**Код возврата**

**Linux**

Без отладчика можно смотреть состояние одного регистра за счет копирования его в регистр rdi (Linux) при завершении программы.

```
global _start           ; делаем метку метку _start видимой извне
 
section .text           ; объявление секции кода
_start:                 ; объявление метки _start - точки входа в программу 
    mov rdi, 23         ; помещаем в регистр rdi код возврата - 23 
    mov rax, 60         ; 60 - номер системного вызова exit
    syscall             ; выполняем системный вызов exit
```

Затем выполняется приложение, команда $? выводит код завершения предыдущей команды

```
root@Eugene:~/asm# ./hello
root@Eugene:~/asm# echo $?
23
```

**Windows:**

```
global _start       ; делаем метку метку _start видимой извне
 
section .text       ; объявление секции кода
_start:             ; метка _start - точка входа в программу
    mov rax, 23     ; помещаем в регистр rax код возврата - 23 
    ret             ; выход из программы
```

Получение кода возврата

```
echo %ERRORLEVEL%
```


**Строки**

Статичное определение строки и длины (для последующего вывода)

```
section .data
	msg db "Hello!",10,0
    msg_len equ $ - msg  ; $ - текущая позиция ассемблера
    msg_half_len equ (msg_full_len - 2)/2 ; возможен такой расчет
```

Использование системного вызова

```
global main
section .data
	msg db "Hello",10,0
    msg_len equ $ - msg
section .text
main: 
	mov rax, 1
    mov rdi, 1
    mov rsi, msg
    mov rdx, [msg_len] - 1
    syscall
    ret
```

Использование функции C

```
global main
extern printf
section .data
  fmtint db "Result: %d",10,0
  fmtstr db "Outstring: %s",10,0
  msg db "for textout",0
...
section .text
main: 
...
; вывод числа
	mov rsi, <переменная> ; в rsi то что нужно вывести
    mov rax, 0
	mov rdi, fmtint 
	call printf 
; вывод строки
	mov rsi, msg
    mov rax, 0
	mov rdi, fmtstr 
	call printf 
```

# Данные

Типы данных

<table border="1" id="bkmrk-db-%D0%B1%D0%B0%D0%B9%D1%82-dw-%D1%81%D0%BB%D0%BE%D0%B2%D0%BE-dd-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 22.8039%;"></col><col style="width: 77.1961%;"></col></colgroup><tbody><tr><td>db</td><td>байт</td></tr><tr><td>dw</td><td>слово</td></tr><tr><td>dd</td><td>двойное слово</td></tr><tr><td>dq</td><td>двойное длинное слово</td></tr></tbody></table>

Для строк в конце добавляется завершающий 0 (NULL).

**Массив:**

```
nums dq 11, 12, 13, 14, 15, 16, 17 ; семь 8-байтных чисел
```

Как и всегда, хранится адрес первого элемента.

times определяет массив одинаковых элементов.

```
numb:  times 10 db 2 ; десять чисел, каждое из которых равно 2, 1-байтные
```

Упрощенный вариант выделения памяти для массива (начальные 0):

<table border="1" id="bkmrk-resb-%D0%B2%D1%8B%D0%B4%D0%B5%D0%BB%D1%8F%D0%B5%D1%82-%D0%BD%D0%B5%D0%BA%D0%BE%D1%82%D0%BE" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 28.1548%;"></col><col style="width: 71.9644%;"></col></colgroup><tbody><tr><td>resb</td><td>выделяет некоторое количество байт</td></tr><tr><td>resw</td><td>выделяет некоторое количество слов (2-х байтовых чисел)</td></tr><tr><td>resd</td><td>выделяет некоторое количество двойных слов (4-х байтовых чисел)</td></tr><tr><td>resq</td><td>выделяет некоторое количество четверных слов (8-х байтовых чисел)</td></tr></tbody></table>

Пример:

```
buffer resb 10
```

Значение переменной получаем при \[\], по умолчанию адрес переменной. В Windows лексика сложнее.

**Структура:**

Простое определение - через метку и смещение

```
section .data
; условная структура
person:
    db "Alice",10    ; имя
    dq 34           ; возраст
 
; смещение компонентов в структуре
NAME_OFFSET equ 0
AGE_OFFSET equ 6
section .text
_start:
     
    mov rsi, person     ; в RSI - адрес строки
    mov rdi, 1          ; в RDI - дексриптор вывода в стандартный поток (консоль)
    mov rdx, AGE_OFFSET    ; в RDX - длина строки
    mov rax, 1        ; в RAX - номер функции для вывода в поток 
    syscall              ; вызываем функцию Linux
 
    mov rdi, [rsi + AGE_OFFSET]  ; в RDI - возраст
```

Другой способ:

```
struc имя_структуры 
 
    поле_1:      тип_поля_1    размер_поля_1
    поле_2:      тип_поля_2    размер_поля_2
    ........................................
    поле_N:      тип_поля_N   размер_поля_N
 
endstruc
```

Пример:

```
struc person
    .id:        resd 1  ; 4 байта (d=double word)
    .name:      resb 20 ; 20 байт (b=byte)
    .age:       resw 1  ; 2 байта (w=word)
endstruc

person.id = 0
person.name = 4 (потому что .id занял 4 байта)
person.age = 24 (потому что .name занял 20 байт после .id)
person_size = 26 (общий размер: 4 + 20 + 2)
```

Т е при использовании struc не нужно самому высчитывать адреса меток.

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

Неинициализированный экземпляр (в секции .bss) Это самый простой способ, использующий метку \_size:

```
section .bss
    person1: resb person_size  ; Выделить 26 байт под один экземпляр
    person2: resb person_size  ; Выделить еще 26 байт
```

Инициализированный экземпляр (в секции .data)  
Для создания экземпляра с начальными значениями используются макросы ISTRUC, AT и IEND.

```
section .data
    tom:
        istruc person      ; Начало экземпляра структуры person
            at person.id,   dd 101           ; в поле .id число 101 
            at person.name, db "Tom", 0  ; в .name строка "Tom", 0
            ; (Оставшиеся байты .name будут неявно заполнены нулями)
            at person.age, dw 2            ; в поле .age число 2
        iend                ; Завершение экземпляра структуры person
```

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

Доступ к полям структуры осуществляется путем сложения базового адреса экземпляра структуры с меткой-смещением нужного поля:

Пример доступа к полям экземпляра tom (из .data):

```
; Поместить ID в EAX
mov eax, [tom + person.id]   ; EAX = 101
 
; Поместить возраст в BX
mov bx, [tom + person.age]  ; BX = 2
 
; Получить адрес имени (например, для вызова функции)
lea esi, [tom + person.name]
; Теперь ESI указывает на строку "Tom"
```

**Преобразование разрядности.**

При несоответствии разрядности регистра и памяти желательно точно определять, что делать.

```
...
section .data
nums db 1, 2, 0, 0, 0, 0, 0, 0
...
movzx rax, byte [nums]
```

- byte: преобразует в байт
- word: преобразует в слово
- dword: преобразует в двойное слов
- qword: преобразует в четверное слово

**Точка определения данных:**

<table border="1" id="bkmrk-section-.text-%D0%94%D0%BE%D0%BB%D0%B6%D0%BD%D1%8B" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 24.1072%;"></col><col style="width: 76.012%;"></col></colgroup><tbody><tr><td>section .text</td><td>Должны определяться либо до первой инструкции, либо после последней инструкции. Только константы.</td></tr><tr><td>section .data</td><td>Наиболее логичная точка размещения. Но все данные в этой секции размещаются в бинарнике и затем копируются в ОП.</td></tr><tr><td>section .rodata</td><td>Раздел только для чтения. Отличие от констант в том, что занимают память. Константы подставляются во время компиляции. Нельзя сделать массив констант.</td></tr><tr><td class="align-left">section .bss</td><td class="align-left">Логичнее размещать здесь неизвестные сначала данные, resb/... Не занимается память в бинарнике,</td></tr></tbody></table>

**Косвенная адресация.**

Обращение по некоторому адресу: \[base + (index \* scale) + offset\] Компоненты:

<table border="1" id="bkmrk-base-%D0%B1%D0%B0%D0%B7%D0%BE%D0%B2%D1%8B%D0%B9-%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 12.3215%;"></col><col style="width: 87.7977%;"></col></colgroup><tbody><tr><td>base</td><td>базовый регистр, который содержит некоторый адрес. Это может быть 64-разрядный или 32-разрядный регистр общего назначения или регистр RSP</td></tr><tr><td>index</td><td>индексный регистр, который содержит некоторый индекс относительно адреса в базовом регистре. В качестве индексного регистра также могут выступать 64-разрядный или 32-разрядный регистр общего назначения или регистр RSP</td></tr><tr><td>scale</td><td>множитель, на который умножается значение индексного регистра. Может принимать значения 1, 2, 4 или 8</td></tr><tr><td>offset</td><td>может представлять 32-разрядное значение в виде числа или имени переменной. Это может быть 64-разрядный регистр общего назначения или регистр RSP</td></tr></tbody></table>

# Стек

LIFO. Управляется через регистр RSP. Когда программа начинает выполняться, ОС инициализирует регистр RSP адресом последней ячейки памяти в сегменте стека. Размер стека зависит от системы. На Linux х86-64 стек ограничен 2 мегабайтами.

Стек растет от больших адресов к меньшим. При начале стек выровнен по 16-байтовой границе.

**Использование стека**

push добавляет данные в стек. Возможно добавить 16- и 64-разрядный регистр, адрес в памяти 16- и 64-разрядного числа или значение 16- и 32-разрядной константы (32-битная констранта расширяется до 64 бит).

При выполнении инструкции push от значения регистра RSP вычитается размер операнда. А по адресу, который хранится в стеке, помещается значение операнда.

pop получает из стека значение, адрес которого в регистре RSP. Можно сохранить в 16- и 64-разрядный регистр или адрес в памяти 16- и 64-разрядного числа.

```
global _start
 
section .text
_start:
    mov rdx, 15
    push rdx            ; в стек помещаем содержимое регистра RDX
    pop rdi             ; значение из вершины стека помещаем в регистр RDI
    mov rax, 60
    syscall
```

Сохранение регистров в стек

```
global _start
 
section .text
_start:
... 
    push rdi
    push rdx
...
    pop rdx
    pop rdi
```

Сохранение флагов состояния

pushfq и popfq (без аргументов) сохраняет/восстанавливает регистр RFLAGS

**Восстановление стека без извлечения данных**

При завершении программы нужно восстановить адрес в RSP. Можно через pop. Можно прибавть нужное значение к RSP

```
global _start
 
section .text
_start:
    mov rdi, 11
    mov rdx, 33
 
    push rdi
    push rdx
 
    add rsp, 16     ; прибавляем к адресу в RSP 16 байт 
 
    mov rax, 60
    syscall
```

**Резервирование пространства в стеке:**

```
global _start
 
section .text
_start:
    sub rsp, 16  ; резервируем в стеке 16 байт
     
    ; некоторая работа со стеком
 
    add rsp, 16     ; восстанавливаем значение стека
    mov rax, 60
    syscall
```

**Косвенная адресация в стеке**

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

```
global _start
 
section .text
_start:
    push 12
    push 13
    push 14
    push 15
 
    mov rdi, [rsp+16]      ; [rsp+16] - адрес значения 13
 
    add rsp, 32     ; восстанавливаем значение стека
 
    mov rax, 60
    syscall
```

# Функции и прерывания

Функция - набор инструкций под некоторой меткой (имя функции). Функции завершается ret. Вызываемые функции могут вызывать другие функции.

```
sum:
    mov rdi, 7
    mov rsi, 5
    add rdi, rsi
    ret
```

Вызов функции:

```
call название_функции
```

Call помещает в стек 64-битный адрес инструкции, которая идет сразу после вызова. Это называется адресом возврата. Когда процедура завершает выполнение, для возвращения к вызывающему коду она выполняет инструкцию ret. Команда ret извлекает 64-битный адрес возврата из стека и косвенно передает управление на этот адрес.

```
global _start

section .text
_start:
    call sum
    mov rax, 60
    syscall

sum:
    mov rdi, 7
    mov rsi, 5
    add rdi, rsi
    ret
```

**Стек и функции**

При вызове ret на верхушке стека должен быть адрес возврата. Иначе скорее всего будет ошибка "Segmentation fault":

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

Можно использовать этот адрес для выхода из функции:

```
global _start
 
section .text
_start:
    mov rdi, 5
    mov rsi, 20
    call sum 
 
    add rdi, 10      ; RDI = 15
    mov rax, 60
    syscall
 
; определяем функцию sum
sum:
    jmp [rsp]        ; переходим по адресу, который храниться в RSP
    add rdi, rsi        ; эта строка НЕ выполняется
    ret
```

Функции могут использовать регистры. Поэтому нужно сохранять нужные регистры перед вызовом функций.

**Передача и возврат параметров**

Для передачи параметров применяются регистры, стек или через глобальные переменные. Если параметров немного, то через регистры. Наиболее удобным местом для возврата результатов функции в архитектуре x86-64 являются регистры.

Как правило, результат в регистр RAX, хотя можно любой регистр общего назначения. В RAX большинство языков высокого уровня помещают результат функции. Согласно интерфейсам System ABI и Microsoft Windows ABI целочисленный результат помещается в регистр RAX.

Соглашения о вызовах (Calling Conventions) Определяют, как передавать аргументы и кто очищает стек.

Стандартные соглашения (x86)

<div class="table-scroll" id="bkmrk-%D0%A1%D0%BE%D0%B3%D0%BB%D0%B0%D1%88%D0%B5%D0%BD%D0%B8%D0%B5-%D0%9F%D0%B5%D1%80%D0%B5%D0%B4%D0%B0%D1%87%D0%B0-"><table><thead><tr><th>**Соглашение**</th><th>**Передача аргументов**</th><th>**Очистка стека**</th><th>**Используемые регистры**</th></tr></thead><tbody><tr><td>cdecl</td><td>Через стек (справа налево)</td><td>Вызывающий</td><td>`EAX`, `ECX`, `EDX` — не сохраняются</td></tr><tr><td>stdcall</td><td>Через стек (справа налево)</td><td>Вызываемая</td><td>`EAX`, `ECX`, `EDX` — не сохраняются</td></tr><tr><td>fastcall</td><td>Первые 2 — `ECX`, `EDX`, остальные — стек</td><td>Вызываемая</td><td>`EAX`, `ECX`, `EDX` — не сохраняются</td></tr></tbody></table>

</div>В случае большого объекта можно вместо значения возвратить его адрес (который занимает 8 байт).

При вызове функции доступен весь стек, выделенный в программе. Но функция может иметь свои локальные переменные. Для этого определяется фрейм стека (stack frame) - некоторая область в стеке, которая предназначена для текущей функции, включая адрес возврата, параметры и локальные переменные. Для доступа к фрейму стека предназначен регистр RBP (BP - base pointer или базовый указатель), который представляет указатель на базовый адрес фрейма стека.

```
global _start
 
section .data
nums dq 10, 20, 30, 15, 15
count equ ($-nums)/numSize    ; количество элементов
numSize equ 8   ; размер каждого элемента
 
section .text
_start:
    mov rdi, 11       ; в RDI параметр для функции sum
    call sum            ; после вызова в RAX - результат сложения
    mov rdi, rax     ; помещаем результат в RDI
    mov rax, 60
    syscall
 
sum:
    ; добавляем в стек число 5 - условная безымянная локальная переменная
    push 5          ; RSP указывает на адрес числа 5
    mov rax, rdi    ; в RAX значение параметра из RDI
    add rax, [rsp]  ; rax = rax + [rsp] = rax + 5
    add rsp, 8      ; особождаем стек
    ret
```

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

```
global _start
 
section .text
_start:
    mov rdi, 11       ; в RDI параметр для функции sum
    call sum            ; после вызова в RAX - результат сложения
    mov rdi, rax     ; помещаем результат в RDI
    mov rax, 60
    syscall
 
sum:
    sub rsp, 8         ; резервируем для двух переменных в стеке 8 байт
 
    mov dword [rsp+4], 5       ; По адресу [rsp+4] первая локальная переменная, которая равна 5
    mov dword [rsp], edi     ; По адресу [rsp] вторая локальная переменная, которая равна EDI
 
    mov eax, [rsp+4]    ; в EAX значение первой переменной (5)
    add eax, [rsp]     ; EAX = EAX + вторая переменная (edi)
 
    add rsp, 8           ; особождаем стек
    ret
```

**Установка имен переменных**

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

```
global _start
 
_a equ 4    ; смещение переменной _a относительно rsp
_b equ 0    ; смещение переменной _b относительно rsp
 
section .text
_start:
    mov rdi, 12       ; в RDI параметр для функции sum
    call sum            ; после вызова в RAX - результат сложения
    mov rdi, rax     ; помещаем результат в RDI
    mov rax, 60
    syscall
 
sum:
    sub rsp, 8         ; резервируем для двух переменных в стеке 8 байт
 
    mov dword [rsp+_a], 5      ; По адресу (rsp+4) первая локальная переменная, которая равна 5
    mov dword [rsp + _b], edi    ; По адресу (rsp) вторая локальная переменная, которая равна EDI
 
    mov eax, [rsp+_a]     ; в EAX значение первой переменной
    add eax , [rsp + _b]    ; EAX = EAX + вторая переменная
 
    add rsp, 8           ; особождаем стек
    ret
```

**Регистр RBP**

Для управления доступом к различным частям фрейма стека Intel предоставляет специальный регистр - RBP (Base Pointer). А для доступа к объектам во фрейме стека можно использовать смещение до нужного объекта относительно адреса из регистра RBP.

Вызывающий функцию код отвечает за выделение памяти для параметров в стеке и перемещение данных параметра в соответствующее место. Инструкция call помещает адрес возврата в стек. Функция несет ответственность за создание остальной части фрейма, в частности, за добавление локальных переменных. Для этого при вызове функции значение RBP помещается в стек (поскольку при вызове функции в RBP значение вызывающего кода, и это значение надо сохранить), а значение указателя стека RSP копируется в RBP. Затем в стеке освобождается место для локальных переменных.

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

```
global _start
 
section .text
_start:
    mov rdi, 11       ; в RDI параметр для функции sum
    call sum            ; после вызова в RAX - результат сложения
    mov rdi, rax     ; помещаем результат в RDI
    mov rax, 60
    syscall
 
sum:
    push rbp              ; сохраняем старое значение RBP в стек
    mov rbp, rsp         ; копируем текущий адрес из RSP в RBP
    sub rsp, 16          ; выделяем место для двух переменных по 8 байт
 
    mov qword[rbp-8] , 7      ; По адресу [rbp-8] первая локальная переменная, равная 7
    mov qword [rbp-16], rdi    ; По адресу [rbp-16] вторая локальная переменная, равная RDI
 
    mov rax, [rbp-8]    ; в RAX значение из [rbp-8]  - первая локальная переменная
    add rax, [rbp-16]    ; RAX = RAX + [rbp-16] - вторая локальная переменная
 
    mov rsp, rbp         ; восстанавливаем ранее сохраненное значение RSP 
    pop rbp               ; восстанавливем RBP
     
    ret
```

  
**Инструкции enter и leave**

Поскольку данная схема работа с регистром %rbp довольно распространена, то для упрощения ассемблер NASM предоставляет две дополнительные инструкции. Так, вместо кода:

```
push rbp
mov rbp, rsp
sub rsp, N_байтов 
```

Можно применять следующую инструкцию:

```
enter N_байтов, 0
```

Инструкции enter передается выделяемое в стеке количество байт, а второй параметр - число 0. При выполнении эта инструкция сама сохранит старое значение %rbp в стек, скопирует значение rsp в rbp и выделит в стеке N\_байтов.

А вместо кода

```
mov rsp, rbp
pop rbp 
```

Можно применить

```
leave
```

# Системные и внешние вызовы

**Syscall**

Инструкция процессора, мост между ядром и непривилегированными программами. Для вызова заполняются регистры в соответствии с соглашениями ABI (Application Binary Interface). Есть обновляемая [таблица системных вызовов](https://syscalls.mebeim.net/?table=x86/64/x64/v6.5) Номер функции размещается в регистре rax, Аргументы функции последовательно в регистрах rdi, rsi, rdx, r10, r8, r9.

syscall изменяет регистры rcx и r11. В регистр RCX сохраняется предыдущее значение регистра RIP - адрес следующей инструкции, которую будут выполнять приложение после завершения системного вызова, а в RIP помещается адрес обработчика системного вызова. Также syscall изменяет регистр флагов RFLAGS в соответствии с системным вызовом, а старое значение RFLAGS сохраняется в регистр r11. Поэтому, если программа использует регистры rcx и r11, то перед выполнением системного вызова эти регистры следует сохранить, например, в стек, чтобы не потерять их содержимое.

Кроме того, системный вызов может возвращать некоторый результат, который помещается в регистр rax.

# Kerberos

# Общая информация

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

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