13 Commits

Author SHA1 Message Date
e219503f22 docs: документирование ассемблерной части
- Добавил комментарии в time.asm

- Добавил секцию про ассемблер в README
2024-09-14 16:12:58 +03:00
55c5096d49 feat: добавил болььше пояснений для заигрываний с консолью 2024-09-14 15:39:28 +03:00
c42d755e80 docs(README): добавлена секция про виртуальные адреса 2024-09-13 00:09:30 +03:00
6c9d810c6e chore: изменения в коде шаблона 2024-09-13 00:08:49 +03:00
a5a36086b0 feat: написана заготовка под замеры времени 2024-09-13 00:07:13 +03:00
acc1ada69c docs: в README 1 лабы добавлена секция про ассемблеру 2024-09-12 20:14:32 +03:00
ca7fbed016 feat: переписано все, что не ассемблер 2024-09-12 16:25:41 +03:00
3412167a09 docs: описаны манипуляции с установкой параметров терминала 2024-09-11 18:25:36 +03:00
4c423c7acc feat: новые функции были подшиты к основному шаблону 2024-09-10 23:53:08 +03:00
a338ac968e chore: переименовал файл шаблона, так как стало несколько файлов 2024-09-10 23:44:30 +03:00
386c7be1a7 feat: написал замены функциям
Для повторения bioskey было написано несколько новых функций, переводящих теорминал в неканонический режим и читающие нажатия на клавиши. Также все наработки были вынесены в отдельный файл, чтобы не засорять основной файл
2024-09-10 23:43:12 +03:00
34d0c33e87 chore: шаблон починен на 30%
Заменены все поломанные символы на нормальный текст, сделан проход форматером, заменен delay
2024-09-10 21:15:57 +03:00
65d5c3f5c3 feat: смог очистить от комментариев и отформатировать первый шаблон 2024-09-10 20:13:01 +03:00
9 changed files with 446 additions and 120 deletions

View File

