Czy warto rzucić okiem na tę mieszaninę bajtów by poznać zależności w niej występujące? Oczywiście, że tak! Zwłaszcza że wiedza ta może obficie zaowocować w praktyce. Informacje o budowie programów są niezbędnym składnikiem w wielu ważnych dziedzinach życia komputerowego. Z namacalnych przykładów można wymienić chociażby programy do zabezpieczania oprogramowania (tzw. protectory), programy kompresujące pliki wykonywalne (tzw. packery), programy antywirusowe i wiele innych. Poza tym, wiedza ta jest niezbędna do stosowania technik reverse engineeringu, a tym właśnie zajmiemy się w dalszej części artykułu.
Jak Windows uruchamia programy?
Windows tworzy dla każdej uruchamianej aplikacji osobną przestrzeń adresową. Następnie odczytuje z uruchamianego pliku wartość ImageBase i wgrywa pod ten adres uruchamiany program. Wszystkie sekcje programu, które mają być załadowane do pamięci, umieszczane są pod swoimi adresami wirtualnymi (tzw. VA). Wartości te przechowywane są w pliku w tabeli sekcji. Tabela ta zwiera także wielkość danej sekcji w pamięci. Windows przycina sekcję do tej wielkości lub wypełnia zerami odpowiednią ilość miejsca za sekcją, tak aby zwiększyć jej rozmiar (tzw. padding). W ten sposób utworzony został obraz programu w pamięci.
Uruchomienie aplikacji następuje w momencie wykonania jej pierwszej instrukcji. System operacyjny lokalizuje jej adres poprzez odczytanie z pliku wartości Entry Point (EP) i dodanie jej do ImageBase programu.
Po zakończeniu działania aplikacji, Windows zwalnia pamięć zajmowaną przez program.
Konwersja RVA na VA
Większość adresów, w nagłówkach plików PE, przechowywana jest w formie względnych adresów wirtualnych (RVA). Zamiana ich na adresy wirtualne (VA) jest bardzo prosta i ogranicza się do zsumowania RVA i ImageBase (VA = RVA + ImageBase). ImageBase jest bazowym adresem aplikacji – adresem pod który został wgrany program.
Format pliku wykonywalnego Windows
Programy w systemie Windows są przeważnie plikami PE. PE – skrót od Portable Executable – jest formatem wprowadzonym przez firmę Microsoft. Posiada on dosyć przejrzystą budowę. Na początku pliku znajduje się nagłówek MZ i DOS stub następnie nagłówki PE i OPT, Data Directory, tabela sekcji oraz same sekcje i jeszcze parę innych struktur. Przyjrzyjmy się im z bliska.
Dodam tylko, że będę w tym artykule operował wielkościami WORD i DWORD. WORD oznacza 2 bajty, natomiast DWORD – 4 bajty.
Nie będę także przytaczał wszystkich pól prezentowanych struktur, ograniczę się jedynie do istotnych z naszego punktu widzenia. Pominięte pola oznaczę trzema kropkami („…”).
Nagłówek MZ i DOS stub
Tabela 1. Przegląd struktury nagłówka MZ
Offset względem początku pliku (hex) Wielkość Opis
0 WORD Sygnatura MZ
… … …
3C DWORD Offset nagłówka PE
Nagłówek MZ jest pozostawiony w celach kompatybilności z systemem MS-DOS. Zauważ, że przy próbie uruchomienia programu dla Windows w systemie MS-DOS, na ekranie pojawia się tekst: This program must be run under Win32. Za wypisanie tego komunikatu i zakończenie działania programu odpowiedzialny jest DOS stub. DOS stub jest małym programikiem znajdującym się w każdym pliku PE (o ile nie został zmieniony lub usunięty) od razu za nagłówkiem MZ. Jego kod jest przedstawiony na Listingu 1.
Sam nagłówek MZ ma 40h bajtów, jednak tylko 2 pierwsze i 4 ostatnie bajty zawierają interesujące informacje, reszta pól jest przestarzała lub zarezerwowana.
Sygnatura MZ zawsze zawiera bajty 4Dh, 5Ah (czyli litery „M” i „Z” w kodzie ASCII, od jednego z twórców tego nagłówka – Marka Zbikowsky’ego).
Offset nagłówka PE jest to zwykłe przesunięcie względem początku pliku. Jest to bardzo ważne pole, dzięki niemu system operacyjny uruchamiając program, wie gdzie zaczyna się nagłówek PE.
<