Welcome to ROOTPWN!

Пишем калькулятор на С...другим путем

Регистрация
10 Дек 2018
Сообщения
5
Симпатии
5
#1
Итак, сегодня мы будем писать калькулятор на С. Для этого нам понадобится понять, что такое системный вызов и зачем он нужен.

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

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

Используются в Linux только два уровня привилегий - kernelmode (ring0) и usermode (ring3). Но что же делать, если нашей программе (ring3) требуется выполнить какую-то привилегированную операцию (ring0)? Тут нам на помощь приходят прерывания.


Что же такое прерывание? Прерывание - это событие, генерируемое аппаратно или программно.

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

Программные прерывания же вызываются соответственно при помощи инструкции INT (в интеловской архитектуре х86, которую мы сейчас рассматриваем).

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

В общем, реализовано это с помощью сегментной защиты памяти (https://ru.m.wikipedia.org/wiki/Сегментная_защита_памяти), в которую мы пока что углубляться не будем, а то до калькуляторов вообще никогда не дойдем.

Итак, как же реализовать это на практике и как это будет выглядеть в инструкциях процессору?

Выглядит (легаси-версия) это все следующим образом.

допустим, мы хотим выполнить программу (в нашем случае калькулятор). Воспользуемся сисколлом execve() из unistd.h:


#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

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

execve("bc",NULL,NULL);

}


Вот и весь наш калькулятор, лол)

Разберемся, что это за фигня была.

Мы только что через прослойку glibc, а не напрямую (почему сисколлы лучше не вызывать напрямую, в отдельной статье) вызвали исполнение команды bc (консольный калькулятор в никсах).

Что при этом случилось?

Вызов execve (исполнение) принимает 3 аргумента - filename, argv, envp. Для вызова нам надо указать все 3, но в данной ситуации последние 2 нам не нужны, поэтому мы заменили их на NULL. Все довольно просто.

Теперь, как это выглядит с точки зрения процессора.

в eax у нас кладется значение filename (в нашем случае bc)

в ebx кладется argv (NULL)

и в ecx кладется envp (NULL)

после чего вызывается, так как у нас легаси-версия, прерывание 0х80 (эквивалент sysenter).

Уф, чет я задолбался, объяснил максимально коряво, но буду дополнять, , если понравится, писать новые статьи
 
Регистрация
10 Дек 2018
Сообщения
5
Симпатии
5
#2
описал максимально коряво, сорян(
но, в общем, это все для того, чтобы вы хоть базово понимали, что происходит в системе, под которой вы работаете. Могу продолжить эту рубрику, если хотите.
 
Регистрация
10 Дек 2018
Сообщения
5
Симпатии
5
#7
а хотя, замутим с этого еще один уникальный тутор по вопросам эксплуатации.
это не работает, потому что не в рабочей директории нет самого калькулятора bc. дописываем /usr/bin/ к bc и все работает.
но это крайне нездоровая херня, и вам задача нагуглить, почему
 
Сверху Снизу