@ -2,3 +2,145 @@
## Введение в низкоуровневое программирование. Встроенный отладчик. Встроенный Ассемблер
## Переписываем шаблон
Поскольку весь шаблонный текст написан под MS-DOS, мы очевидным образом не можем его использовать для решения задачи под linux.
Замены требуют следующие функции:
- getch
- delay
- inp
- bioskey
### getch
Наиболее простая замена будет для `getch()`, поскольку единственное ее назначение - ожидать нажатия клавиши. В этом контексте у линукса есть полноценная замена в виде `system("pause")`
### delay
Здесь уже несколько посложнее, потому что DOS'овский `delay` использует задержку в миллисекундах, а линуксовый `sleep` - в секундах. Поэтому используем функцию `usleep`. Она принимает время задержки в микросекундах, поэтому для получения миллисекунда нужно просто умножить на 1000. То есть код:
```C
void delay(unsigned ms)
{
usleep(ms * 1000);
}
```
### bioskey
Из всех пока что самая сложная замена. Если вызвать `bioskey(1)`, то она вытаст 1 если какая либо клавиша была нажата и 0 если не была. при этом проверка происходит в моменте и не блокирует выполнение программы.
Для иммитации этого на линуксе нам потребуется неканонический режим ввода в терминал, а также сделать так, чтобы все печатаемое не выводилось в курсор. Этого можно добиться 2 способами:
1. Покурить гигагалактический томик по ассемблеру и узнать про системный вызов ioctl, после чего руками разметить область оперативной памяти, провести все системные вызовы, потом при помощи poll проверять наличие символов в буфере, обрабатывать ошибки и интегрировать функции через прототипы в наш код на C
2. Сдаться и выбрать путь языка C
Я уже сказал, что я из слабых, поэтому писать кусок на ассемблере как-то не горю желанием (хотя может когда-нибудь в будущем по просьбам напишу)
#### Зависимости
Язык программирования C имеет определенный уровень абстракции от конкретных системных вызовов и предоставляет нам несколько вещей:
- `<termios.h>` - структура данных, хранящая информацию о текущем состоянии терминала, а также удобные методы `tcgetattr` и `tcsetattr`
- `<unistd.h>` - Библиотека, используемая для унификации дескрипторов, битов и прочих унификаций
- `<stdlib.h>` - много чего, но нам для безопасности потребуется `atexit`, чтобы если что-то пошло не так, у нас не наебнулся терминал
Опционально берется `<stdio.h>` для целей адекватного вывода ошибок. Не обязательно, но предпочтительно
#### Реализация
Для начала нам необходимо сохранить свой текущий терминал, чтобы без проблем его восстановить в будущем, для этого заводим в памяти переменную (придется сделать ее глобальной, потому что на инкапсуляцию и защиту нет времени, нервов и желания)
```C
struct termios saved_attributes;
```
Далее сразу напишем функцию для восстановления
```C
void reset_input_mode()
{
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}
```
Здесь `STDIN_FILENO` - это дескриптор потока стандартного ввода (ввод с консоли по простяге). Вообще это число, но в `<unistd.h>` он вынесен в макрос для хоть какой-то унификации, `TCSANOW` - тоже число. В контексте функции `tcsetattr` оно заставляет изменениям в формате терминала вступить в силу немедленно, вне зависимости от того, есть ли еще в буфере текст на вывод. Другими вариантами могут стать:
- `TCSANOW` - применить изменения сразу при сигнале и продолжать предыдущий вывод с того же места, где он кончился
- `TCSADRAIN` - заставит сначала очистить текущий буфер вывода до дна, а только потом сменит режим. То есть сначала все, что было на момент запроса в буфере, будет выведено, а только потом сменится режим терминала
- `TCSAFLUSH` - то же, что и `TCSADRAIN`, только еще и сносит весь буффер ввода
```C
void set_input_mode()
{
struct termios tattr;
char *name;
// Убеждаемся, что STDIN - это терминал
if (!isatty (STDIN_FILENO))
{
fprintf (stderr, "Not a terminal.\n");
exit (EXIT_FAILURE);
}
// Сохраняем параметры текущего терминала
//для последующего восстановления
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);
// Устанавливаем все режимы, которые
// нас в общем-то интересуют
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 0;
tattr.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}
```
**Разберем некоторые строки подробнее**
`isatty(STDIN_FILENO)` - в целом `isatty` просто проверяет, является ли дескриптор файла консолью в общем смысле этого слова. Вообще в линуксе `tty` означает teletype - консоли, которые могут использоваться для выполнения команд, восстановления системы и прочего. В некоторых дистрибутивах между ними можно даже переключаться, но так как я на wsl, мне эта роскошь не доступна
`fprintf` - функция, позволяющая делать "форматированный вывод" в поток дескриптора. То есть это как `prinf`, только еще и дескрипторы принимает
`EXIT_FAILURE` и `EXIT_SUCCESS` - обозначают 1 и 0 соответственно. Используются чтобы избежать неявной договоренности между пользователями линукс, что при возвращении нуля из функции - это успех, а другого - ошибка
Очевидным образом если `tcsetattr` устанавливал параметры терминала, то `tcgetattr` должен их получать. В качестве второго параметра принимает указатель на структуру данных, где должен их сохранить. В нашем случае ей выступает упоминавшаяся выше глобальная переменная
После получения параметров мы начинаем c ними играться в флагах. В данном случае я вырубаю `ICANON` тем самым говоря, что вводить `enter` при вводе команд не обязательно, а также рублю флаг `ECHO`, из-за чего набираемые с клавиатуры символы не отображаются
`VMIN` говорит о том, что одного символа в буфере достаточно, чтобы считать, что пользователь ввел все, что хотел. Есть еще параметр `VTIME`, который говорит, что если буфер не меняется какое-то время, то пользователь закончил
Остальное нам вроде бы знакомо =)
<!---Тут следует следить за обновлениями кода в substitutions.py, а то неактуальный код - большая беда-->
## Исправление ассемблерных вставок
Должен сказать, что я не большой поклонник "inline assembly". На мой субъективный взгляд намного лучше, читаемее и стабильнее добавлять ассемблер на этапе линковки. Это дает несколько приятных бонусов:
1. Код можно поддерживать на любимом ассемблере
2. Код ассемблера можно компилить отдельно
3. Код программы на C становится ощутимо чище (*лично на мой взгляд ассемблерные вставки плохо смотрятся в коде*), а также все макросы ассемблера не касаются кода на C
4. Меньше потенциальных ошибок из-за того, что вы что-то не так поняли и откомпилировалось все неправильно
Помимо прочего очень важный момент: я использую gcc для компиляции, а в отличие от clang, он довольно ленивый и наши строки для ассемблера в нетронутом виде отправятся прямо в текст программы, которая затем будет скормлена ассемблеру. Отсюда следует несколько нюансов:
- Стандартный ассемблер, используемый `gcc` - `as` и по умолчанию он использует синтаксис AT&T. Однако я не очень люблю этот синтаксис, предпочитаю работать с синтаксисом intel. Выхода тут 2:
- Дать компилятору флаг -masm=intel, после чего уже собственный ассемблер переключится на intel синтаксис
- В начале каждой ассемблерной вставки ставить ".intel_syntax noprefix", а после вставки но перед параметрами ставить ".att_syntax prefix". Это может периодически плохо работать
- При написании ассемблера необходимо соблюдать все переносы строк и при этом указывать это явно (поэтому в конце строк у меня и появляются `\n\t` - это поддержание табуляции и переноса строки
- Компилятору надо понимать, что будет происходить с переменными и регистрами во время ассемблерной вставки, поэтому и это тоже придется указать отдельно
Собственно видно, что есть ньансы, которые и заставляют меня сделать выбор в пользу обычного ассемблера и линковки, но раз лаба хочет, чтобы использовался именно inline, то будем использовать inline
UPD 12.09.24 22:00: в самый последний момент преподаватель решил в своей методички пингануть адрес в памяти, который в ms-dos отведен для хранени данных BIOS, а конкретнее ту часть, которая отведена под системные часы насколько я понимаю. В случае DOS это вполне себе реальная память, которая вполне себе реально существует более того, в досе процессор находится в режиме реальных адресов. Linux в свою очередь относится к приколам с обращением к произвольному участку памяти как к уязвимостям, поэтому не дает просто почитать или пописать в непромапаную память. Но это пол беды на самом-то деле, ведь вся память у любой программы виртуальная и уже на уровне операционной системы и процессора перегонятся в виртуальную, поэтому даже если я воспользуюсь `mmap` и промапаю соответствующий адрес в памяти, в нем будет просто лежать мусор и не более. Поэтому последнюю часть работы, где достается время из памяти BIOS я пропускаю за невозможностью ее выполнить на машине на базе Linux
## Замеры времени ассемблерной команды
Мне лабораторную работу зачли и без этой части, но если кто-то будет сдавать ему лабу так же как и я, такое возможно не проканает, поэтому для решения задачи замера приложен файлик time.asm (по крайней мере должен быть, если я не забыл). В нем в комментариях я постарался пояснить все этапы замера времени. Но для базового понимания придется сделать некоторые пояснения относительно организационных решений. В методичке преподаватели предпочли обратиться к чтению из промапаной области памяти BIOS, так называемой BIOS data area. Неплохой вариант, если есть прямой доступ к памяти устройства, поскольку BIOS хранит в памяти довольно много полезных данных. Однако из пользовательских программ простучать эту память не получается, потому что linux будет выдавать ошибку даже если такие программы запускать под рутом.
Однако как же тогда получать время и делать другие манипуляции? Довольно просто на самом деле - системными вызовами. Системный вызов - это программное прерывание, которое просит операционную систему в режиме ядра выполнить какую-то работу: получить время, установить время, поменять разрешения на порты, сменить режимы терминала и очень многое другое вплоть до создания X-server'а. Найти системные вызовы можно прогуглив `linux syscalls table`. От себя порекомендую этот [сайт](https://syscalls.mebeim.net/?table=x86/64/x64/latest). Они вытаскивают системные вызовы из каждой версии ядра. Также возможный, пусть и требующий значительно больше знаний вариант - посмотреть стандартную библиотеку вашего компилятора C. Дело в том, что сам язык C довольно мал и весь его огромный функционал завязан на не таком уж и большом количестве зарезервированных слов и конструкций. В целом сопоставление между C и ассемблером, если компилировать без оптимизаций компилятора (`-O0`), выходит довольно однозначное. И эта же особенность заставляет постоянно переписывать те библиотеки в C, которые зависят от системы или архитектуры. **Не мудрено, что и системные вызовы тоже хранятся где-то в заголовках**. Однако я тут ничего не подскажу, так как так и не понял, где эти номера нормально записаны. Если вы знаете - пишите.

135
01-asm-basics/main.c Normal file
View File

@ -0,0 +1,135 @@
#include <stdio.h>
#include <sys/io.h>
#include <stdlib.h>
#include <unistd.h>
#include "substitutions.h"
#define PortCan0 0x40
void beep(unsigned iTone, unsigned iDlit);
void delay(unsigned int ms)
{
usleep(ms * 1000);
}
int main(void) {
long int lCnt = 0;
int iA = 0x1234;
char *pT = (char *)0x46C;
printf("\nПечатаем 10 раз значение байта с известным адресом\n");
for (int i = 0; i < 10; i++)
{
printf(" \n %d ", *pT);
}
printf("\n Для продолжения нажмите любую клавишу \n");
system("pause"); // Ждем нажатия клавиши
printf("\n Читаем содержимое порта с адресом 40 с помощью функции Си \n");
printf("\n Для выхода из цикла - нажмите любую клавишу \n");
// Линуксу не сильно нравится, что ты насилуешь порты ввода и вывода процессора, поэтому нужно выдать ему на это дело разрешение
ioperm(PortCan0, 1, 3); // Что означает тройка напишу позже
set_input_mode();
while (isKeyPressed() == 0) {
printf("\n Порт40 = %d", inb(PortCan0));
delay(500);
}
reset_input_mode();
system("pause");
printf("\n Читаем содержимое порта с адресом 40 ассемблером \n");
set_input_mode();
while (isKeyPressed() == 0) {
// asm {
// push ax
// in al,0x40
// }
unsigned char Tmm = 0;
asm (
"push rax\n\t"
"in al, 0x40"
"mov %0, al"
"pop rax"
:"=r"(Tmm)
:
:"rax"
);
delay(500);
printf("\n Порт40 = %d", Tmm);
}
reset_input_mode();
system("pause");
printf("\n Для продолжения - нажмите любую клавишу \n");
system("pause");
long *pTime = (long *)0x46C;
set_input_mode();
while (isKeyPressed() == 0) {
printf("\n %ld", *pTime);
delay(1000);
}
reset_input_mode();
system("pause");
// Данная секция закомментирована, поскльку линукс не дает обратиться к
// не промапанной и не аллоцированной памяти. Но даже если ее аллоцировать
// mmem'ом, все равно эта память будет виртуальная, поэтому смысла делать
// это не имеет. Вариант просмотра содержимого условной ячейки памяти на nasm
// приведен в файле time.asm. Объяснить тот код, который я вижу
// на базовом уровне я в состоянии
// int Time;
// set_input_mode();
// while (isKeyPressed() == 0) {
// Здесь происходит операция получения времени суток при
// помощи обращения к специально размеченой области памяти
// Однако можно ли такой фокус сделать в linux это еще надо узнать
// asm push ds
// asm push si
// asm mov ax, 40h
// asm mov ds, ax
// asm mov si, 0x6C
// asm mov ax, [ds : si]
// asm mov Time, ax
// asm pop si
// asm pop ds
// asm(
// "mov "
// );
//
// printf("\n %d", Time);
// delay(300);
// }
// reset_input_mode();
//
// beep(400, 200);
// for (lCnt = 0; lCnt < 1000000; lCnt++) {
// a1:
// asm {
// mov ax,iA
// mov ax,iA
// mov ax,iA
// mov ax,iA
// mov ax,iA
// mov ax,iA
// mov ax,iA
// mov ax,iA
// mov ax,iA
// a2:
// mov ax,iA
// }
// }
// beep(400, 200);
// здесь секция для выполнения замеров времени. Поскольку доступ к звуку
// Я иметь не могу, если не буду использовать pulseaudio, замерим старыми дедовскими методами
// При помощи clock_gettime
}

View File

@ -0,0 +1,63 @@
#include "substitutions.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <time.h>
/* Use this variable to remember original terminal attributes. */
struct termios saved_attributes;
void reset_input_mode()
{
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}
void set_input_mode()
{
struct termios tattr;
char *name;
/* Make sure stdin is a terminal. */
if (!isatty (STDIN_FILENO))
{
fprintf (stderr, "Not a terminal.\n");
exit (EXIT_FAILURE);
}
/* Save the terminal attributes so we can restore them later. */
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);
/* Set the funny terminal modes. */
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 0;
tattr.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}
void delay(unsigned int ms)
{
usleep(ms * 1000);
}
char isKeyPressed()
{
char key_handler = 0;
read(STDIN_FILENO, &key_handler, 1);
if (key_handler > 0)
{
return 1;
}
return 0;
}
int main()
{
set_input_mode();
while (isKeyPressed() == 0) {printf("hell\n");}
printf("ok\n");
reset_input_mode();
}

