Введение в системное программирование

Введение

Системное программирование — создание системных программ под различные операционные системы ( ОС ), либо написание самих ОС. Например, системным программным обеспечением ( ПО ) под Linux является: демоны, различные скрипты для настройки системы, вредоносные программы, будь то руткиты, или бэкдоры. Под Windows: службы, драйвера, в общем, все, что взаимодействует плотно с системой. Говоря в общем случае: системное ПО — это такое ПО, которое непосредственно использует возможности операционной системы. В данном блоге мы будем изучать системное программирование под Windows.

Как устроена Windows?

Буду краток. В папке Windows находятся служебные файлы, в папке System32 системные утилиты и библиотеки. Что такое системная библиотека, например: kernel32.dll,  user32.dll, advapi32? Данная библиотека содержит в себе различные функции, которые могут быть вызваны любой программой. Например, функция ExitProcess из kernel32.dll служит для завершения собственного процесса, функция MessageBox из user32.dll для отображения какого-либо сообщения на экране. С этими функциями мы будем работать вплотную, функции из таких библиотек называются Windows API, или сокращенно WinApi. API — это программный интерфейс, его можно встретить где угодно, будь то API скайпа, API вконтакте, или API какого-либо интернет-сервиса. Windows тоже имеет свой API, о котором мы уже сказали.

Что же происходит при вызове APi функций?

Давайте посмотрим: Допустим, мы вызываем ExitProcess. Эта функция является всего-лишь оболочкой, для более низкоуровневых функций. В нашем случае, вызывая ExitProcess уже сама функция вызывает RtlExitUserProcess из ntdll.dll, а та, в свою очередь, ZwTerminateProcess, которая уже напрямую ( либо через FastSysCall, либо sysenter, об этом позже ) вызывает системное прерывание, недоступное из третьего кольца ( об этом потом ).  В общем случае, «путь» вызовов выглядит так: ExitProcess -> RtlExitUserProcess -> ZwTerminateProcess Вы спросите: а почему сразу не вызвать? Можно, мы можем вызвать хоть ZwTerminateProcess, вопрос лишь в удобстве. Функции ExitProcess мы передаем лишь один параметр: код завершения, а ZwTerminateProcess требует даже хэндл процесса ( о хэндлах потом ).

Что такое Zw/Nt функции?

Их называют Native функции ( NTAPI ). В User-mode ( третье кольцо, о них читайте дальше ) Zw и Nt функции одинаковы, так что различие между ними в 0-м кольце не буду вводить, так как мы его не будем трогать. Причины опять же смотрите позже :) Эти функции находятся в ntdll.dll, они плохо документированы, и Microsoft в любой момент может изменить их, даже не сообщив. Так что от использования лучше воздержаться, если только вы пишите не вредоносное ПО. Почему вредоносам лучше это использовать? Причина проста: многие мониторы ставят хуки ( перехват API функций ) на WinApi, и вызовы Nt/Zw функций они могут просто пропустить. Вредоносными программами мы тут заниматься не будем, потому что, во-первых — это не законно. Во-вторых, из полученных знаний вы сами сможете их написать. Я, конечно же, не несу ответственности за ваши действия, так как полицейский может пойти убивать, и кто понесет ответственность? Только он. Ладно, что-то отошел от темы.

Кольца защиты

Всего существует 4 кольца защиты: 0-е, 1-е, 2-е, 3-е. Мы с вами тут рассмотрим только два: 0-е и 3-е. Итак, в 3-е кольце защиты, его другие названия: пользовательское пространство, пользовательский режим ( user-mode ). Ну и сленговое: юзермодное приложение, как пример, приложение которое работает в 3-м кольце защиты. В 3-м кольце защиты работают все пользовательские приложения, и системные: браузер, игры, аська там, проводник винды, svchost и так далее. Они имеют ограниченные права: не могут выполнять привилегированные команды процессора, не могут изменять системные структуры, такие как SSDT таблица. 0-е кольцо — кольцо власти, как я его называю, там можно все: от изменения ядра ОС, до повышения прав калькулятора до системных. На этом кольце работают драйвера. Их написание мы не будем рассматривать, так как оно довольно хорошо описано самим майкрософтом и с Win Vista требуется цифровая подпись для драйвера, чтобы его загрузить. Хотя буткит Rovnix каким-то образом ( подозреваю, что на этапе загрузки :)) без цифровой подписи грузил драйвера, можете погуглить.

Что происходит при запуске исполняемых файлов?

Только в общих словах: загрузчик ( не тот, который грузит ОС, а тот, который грузит PE файлы в память ), проверяет, является ли файл действительно PE файлом ( иногда можем увидеть сообщение, что приложение не является исполняемым Win32, это тогда, когда проверка прошла неудачно ). Далее он заполняет таблицу импорта ( IAT таблица ), выделяет память и грузит туда файл по определенному адресу, и создает поток на точке входа ( EntryPoint ), исключение, если есть TLS-callback`и, которые вызываются до точки входа. Подробнее можете почитать в статьях на васме: http://wasm.ru/wault/article/show/green2red02 ( не реклама ). Статья может показаться сложной, ничего страшного — тема не простая.

На кого рассчитан данный блог?

В первую очередь, на людей, знакомым с программированием на каких-либо компилируемых языках и желающих познакомиться с миром Windows, научившись использовать ее возможности по полной. На каких языках следует программировать? На тех языках, которые поддерживают вызов WinApi. Могу привести примеры, которые прекрасно подойдут: C/C++, Assembler, Pascal ( Delphi ), C#, VB Сам я знаю C++, Assembler и Pascal (Delphi) поэтому в книге преимущественно будут примеры на них.

Есть различия в WinApi на разных языках?

Ответ — НЕТ. Как уже было сказано: WinApi — это функции, которые может вызывать любая программа. Системные библиотеки то не изменятся, от того, на каком языке написана программа? К тому же, все программы, на любом компилируемом языке, превращаются в машинный код. За исключением .NET среды, там свой машинный код: p-code.  Но машинный код — он есть машинный.

Примеры создания сообщения на разных языках

Чтобы вы поняли, что различий существенных нет: только в синтаксисе языка. Я приведу простейший пример Hello, World’а. В следующих статьях научу использовать и понимать другие функции, а так же, научу пользоваться вас MSDN’м. Создание сообщения и выход из процесса в среде Delphi 7. Открываем Delphi 7. Потом: File -> New -> Other -> Console Application. Создается каркас

Удаляем {$APPTYPE CONSOLE} и добавляем в модули Windows. {$APPTYPE CONSOLE} нужен для указания того, что в SUBSYSTEM будет указан CONSOLE и тогда загрузчик создаст еще консольное окно, но нам оно не нужно. Вот приложение, создающее окно и выходящее из процесса:


program Project2;

uses
SysUtils,
Windows;

// в Windows - описание нужных нам API, но не всех.

begin
MessageBox(0, 'Hello, World!', 'Hi', MB_OK); // 0 - владелец, т.е мы. Hello, World - //сообщение, Hi - заголовок, MB_OK - тип сообщения

ExitProcess(0); // выходим из процесса
end.

C/C++


#include <windows.h>

int main()

{

MessageBox(0, "Hello, World!", "Hi", MB_OK);

ExitProcess(0);

}

Flat Assembler:


include 'win32ax.inc'

.data

mes db 'Hello, World',0

title db 'Hi' ,0

.code

start:

push 0

push title

push mes

push 0

call [MessageBoxA]

push 0

call [ExitProcess]

.end start

Как видите, разницы — почти никакой. Надеюсь вам понравилось, оставайтесь с нами!

Оставьте комментарий