View File

@ -0,0 +1,9 @@
#ifndef SUBSTITUTIONS_H
#define SUBSTITUTIONS_H
void reset_input_mode();
void set_input_mode();
void delay(unsigned int ms);
char isKeyPressed();
#endif

97
01-asm-basics/time.asm Normal file
View File

@ -0,0 +1,97 @@
; Эта директива делает функцию видимой.
; По умолчанию в ассемблере используется _start,
; но поскольку для вывода на экран я пользуюсь
; С'шной функцией prinf, для корректного подключения библиотек на этапе линковки
global main
; Объявляю, что буду ссылаться на метку printf, которой нет внутри кода программы
; extern вообще обзначает, что метка объявлена где-то еще
extern printf
; тут объявлен макрос CLOCK_REALTIME, который на этапе ассемблирования заменится на число 0
; Использован он тут, так как является clock_id, о котором будет сказано позже. И я не уверен
; что на всех системах это число будет одинаково. Свое я посмотрел в файлах компилятора.
%define CLOCK_REALTIME 0
; так в ассемблере задаются структуры. Существуют они лишь на уровне препроцессора
; да и применение их весьма специфично. Но подробнее лучше погуглите
; struct timespec { time_t tv_sec; long tv_nsec; } - это шаблон из C
struc timespec
.tv_sec: resq 1
.tv_nsec: resq 1
endstruc
section .note.GNU-stack ; чтобы не жаловался линкер
; Секция с данными, ее особенность в том, что нужно указать лишь сколько нужно зарезервировать
section .bss
; вообще можно было бы использовать istruc и создать эти 2 структуры в .data, но я решил,
; что не хочу тратить время на инициализацию того, что и так будет перезаписано
; обе эти инструкции просто нужны чтобы застолбить по 16 памяти на каждый замер времени
; потому что time_t и long имеют размер 8 байт, а поля 2
start:
resq 2
finish:
resq 2
; Секция с данными, которые заранее заполняются чем-то
section .data
fstring db "Operations took %ul seconds and %ul nanoseconds", 10, 0 ; строки стиля C должны оканчиваться нулем
flen equ $-fstring ; длина строки. $ - это текущий адрес. Подробнее не буду рассказывать - мне лень
section .text
main: ; лично в моей системе time_t представляет из себя long int
mov rax, 228 ; Системный вызов получения времени
mov rdi, CLOCK_REALTIME
mov rsi, start
syscall
; здесь место для кода под замер времени
mov rcx, 20000 ; сколько раз нужно прогнать цикл
; цикл
looper:
mov rax, start
loop looper ; про это чуть позже узнаете
; замеряем время второй раз
mov rax, 228
mov rdi, CLOCK_REALTIME
mov rsi, finish
syscall
; считаем время для секунда и миллисекунд
; секунды
mov rsi, [finish + timespec.tv_sec]
sub rsi, [start + timespec.tv_sec]
; наносекунды
mov rdx, [finish + timespec.tv_nsec]
sub rdx, [start + timespec.tv_nsec]
; вызываем функцию printf. Согласно соглашению о вызовах fastcall
; при вызове функций для передачи аргументов используются регистры по порядку следования аргументов
; rdi, rsi, rdx, rcx, r8, r9, а остальные пушатся в ассемблер.
; Свои заморочки там с числами с плавающей точкой, но об этом не сейчас
mov rdi, fstring
mov rax, 0
; Вот тут все во имя выравнивания стека. Об этом я сейчас рассказывать не буду, только если попросят в readme чиркану
sub rsp, 8
; собственно вызов функции. На самом деле это обычный jmp, который предварительно пушит в стек адрес возврата.
; в будущем будьте аккуратнее с этими приколами, потому что при встрече ключевого слова ret ассемблер всегда.
; подчеркиваю ВСЕГДА прочитает 8 байт со стека и передаст туда управление. И как бы что там будет - одному богу ведомо
; Так что в ваших же интересах следить за тем, чтобы в стеке лежали правильные байты
call printf
; поскольку выравнивание больше не нужно, возвращаем стек в исходное состояние
add rsp, 8
exit:
; Тут происходит системный вызов выхода из приложения. Если его не увидит
; linux, то он решит, что программа завершилась аварийно
mov rax, 60
mov rdi, 0 ; код ошибки. если вернется 0 - считается, что ошибок не произошло
syscall

View File

@ -1,17 +0,0 @@
all: test testee
test: test.o
gcc $^ -o $@ -g
test.o: test.c
gcc -c $^ -g
testee: testee.o
ld $^ -o $@
testee.o: testee.asm
nasm -felf64 $^ -o $@ -g
clean:
rm *.o test *.gch testee

View File

@ -2,13 +2,3 @@
## Отладчик
Всю основу написания отладчиков на Linux держит на своих плечах один единственный систенмый вызов - ptrace ("process trace"). Он позволяет выполнять все то, что мы привыкли делать в дебаггере - шаг с обходом, шаг с заходом, просмотр регистров, мучения оперативы и прочие радости отладки. Как тут помочь не знаю, потому что подробный гайд займет целую статью. Предложение простое - почитать великую статью по написанию (первые 2 части покроют весь материал этой лабы): https://blog.tartanllama.xyz/writing-a-linux-debugger-setup/
В основном пробелмы будут сопряжены только с чтением того, что вообще может делать ptrace, а может он почти все, что нас инетерсует, тольк вот интерфейс у него мягко скажем не по solid.
Внутри своего кода я ввел специальный макрос `DONT_CARE`, который призван сигнализировать о том, что при определенном флаге некоторые поля будут игнорироваться.
В остальном удачи и учтите, что программа останавливается как-то по своей логике, а не по логике обычного человека. Она остановится не в момент ptrace (PTRACE_TRACEME, ...), а в момент execv. Почему так? да фиг его знает если честно. Скорее всего просто в этот момент программа посылает сигнал о том, что образ потока был переписан. Других идей у меня нет.
Собственно именно поэтому continue прожималось мной 2 раза, а не один, как хотелось бы верить по логике

View File

@ -1,67 +0,0 @@
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ptrace.h>
#include<sys/user.h>
#include<sys/wait.h>
#define DONT_CARE 0
int stats;
struct user_regs_struct regs;
void continue_execution(pid_t pid)
{
ptrace(PTRACE_CONT, pid, DONT_CARE, DONT_CARE);
waitpid(pid, &stats, 0);
return;
}
void print_rax(pid_t pid)
{
ptrace(PTRACE_GETREGS, pid, DONT_CARE, &regs);
printf("rax = %llu\n", regs.rax);
return;
}
void step(pid_t pid)
{
ptrace(PTRACE_SINGLESTEP, pid, DONT_CARE, DONT_CARE);
waitpid(pid, &stats, 0);
return;
}
int main()
{
printf("procces is run ");
pid_t pid;
pid = fork();
if (pid==0)
{
printf(" -- child\n");
ptrace(PTRACE_TRACEME, 0, 0, 0);
execl("./testee", "testee", NULL);
printf("____AFTER_TRACE_CHILD____\n");
}
else if(pid > 0)
{
char buff;
printf(" -- parrent\n");
continue_execution(pid); // to start app
continue_execution(pid);
if (stats & SIGTRAP)
{
printf("--BREAKPOINT--\n");
while (stats != 0)
{
read(0, &buff, 1);
print_rax(pid);
step(pid);
}
}
printf("____PROCESS_WAS_TERMINATED____\n");
}
return 0;
}

View File

@ -1,26 +0,0 @@
global _start
section .data
msg: db "I'm alive", `\n`, 0
msg_len equ $-msg
section .text
_start:
xor rax, rax,
add rax, 1
;int3
add rax, 12
;int3
mov rax, 33
;int3
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, msg_len
syscall
int3
mov rax, 60
mov rdi, 0
syscall