Jak funguje debugger. Jak funguje ladicí program jádra operačního systému Co je ladění jádra

Tato série článků se objevila ze dvou důvodů. V první řadě mě baví práce s projektem HackSysExtremeVulnerableDriver. Za druhé jsem obdržel hodně přání k pokrytí tohoto tématu.

Veškerý kód použitý při psaní této série je v mém úložišti.

V této sérii článků se podíváme na psaní exploitů na úrovni jádra ve Windows. Je důležité poznamenat, že se budeme zabývat známými zranitelnostmi a není potřeba zpětné inženýrství (alespoň u ovladače).

Očekává se, že po přečtení všech článků budete znát všechny nejběžnější třídy zranitelností a způsoby zneužití a také budete schopni přenést exploity z architektury x86 na architekturu x64 (pokud je to možné) a seznámíte se s novými metodami ochrany. ve Windows 10.

Obvod ladění jádra

Na rozdíl od ladění na úrovni uživatele, které pozastavuje provádění jednoho procesu, ladění na úrovni jádra zahrnuje celý systém a tuto metodu nebudeme moci použít. V souladu s tím potřebujeme samostatný ladicí stroj, který dokáže komunikovat se systémem, kde se jádro ladí, prohlížet paměť a struktury jádra a také zachycovat pády systému.

Další materiál ke studiu:

Zneužití zranitelností jádra

Tento proces je mnohem zábavnější než provoz na uživatelské úrovni J.

Hlavním cílem je dosáhnout privilegovaného provádění v kontextu jádra. A pak už vše závisí na naší fantazii, od hostiny s domácím pivem až po zavedení státem podporovaného malwaru.
Obecně je naším úkolem získat shell se systémovými oprávněními.

Témata článků v této sérii

  • Část 1: Nastavení pracovního prostředí
    • Konfigurace tří virtuálních strojů a systému, který bude fungovat jako debugger.
    • Konfigurace ladicího programu WinDBG.
  • Část 2: Užitečné zatížení
    • Prozkoumejte nejběžnější užitečné zatížení. Následující části se budou zabývat konkrétními zranitelnostmi a tam, kde je to vhodné, poskytnou odkazy na tento článek.
  • Zbývající části.
    • Zvažování zranitelností.

Životní cyklus vývoje využívající jádro

  • Nalezení zranitelnosti. Toto téma nebude v této sérii probíráno, protože už přesně víme, kde jsou mezery.
  • Zachycení vlákna provádění. Některé chyby zabezpečení zahrnují spuštění kódu a některé mají další požadavky.
  • Eskalace privilegií. Hlavním cílem je získat shell se systémovými oprávněními.
  • Obnovení vlákna provádění. Nekontrolované výjimky na úrovni jádra způsobují pád systému. Pokud se nechystáte napsat exploit pro DoS útok, měli byste tuto skutečnost vzít v úvahu.

Typy cílových systémů

Budeme pracovat se zranitelnostmi v následujících systémech (konkrétní verze není důležitá):

  • Win7 x86 VM
  • Win7 x64 VM
  • Win10 x64 VM

Začněme architekturou x86 a poté exploit přeneseme na systém Win7 x64. Některé exploity nepoběží na počítačích s Win10 kvůli přítomnosti nových ochran. V tomto případě buď změníme logiku exploitu, nebo použijeme úplně jiný přístup.

Použitý software:

  • Hypervisor (spousta možností).
  • Windows 7 x86 VM
  • Windows 7 x64 VM
  • Windows 10 x64 VM

Nastavení systémů pro ladění

Ladicí systémy, se kterými budeme komunikovat, jsou navrženy tak, aby načetly zranitelný ovladač. Tyto stroje budou často padat, protože k tomuto typu jevu přispívá většina výjimek v jádře. Pro tyto systémy musíte přidělit dostatek paměti RAM.

Na každém počítači, který bude laděn, musíte provést následující:

  • V adresáři VirtualKD spusťte soubor target\vminstall.exe. Bude přidána nová spouštěcí položka a budou k dispozici ladicí funkce a v systému nainstalováno automatické připojení k serveru VirtualKD, který funguje jako debugger.

V případě Windows 10 VM je potřeba povolit testovací režim podepisování, který umožňuje načíst nepodepsané ovladače do jádra.

Po spuštění bcdedit /set testinging on command a restartu se na ploše objeví „Test Mode“.

Stručný popis modulu HEVD

Procedura DriverEntry je startovací procedura pro každého řidiče:

NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) (
UINT32 i = 0;
PDEVICE_OBJECT DeviceObject = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
UNICODE_STRING Název zařízení, DosDeviceName = (0);

UNREFERENCED_PARAMETER(Cesta registru);
KÓD_STRÁNKY();

RtlInitUnicodeString(&Název zařízení, L"\\Zařízení\\HackSysExtremeVulnerableDriver");
RtlInitUnicodeString(&DosDeviceName, L"\\DosDevices\\HackSysExtremeVulnerableDriver");

//Vytvořte zařízení
Stav = IoCreateDevice(DriverObject,
0,
&Název zařízení,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
NEPRAVDIVÉ
&DeviceObject);

  • Tato procedura obsahuje volání funkce IoCreateDevice, která obsahuje jméno ovladače, který budeme při komunikaci používat.
  • Do objektu DriverObject budou přidány potřebné struktury a ukazatele funkcí.
  • Pro nás je důležitý funkční ukazatel spojený s procedurou DriverObject->MajorFunction, který je zodpovědný za zpracování IOCTL (I/O Control);
  • V HEVD se tato funkce nazývá IrpDeviceIoCtlHandler, což je velký podmíněný výraz s mnoha větvemi pro každý IOCTL. Každá chyba zabezpečení má jedinečný IOCTL.

Příklad: HACKSYS_EVD_IOCTL_STACK_OVERFLOW je IOCTL používaný ke spuštění zneužití přetečení zásobníku.

Tím končí první část. V příštím článku si povíme o užitečné zátěži. V tuto chvíli je jediným dostupným nákladem ten určený ke krádeži tokenů, který bude použit ve třetím díle.

P.S. Chápu, že existuje spousta složitostí a problémů, se kterými se můžete setkat. Protože se tato série zaměřuje na vývoj exploitů, budete muset vyřešit všechny související problémy sami. V komentářích se však můžete zeptat na jakékoli otázky.

Slušných debuggerů na úrovni jádra pro Windows je málo, ale v Linuxu se dají spočítat na prstech jedné ruky a i ty jsou většinou hrubé, nedokončené nebo opuštěné a zarostlé mechem... Dnes si povíme o těch nejoblíbenějších a nejzajímavější z nich -
Linice.

Úvod

Jak už z názvu tušíte, Linice je neoficiálním „přístavem“ legendárních
SoftICE pro Linux, který si zachoval rozhraní, příkazový systém a většinu jeho schopností: vyskakovací okno pomocí horké klávesy (v Linice Tento ); nastavení hardwarových bodů přerušení u všech funkcí a systémových volání; prohlížení stránek GDT/LDT/IDT, fyzické paměti; funkce převzaté z GDB (volání libovolné funkce pomocí příkazu CALL, uložení/obnovení kontextu registru, interní proměnné atd.).

Na rozdíl od většiny ostatních debuggerů, které pracují prostřednictvím mechanismu ptrace, který je nereentrantní a snadno detekovatelný ochranami (jehož analogem Windows je DEBUG_PROCESS, používaný ladicími programy aplikací), Linice používá nativní trasování, stejné jako v SoftICE, které umožňuje oběma debuggerům ladit vysoce bezpečné programy, se kterými si ostatní neporadí.

Ve skutečnosti se vůbec nejedná o port (proto ty uvozovky), ale o nezávislý projekt, napsaný od začátku a distribuovaný ve zdrojovém kódu zdarma (od SoftICE je pouze inspirace). Hlavní část kódu určeného pro jádro 2.4 napsal německý hacker Goran Devic, ale na údržbě jádra 2.6 se podíleli úplně jiní lidé: Daniel Reznick, Peter K. a Carlos Manuel Duclos Vergara. A náš krajan - Oleg Khudakov - přepsal soubory assembleru z nasm na
gcc.

Zdrojové texty jsou na oficiálních stránkách projektu -
Řádek%0A.com">www.Linice.com, je zde také dokumentace, krátké FAQ a odkaz na fórum
Řádek%0A">groups.google.com/group/Linice. Neexistují žádná hotová binární sestavení.
Tvůrci projektu si otevřeli svůj vlastní účet na SourceForge, ale byli příliš líní na to, aby na něj nahrávali jakékoli soubory, a představovali pouze 3 screenshoty velmi nízké kvality:
.

Požadavky na systém

Nejnovější verze Linice má číslo 2.6 a pochází z 28. července 2005 a plně podporuje jádro 2.4.x a režim konzoly VGA. S novějšími jádry jsou vážné problémy a jádro 2.6.x má pouze omezenou podporu.
Ladicí program byl vyvinut a testován pod Debianem 2.6. Není zaručena její kompatibilita s jinými distribucemi, což nás nutí uchýlit se k tamburíně, ale v některých případech ani tamburína nepomáhá. Vlastně si ponechte Debian na svém počítači, abyste s ním mohli pracovat Linice,- to je docela normální. Kdysi dávno, když ještě neexistovala implementace SoftICE pro NT, mnoho hackerů instalovalo Win 9x jen proto, aby rozbíjelo programy, ačkoli sami seděli pod
N.T. Protože pokrýt všechny složitosti instalace Linice v rámci jednoho článku je to prakticky nemožné, omezím se na popis procesu kompilace a spuštění Linice pod jednou konkrétní distribucí - Knoppix 3.7 s jádrem 2.4.1 v konzolovém VGA režimu.
Linice podporuje ACPI a víceprocesorové stroje, ale nepracuje dobře s X, zejména na grafických kartách jiných než nVidia. Vůbec nevnímá 24bitovou barevnou hloubku, „tráví“ pouze 8, 16 a 32 bitů, takže je pohodlnější k ladění X aplikací prostřednictvím vzdáleného terminálu připojeného přes COM port pomocí protokolu VT100. V tomto případě bude místní klávesnice fungovat také s
Linice!

Kompilace a konfigurace Linice

Stáhněte si gzip archiv zdrojových textů www. Linice.devic.us/ Linice-2.6.tar.gz, který zabírá o něco méně než megabajt, rozbalte jej na disk, přejděte do adresáře ./docs a ze souboru readme zjistíme, že debugger je postaven pro jádro 2.4 následovně:

# sestavení cd
# ./make_bin-2.4
# cd ../bin
# vyčistit; mak E

Před spuštěním make však musíte otevřít soubor ./bin-2.4/Makefile a upravit řádek "TARGET" podle konfigurace a architektury cílové platformy. Konkrétně na počítačích ACPI s vícejádrovými procesory nebo procesory HyperThreading to bude vypadat takto:

TARGET = -DSMP -DIO_APIC

Po dokončení kompilace bude v adresáři ./bin mnoho souborů a adresářů, ale jediné významné jsou:

linsym – načítací modul debuggeru;
linince.dat – konfigurační soubor;
xice – podpora X“, při práci v textovém režimu lze smazat;
./Linice_2.4.27/Linice.o – zaváděcí modul jádra obsahující samotný debugger.

Proces sestavení Linice

Po sestavení minimálně fungující sady by bylo hezké získat vše ostatní - ukázkové příklady ladění umístěné v adresáři ./test a zkompilované pomocí kompilačního skriptu a také rozšiřující modul (podle našeho názoru plugin) umístěný v ./ext, zkompilovaný příkazem make a načtený příkazem insmod. Není z toho žádný užitek, ale po prostudování zdrojového kódu si můžeme napsat vlastní moduly rozšiřující funkcionalitu
Linice.

Při načítání Knoppix se na spodním řádku obrazovky objeví výzva "boot:", kde musíte zadat "knoppix 2 vga=normal". Cheat kód "knoppix" vybere jádro 2.4 (automaticky načteno ve výchozím nastavení, takže "knoppix" lze vynechat), „2“ blokuje načítání X“ a „vga=normal“ nastavuje standardní režim vga s rozlišením 80x25.

Po čekání na dokončení stahování řekneme „su“, poté „passwd“ a zadáme nové heslo pro root „a, pod kterým se ihned přihlásíme do systému pomocí příkazu login. Pokud tak neučiníme, pokus o Start Linice skončí zdrcujícím selháním s výkřikem „chyba segmentace“.

Když spustíte Knoppix z pevného disku (na který jej můžete nainstalovat příkazem „sudo knoppix-installer“ napsaným v okně terminálu z relace LiveCD), zobrazí se nabídka Start se seznamem dostupných jader. Vyberte Linux (2.4)-1 a klikněte pro nastavení spouštěcích parametrů - „2 vga=normal“. Není třeba psát slovo „knoppix“, protože jádro je již vybráno. Po dokončení stahování zadejte příkaz login a přihlaste se jako root (za předpokladu, že účet byl vytvořen dříve).

Ladicí program se spustí příkazem ./linsym –i, po kterém se okamžitě objeví na obrazovce. Pokud se tak nestane, zkuste zadat přepínač "--verbose 3" pro zobrazení diagnostických zpráv.
Jedním z důvodů selhání bootování může být absence souboru /boot/System.map obsahujícího adresy funkcí jádra. Stahování se také nezdaří, pokud obsah System.map neodpovídá aktuálnímu jádru, což se může stát například při jeho rekompilaci. Některé kompilátory distribuce buď System.map vůbec nezahrnují (věří, že to posílí bezpečnost systému, protože pro rootkity bude obtížnější zachytit systémová volání), nebo sem umístí něco úplně špatně a obecně není jasné, kam to přišlo z. V takových případech postačí jednoduše překompilovat jádro a nasměrovat debugger na cestu k souboru System.map pomocí přepínače "-m", pokud se nachází jinde než v /boot. Tímto způsobem nebude ohrožena bezpečnost a Linice může fungovat!
Ladicí program se vrátí do systému nebo pomocí příkazu "x " Kombinace volá debugger z libovolného programu. Není ale vůbec pravda, že se ocitneme v jeho kontextu, protože Linux je multitaskingový systém, který přepíná procesy jeden za druhým a příkazy ADDR (context switching) jsou v „lexikonu“ Linice stále neexistuje a kdy se objeví, není známo. Proto musíte být mazaní a nastavovat body přerušení na systémových voláních používaných konkrétním programem nebo se do procesu vloupat pomocí metody INT 03h, o které si nyní povíme.

Přepínač „-x“, předaný stejnému linsymu, je zodpovědný za uvolnění ladicího programu (pokud jej opravdu chcete uvolnit).

Základy práce s Linice

Pro ty, kteří již pracovali se SoftICE, mastering Linice nebude činit žádný problém. Jsou zde použity stejné příkazy: D – výpis paměti, E – editace paměti, T – trasování krok za krokem, P – trasování bez zadávání funkcí, R – prohlížení/úprava registrů, BPM/BPX – nastavení bodu přerušení pro přístup do paměti /provedení atd. Úplný seznam příkazů je obsažen jak ve vestavěné nápovědě volané pomocí HELP (mimochodem „HELP command_name“ poskytuje další informace o příkazu), tak ve standardní dokumentaci.

Pojďme kliknout a prohlédněte si seznam procesů zobrazený příkazem PROC, přičemž aktuální proces je zvýrazněn modře:

:PROC

1 0000 C1C3E000 SPÁNEK 0 0 init
2 0000 F7EE8000 SPÍNÁNÍ 0 0 keventd
3 0000 F7EE2000 SLEEPING 0 0 ksoftirqd_CPU0
4 0000 F7EE0000 SLEEPING 0 0 ksoftirqd_CPU1
5 0000 F7ED0000 SPÍNÁNÍ 0 0 kswapd
6 0000 F7EAA000 SPÁNEK 0 0 bdsplach
7 0000 F7EA8000 SLEEPING 0 0 kupd
56 0000 F6A36000 SPÁNEK 0 0 kjournald
1006 0000 F7A34000 RUNNING 0 0 automount
1013 0000 F68E6000 SPÍNÁNÍ 0 0 cupsd
...
1105 0000 F6DDE000 SPÍNÁNÍ 0 0 mc
1106 0000 F6DD4000 SPÁNEK 0 0 spoři.

Procesy jsou samozřejmě dobré, ale jak stále ladíme programy? Nejjednodušší je vložit do vstupního bodu strojovou instrukci CCh odpovídající instrukci INT 03h, přičemž nejprve zapíšeme obsah původního bytu. To lze provést pomocí libovolného hex editoru, například toho, který jsem několikrát zmínil
HTE.

Po načtení souboru do editoru klikněte (režim), vyberte elf/obraz, přesuňte kurzor na „entrypoint:“, stiskněte (edit) a změňte první bajt na CCh, uložte změny do (uložit) a odejít. Při spuštění opraveného programu Linice okamžitě vyskočí, narušen výjimkou generovanou CCh, po které EIP indikuje konec
CCh.

Stav programu s opraveným vstupním bodem v okamžiku, kdy se objeví debugger

0023:080482C0 CC int 3
0023:080482C1 ED v eax, dx
0023:080482C2 5E pop esi
0023:080482C3 89E1 mov ecx, esp

Kurzor ukazuje na instrukci in eax,dx (EDh), která je fragmentem opravené instrukce xor ebp,ebp (31h EDh). Nyní (teoreticky) bychom měli obnovit původní bajt změnou CCh na 31h, zmenšit registr EIP o jeden a pokračovat ve sledování jako obvykle.

Ale nebylo tomu tak! Linice- toto je samozřejmě port, ale jen velmi hrubý a nedokáže upravit paměť obrázku stránky, i když nejprve otevřete segment kódu pro zápis. Nefunguje ani E (edit), ani F (fill), ani M (paměťová kopie)! Ale zápis do zásobníku funguje a to nám hackerům docela stačí.

Pamatujeme si aktuální hodnotu registru EIP; zkopírujte příkaz patched machine do horní části zásobníku; obnovit tam bajt CCh; přeneseme na něj řízení a změníme hodnotu EIP; provádíme to provedením jediného úkonu trasování; a vraťte EIP na své místo, to znamená na následující strojový příkaz:

Obnovení původního bajtu nahrazeného instrukcí INT 03h

; Podívejme se, co je na vrcholu zásobníku (z čisté zvědavosti).
:d esp-10
0018:BFFFEFC0 C0 82 04 08 00 00 00 00 5D 0C 00 40 DC EF FF BF

; Zkopírujeme příkaz patched machine na vrchol zásobníku.
; Číslo 10h je maximální možná velikost strojové instrukce na x86.
:m eip-1 L 10 esp-10

; Podívejme se, jak se zásobník změnil.
:d esp-10
0018:BFFFEFC0 CC ED 5E 89 E1 83 E4 F0 50 54 52 68 F0 85 04 08

; To jo! Zásobník se opravdu změnil, je čas opravit CCh na 31h.
:e esp-10 31
Upravit okamžitá data dosud nebyla implementována.

; Jejda! Přímé přiřazení dat není v Linici implementováno,
; ale můžeme výpis upravit interaktivně (také
; jako v SoftICE) nebo dejte příkaz F esp-10 L 1 31, jen mějte na paměti, že
; že na rozdíl od SoftICE ladicí program Linice neaktualizuje okno výpisu,
; takže po provedení příkazu F se může zdát, že
; žádný výsledek; ve skutečnosti tomu tak není, stačí aktualizovat
; vypiš příkazem D esp-10 a vše zapadne na své místo.

; Přeneseme řízení na příkaz zkopírovaný do zásobníku,
; zapamatovat si hodnotu registru EIP.
:r eip (esp-10)
reg:eip=BFFFEFC0

; Provádíme jeden úkon sledování.
:t
0023:BFFFEFC2 5E pop esi

; Jak vidíme, registr EIP se zvýšil o 2 (BFFFEFC2h - BFFFEFC0h) = 02h,
; proto je adresa dalšího příkazu: 080482C1h - 01h + 02h = 080482C2h,
; kde 080482C1h je počáteční hodnota EIP při vstupu do programu a 01h je velikost INT 03h.

; Nastavíme EIP na příkaz podle opravené instrukce.
:r eip 80482C2
reg:eip=80482C2

To jsou druhy tanců s tamburínou, které musíte dělat. a co dělat? Takže jsme vyřešili načítání programů do debuggeru; nyní rozdělíme body přerušení na systémová volání a funkce jádra.

Příkaz exp vypíše názvy exportované jádrem, z nichž kterékoli se může objevit ve výrazech, například "bpx do_bkr" je ekvivalentní "bpx C012C9E8".

Výpis názvů exportovaných jádrem

:exp
jádro
C0320364 mmu_cr4_features
C02AC3A4 acpi_disabled
C02AC8A0 i8253_lock
...
C012BDA8 do_mmap_pgoff
C012C764 do_munmap
C012C9E8 do_brk
C011E990 exit_mm
C011E69C exit_files

Systémová volání jsou obtížnější. Přímá podpora od Linice není to tady (ale mělo by být, vzhledem ke specifikům Linuxu), takže tato věc musí být provedena ručně.

Tabulka syscall, jak víte, je pole dvojitých slov začínající na adrese sys_call_table (tato proměnná je exportována jádrem).

Tabulka systémových volání

; Ladicí program přepneme do režimu zobrazení dvou slov.
:dd

; Tabulku zobrazíme na obrazovce.
:d sys_call_table
0018:C02AB6A8 C0126ACC F8932650 F89326A0 C013DC10
0018:C02AB6B8 C013DD18 C013D5C8 C013D724 C011F3BC
0018:C02AB6C8 C013D664 C014A8E0 C014A3B4 F893020C

Každý prvek tabulky odpovídá svému vlastnímu systémovému volání a každé volání má své vlastní číslo, které lze najít v souboru /usr/include/sys/syscall.h, ale je lepší to nedělat pod Linuxem , kde nejsou žádná přímá čísla, ale půjčit si jeden stejný soubor z BSD - stejně, čísla hlavních systémových volání na všech systémech jsou stejná. Konkrétně otevřené systémové volání je číslo 5.

Chcete-li nastavit breakpoint na open, musíte zjistit jeho adresu, která se nachází v pátém dvojitém slově tabulky systémových volání, počítá se od nuly a rovná se (v tomto případě) C013D5C8h.

Nastavení bodu přerušení při otevřeném systémovém volání

; Nastavte bod přerušení v otevřeném systémovém volání,
:bpx C013D5C8
; opustit debugger,
:X
...
# otevřít nějaký soubor,
...
; okamžitě se objeví debugger a řekne nám o tom,
:Zlomový bod kvůli BPX 01

; Dáváme příkaz proc, abychom se ujistili, že jsme vklíneni do našeho procesu.
:proс
PID TSS Stav úlohy uid gid název
1049 0000 F6364000 SPÍNÁNÍ 0 0 getty
1145 0000 F61CC000 SPÍNÁNÍ 0 0 mc
1146 0000 F614A000 SPÁNEK 0 0 spoři.

Tímto způsobem je snadné nabourat se do již běžících procesů, nastavit body přerušení u systémových volání, které používají, a také dělat mnoho dalších věcí, které jsou pro hackování životně důležité.

Závěr

Navzdory své zjevné vlhkosti, Linice je docela vhodný pro ladění chráněných aplikací, i když poměrně často se musíte uchýlit k řešením, která se v normálních debuggerech spouštějí automaticky. Proto Linice vůbec nenahrazuje gdb, ale pouze jej doplňuje.

Ladicí program je druhá věc po kompilátoru potřebném k vytváření programů. Mnoho z těch, kdo píší počítačové programy a používají debugger, si však neuvědomují principy a mechanismy jeho fungování.


Je těžké být debuggerem...

Vzhledem k tomu, že programátoři používají debugger ve dne i v noci, zvláště když vstoupí do režimu hlubokého ladění, stojí za to říci, že pokud by debugger nebyl program, ale kus hardwaru, pravděpodobně by se přehříval a rozbil. Protože ani kompilátor nemá tolik práce jako debugger.

Samozřejmě, protože nyní existuje mnoho různých programovacích jazyků, pro každý z nich existují různé debuggery. A samozřejmě pro různé kategorie těchto jazyků existují rozdíly v práci debuggerů: například debugger pro programy v interpretovaném Ruby bude fungovat jinak než pro jazyk Java zkompilovaný do bajtového kódu a debugger pro Java v turn, bude mít rozdíly od ladicího programu Visual C++.

Budu mluvit o ladění pro platformu Windows. Po pochopení principů fungování debuggerů pro něj bude možné porozumět jak debuggerům pro systémy POSIX, tak debuggerům, které nepracují na úrovni operačního systému, ale na úrovni virtuálního stroje nebo nějakého interpreta.


Debuggery pro Windows: dva typy

Existují dva zásadně odlišné typy ladicích programů pro Windows. Myslím, že na první narazil každý při programování v Delphi (neprogramoval v něm? Je těžké uvěřit. V čem jsi programoval ve škole a na střední škole?). Jedná se o vlastní ladicí programy aplikací. Je jich mnoho a existují jak jednotlivě, tak (mimochodem zvláště často) jako součást integrovaných prostředí pro vývoj aplikací. Mezi debuggery distribuovanými jako samostatné softwarové produkty je tradičně vyzdvihován OllyDbg a kdysi jsem o něm psal v Computer News.

Druhým typem debuggeru je ladicí program jádra operačního systému. Nacházejí se a používají méně často a jejich design se výrazně liší od vlastních ladicích programů. Nejznámějším a zároveň nejlepším debuggerem jádra je SoftIce. Možná jste o něm nejen slyšeli, ale dokonce ho používali.

Protože práce každého ze dvou typů debuggerů má svá specifika, budu o každém z nich mluvit podrobněji.


Vlastní aplikace Debugger

Ladicí program pro uživatelské aplikace je jednodušší, protože operační systém přebírá tu nejpodřadnější a nejšpinavější práci. Windows má speciální softwarová rozhraní, která jsou navržena pro ladění aplikací na uživatelské úrovni – nazývají se Windows Debugging API. Jsou to ladicí rozhraní API, která používají všechny ladicí programy, které jsou zabudovány do oblíbených integrovaných vývojových prostředí pro Windows.

Aby mohlo začít ladění, musí debugger spustit laděný proces speciálním způsobem - aby systém věděl, že tento proces bude v ladění. Poté začíná cyklus ladění: program je prováděn, dokud nenastane určitá událost, která se nazývá událost ladění. Tím se spustí smyčka ladění v samostatném vláknu, aby se zabránilo zablokování ladicího programu.

Ale to je jen začátek. Protože zábava s debuggerem začíná, když dojde k události ladění. Ostatně, co je úkolem debuggeru? Pomoci programátorovi lokalizovat chybu s přesností na konkrétní funkci, konkrétní operaci, konkrétní proměnnou. Operační systém může také pomoci debuggeru v tomto obtížném úkolu.

Takže došlo k události ladění a pak musíme nějak zjistit, jak to souvisí s textem programu. To je možné pouze v případě, že samotný program obsahuje speciální ladicí informace – tabulku ladicích symbolů. Obsahuje informace o shodě mezi adresami a názvy funkcí, datové typy a čísla řádků kódu. Právě díky nim je možné ladění, které zná každý programátor Windows. Tabulky symbolů mají různé formáty, a proto není vždy možné ladit program zkompilovaný jedním vývojářským kompilátorem pomocí debuggeru jiného výrobce. Nejběžnější formát však stále lze specifikovat - jedná se o PDB (Program Database), který vyvinula přirozeně společnost Microsoft Corporation.

Pokud je tedy tabulka symbolů ladění ve formátu PDB, můžete použít speciální nástroj od společnosti Microsoft - symbolický ladicí procesor. Kdysi byl součástí jádra systému a jmenoval se Imagehlp.dll, ale už dávno byl oddělen do samostatné knihovny. Znakový procesor umožňuje najít nejbližší otevřenou funkci nebo globální proměnnou na dané adrese a také číslo řádku a název zdrojového souboru, ve kterém se tento řádek nachází. Podporovány jsou i inverzní operace, například hledání adresy funkce podle jejího názvu.

To samozřejmě není všechna práce, kterou ladicí program vlastní aplikace dělá. Například při ladění vícevláknových aplikací vzniká mnoho velmi jemných problémů souvisejících s interakcí vláken. I při ladění tak relativně jednoduchých věcí, jako jsou služby, existují určité nuance.

Ale nebudeme se nyní zabývat nuancemi - na konci článku vám řeknu, kde si o nich můžete přečíst. Nyní se podíváme na ladicí programy jádra.


Debugger jádra

Ladicí programy jádra jsou mnohem složitější programy než debuggery uživatelských aplikací a myslím, že je zcela jasné proč: nemají asistenta operačního systému. V tomto případě je jejich klientkou, protože je to ona, kdo nakonec musí ladit.

Většina ladicích programů jádra vyžaduje ke své činnosti dva počítače spojené kabelem nulového modemu. Nulový modem je způsob, jak propojit dva počítače přímo kabelem přes jejich porty COM nebo LTP. Druhý počítač je potřeba, protože část debuggeru sedící na prvním (na tom, kde je nainstalován laděný systém) má omezený přístup k hardwaru, a proto všechna data jdou přes nulový modem do druhého počítače.

Moderní procesory architektury Intel x86 mají speciální ladicí registry (staré 368 i novější modely procesorů jich mají pouze osm, nazývají se DR0-DR7). Tyto registry umožňují ladicímu programu nastavit body přerušení při čtení a zápisu paměti a také na I/O portech. Obecně vše vypadá přesně takto a myslím, že nemá cenu nyní podrobně popisovat, za co je každý z registrů ladění zodpovědný, co přerušuje implementovat body přerušení a poskytovat další podobné informace. Je lepší vám říci o konkrétních existujících ladicích programech jádra pro Windows.

No, za prvé, je to debugger zabudovaný do samotného jádra operačního systému. Je přítomen ve všech operačních systémech řady NT, počínaje Windows 2000. Je součástí souboru NTOSKRNL.EXE a lze jej aktivovat nastavením možnosti "/Debug" pro operační systém v souboru BOOT.INI. Tento debugger vyžaduje připojení nulovým modemem a druhý počítač se stejným OS.

Existuje další ladicí program jádra od společnosti Microsoft - WinDBG. Přísně vzato se nejedná o ladicí program jádra, ale o hybridní debugger, který lze také použít k ladění aplikací na uživatelské úrovni. Na rozdíl od debuggeru zabudovaného v jádře má grafický shell, a proto se snáze používá. Tento debugger také podporuje speciální rozšíření, která mohou být užitečná pro některé úlohy ladění. Ale také to vyžaduje dva počítače k ​​ladění jádra.

Existuje však ladicí program jádra, který dokáže ladit na jednom počítači. Tohle je SoftIce. SoftIce zároveň umí ladit i aplikační programy. Použití tohoto debuggeru pro uživatelské programy má své opodstatnění například v případě ladění systémů reálného času vázaných na systémový časovač. Pokud ladíte pomocí běžného debuggeru, může být výsledek nesprávný, i když program běží správně, a SoftIce zastaví program i časovač. To je užitečné při ladění vícevláknových aplikací. SoftIce má navíc velmi, velmi dobře propracované prostředky pro zobrazení informací o všech vláknech v systému, o synchronizaci vláken pro vícevláknové aplikace, informace o handlech... Jedinou nevýhodou tohoto debuggeru je jeho složitost pro programátora aplikací. Ale z jaderných debuggerů je to nejjednodušší a nejúčinnější.


Pro ty nejzvědavější

Nyní samozřejmě není řeč o debuggerech pro aplikace Windows tak relevantní jako před deseti lety. O internet se začal zajímat celý svět a hlavními uživateli SoftIce byli crackeři, neúnavní pracovníci v oblasti pirátství. Není to však tak špatné. Komunikace se SoftIce nepochybně rozvíjí člověka po stránce počítačových znalostí, i když samozřejmě, pokud komunikujete pouze s debuggery a nekomunikujete se skutečnými lidmi, nějaké vedlejší efekty jsou možné, no, myslím, že to už tuší každý.

Debuggery jsou některé z nejvýraznějších typů softwaru, ale z hlediska vývoje jsou i ladicí programy na uživatelské úrovni poměrně složité. Ale přesto, pokud máte chuť a čas vyvinout svůj vlastní debugger, vaše znalosti v oblasti operačních systémů a programování se výrazně zvýší, což znamená, že vaše šance na získání dobře placené práce se zvýší.

Pokud si tedy chcete vytvořit svůj vlastní debugger, měli byste si nejprve přečíst materiály na toto téma. Podle mého názoru je nejlepší knihou začít kniha Johna Robbinse Ladění aplikací pro Windows. Je již stará, vyšla v roce 2001, ale informace v ní obsažené jsou aktuální i dnes, neboť mají obecný, byť nějakým způsobem zásadní charakter. Tato kniha obsahuje příklady psaní debuggerů pro Windows a bude také užitečná, pokud programujete v C++ a chcete lépe porozumět zpracování výjimek. Ve skutečnosti jsem se z této knihy dozvěděl informace o debuggerech uvedených v článku. Pokud tuto knihu nemůžete najít (koneckonců, je už docela stará), existuje několik adres, které se vám mohou hodit. První je takto: www.xakep.ru/post/19158/default.asp. Tento článek z magazínu Hacker se zabývá trochu podrobněji o ladicích programech jádra než já a obsahuje také kód pro jednoduchý ladicí program. A na kalashnikoff.ru/Assembler/issues/016.htm se můžete naučit, jak napsat debugger DOS. Ale samozřejmě nejlepší je přečíst si MSDN a zároveň si najít nějaký open source debugger, abys tomu porozuměl. A samozřejmě, pokud jste se pustili do psaní debuggeru, pak úspěch v tomto obtížném úkolu!

  • autoři:

    Barinov S.S., Shevchenko O.G.

  • Rok:
  • Zdroj:

    Informatika a počítačové technologie / Materiály VI mezinárodní vědeckotechnické konference studentů, postgraduálních studentů a mladých vědců - 23.-25. listopadu 2010, Doněck, DonNTU. - 2010. - 448 s.

anotace

Je poskytnuta srovnávací analýza uživatelského režimu ladění a režimu jádra ve vztahu k operačnímu systému Microsoft Windows, jsou zdůrazněny rozdíly a problémy organizace ladění operačního systému Microsoft Windows. Na základě získaných výsledků jsou formulovány základní požadavky na budování debuggerů v režimu jádra v případě nouzového a interaktivního ladění. Byla provedena analýza stávajících řešení pro shodu s požadavky. Zvláštní pozornost je věnována programu Microsoft Windows Debugger.

Hlavní část

Ladění je proces identifikace a odstraňování příčin chyb v softwaru. V některých projektech zabere ladění až 50 % celkového času vývoje. Ladění lze výrazně zjednodušit pomocí specializovaných nástrojů, které jsou neustále vylepšovány. Hlavním takovým nástrojem je debugger, který vám umožňuje řídit provádění softwaru, sledovat jeho průběh a zasahovat do něj. Nástroje pro ladění jádra používají především vývojáři ovladačů.

Nástroje pro vývoj aplikačního softwaru nabízejí programátorovi širokou škálu možností. Jakékoli integrované vývojové prostředí zahrnuje schopnost ladit bez nutnosti používat nástroje třetích stran. Pokud se bavíme o systémovém softwaru a vývoji ovladačů konkrétně, tak vzhledem ke svým specifikům je proces vývoje extrémně náročný a málo automatizovaný. Všechny vývojové fáze, včetně ladění, jsou samostatné. Pro provedení každého z nich jsou vyžadovány zvláštní podmínky: psaní programového kódu se provádí na plnohodnotném počítačovém systému, ladění se provádí na ladicím systému, testování se provádí v závislosti na okolnostech atd. Samotný debugger režimu jádra je obtížnější se naučit, a proto je méně přátelský.

Obecně lze mluvit o nedostatku nástrojů pro ladění jádra. I když jsou takové prostředky k dispozici, často se nemluví o alternativách. Například ladicí program Microsoft Windows má velmi vysokou vstupní prahovou hodnotu. Mnoho programátorů při setkání s ním mluví o první negativní zkušenosti a většina jeho schopností zůstává nevyužita.

Na základě struktury virtuálního adresového prostoru, pokud aplikace udělá chybu, která způsobí, že aplikace zapíše data do libovolného paměťového místa, aplikace poškodí pouze svou vlastní paměť a neovlivní provoz ostatních aplikací a operačního systému. . Zatímco programový kód režimu jádra je schopen poškodit důležité datové struktury operačního systému, což nevyhnutelně povede k obecnému selhání. Neefektivně napsaný ovladač může také způsobit vážnou degradaci celého operačního systému.

    Moderní debuggery poskytují následující základní funkce:
  • ladění na úrovni zdrojového kódu;
  • exekuční řízení;
  • prohlížení a změna paměti;
  • prohlížení a změna obsahu registrů procesoru;
  • prohlížení zásobníku hovorů.

Pro usnadnění práce s rozebraným kódem, tzv. ladicí symboly. Za běhu linkeru lze kromě obrazu spustitelného souboru vytvořit také datový soubor obsahující informace, které nejsou vyžadovány při spouštění programu, ale jsou mimořádně užitečné při jeho ladění: názvy funkcí, globální proměnné, popisy struktur. Symboly ladění jsou k dispozici pro všechny spustitelné soubory operačního systému Windows.

Řízení provádění se týká schopnosti přerušit a obnovit provádění programového kódu po dosažení zadaného příkazu v programovém kódu. Pokud je programový kód vykonáván v režimu krok za krokem, dojde k přerušení pro každý token programovacího jazyka nebo při ukončení podprogramu. Při volném provádění dochází k přerušením provádění v předem určených úsecích kódu – v místech, kde jsou nastaveny body přerušení.

Při přerušení kódu režimu jádra vzniká následující dilema. Ladicí program používá uživatelské rozhraní pro interakci s programátorem. Tito. alespoň viditelná část ladicího programu běží v uživatelském režimu a k jeho sestavení přirozeně používá aplikační programovací rozhraní (Windows API), které zase spoléhá na moduly režimu jádra. Pozastavení kódu režimu jádra tedy může vést k uváznutí: systém přestane reagovat na požadavky uživatelů.

Pro přístup k paměti jádra musí části ladicího programu také běžet v režimu jádra. To vede ke dvěma problémům najednou, které jsou zřejmým důsledkem organizace paměti v chráněném režimu procesoru.

První problém se týká překladu adres virtuální paměti. Ovladače neustále komunikují s aplikacemi v uživatelském režimu přístupem k jejich paměti. Operační systém Windows převádí virtuální adresy na fyzické na základě konceptu kontextu vlákna. Kontext vlákna je struktura, která odráží stav vlákna a zahrnuje zejména sadu registrů a některé další informace. Když je řízení přeneseno do jiného vlákna, dojde ke změně kontextu, která uloží informace o jednom vláknu a obnoví informace o jiném. Když se kontext vlákna přepne na vlákno jiného procesu, přepne se také adresář stránek používaný k překladu virtuálních adres na fyzické.

Zvláštností je, že při odesílání systémových volání operační systém Windows nepřepíná kontext. To umožňuje kódu v režimu jádra používat virtuální adresy v uživatelském režimu.

Jiná situace je při odesílání přerušení nebo provádění systémových vláken. K přerušení může dojít kdykoli, takže neexistuje způsob, jak předpovědět, který kontext vlákna bude použit. Systémová vlákna nepatří k žádnému procesu a nemohou překládat virtuální adresy v uživatelském režimu. Z toho vyplývá, že v těchto situacích nelze přistupovat k paměti uživatelského režimu.

Druhým problémem je přístup k přemístitelné paměti. Většina informací v paměti je přemístitelná a lze je kdykoli přesunout z fyzické paměti na pevný disk do stránkovacího souboru. Pokud se přistoupí na stránku, která není ve fyzické paměti, procesor normálně vygeneruje přerušení Page Fault, které by zpracoval správce paměti, což vedlo k načtení stránky ze souboru stránky a načtení do fyzické paměti.

Toto chování se rozpadne, pokud je kód ladicího programu nucen používat vysoké úrovně požadavků na přerušení (IRQL). Pokud IRQL odpovídá nebo překračuje IRQL správce paměti, správce paměti nebude moci načíst chybějící stránku, protože operační systém zablokuje přerušení Page Fault. To způsobí zhroucení operačního systému.

Ladění se obvykle dělí na interaktivní a nouzové. Při interaktivním místním ladění běží ladicí program na stejném systému jako ladicí program. Při interaktivním vzdáleném ladění běží ladicí program a ladicí objekt na různých systémech. Při ladění kódu jádra je nutné systém ovládat již od prvních fází jeho bootování, kdy síť ještě nefunguje, proto se ke komunikaci mezi systémy používají jednoduchá sériová rozhraní jako COM, FireWire, USB. V poslední době se díky vývojovým trendům virtualizace softwaru na různých úrovních abstrakce stále více využívají virtuální stroje. Hostující OS funguje jako ladicí OS, hostovaný OS obsahuje uživatelské rozhraní debuggeru.

Proto nouzové ladění nevyžaduje instalaci nástroje pro ladění na testovací počítač. Distribuce operačního systému Windows obsahuje mechanismy pro implementaci nouzového ladění. Před restartem může operační systém uložit informace o svém stavu, které může vývojář analyzovat a zjistit důvod. Takové informace uložené do souboru se nazývají výpis paměti.

Základní nástroje pro ladění režimu jádra poskytuje samotný výrobce operačního systému Windows jako součást volně distribuovaného balíčku Debugging Tools for Windows. Mezi nástroje patří grafické a konzolové debuggery WinDbg, respektive KD (dále jen Windows Debugger). Práce těchto debuggerů je založena na mechanismech poskytnutých vývojáři operačního systému a zabudovaných do jeho jádra.

Hlavním režimem pro Windows Debugger je režim příkazového interpretu. Díky své modulární struktuře podporuje Windows Debugger kromě příkazů dodaných vývojářem moduly třetích stran nazývané rozšíření. Ve skutečnosti je většina vestavěných příkazů také zabalena jako rozšíření.

Windows Debugger je zaměřen na vzdálené interaktivní a nouzové ladění, které při použití odhalí všechny své možnosti. Současně není podporováno úplné místní interaktivní ladění: ladicí program vám umožňuje zobrazit pouze některé struktury jádra.

Existuje rozšiřující modul pro Windows Debugger s názvem LiveKD, vytvořený Markem Russinovichem, který v jistém smyslu implementuje místní interaktivní ladění. LiveKD vytváří výpis paměti fungujícího systému za chodu a používá jej k ladění.

Balíček Debugging Tools for Windows je pravidelně aktualizován a podporuje všechny moderní operační systémy Windows.

Debugger jádra SoftICE, produkovaný společností Compuware v softwarovém balíčku DriverStudio, byl tradičně alternativou k balíčku Debugging Tools for Windows. Charakteristickým rysem SoftICE byla implementace místního interaktivního ladění na podporovaném hardwaru. Ladicí program mohl téměř úplně ovládat provoz operačního systému.

K 3. dubnu 2006 byl prodej produktů řady DriverStudio ukončen z důvodu „různých technických a obchodních problémů a také obecných podmínek na trhu“. Poslední podporovanou verzí operačního systému je Windows XP Service Pack 2. Aktualizace Service Pack obvykle nemění aplikační rozhraní operačního systému, ale čísla systémových volání a další nezdokumentované informace se mohou změnit. Ladicí program SoftICE spoléhal na pevně zakódované adresy interních datových struktur. V důsledku toho byla s vydáním aktualizace Service Pack 3 narušena kompatibilita. Je zřejmé, že novější verze operačního systému Windows také nejsou podporovány.

Syser Kernel Debugger byl vytvořen malou čínskou společností Sysersoft jako náhrada za SoftICE debugger. První finální verze byla vydána v roce 2007. Stejně jako SoftICE je i Syser Kernel Debugger schopen interaktivního ladění na běžícím systému. Podporovány jsou pouze 32bitové edice moderních verzí systému Windows.

V současné době je Windows Debugger hlavním nástrojem mezi vývojáři modulů jádra. Používá jej také tým jádra operačního systému Windows.

Jak spustit ladicí program jádra?

Mistrova odpověď:

V procesu vývoje softwaru je jedna velmi důležitá součást – ladění. Ve vztahu k aplikačním programům se provádí prostředky, které fungují v uživatelském režimu a jsou často zabudovány do IDE. Abyste mohli ladit například ovladače, musíte spustit ladicí program jádra.

Musíte spustit příkazový procesor cmd. Otevřete nabídku Start na hlavním panelu. V zobrazeném okně klikněte na položku „Spustit…“. Zobrazí se okno „Spustit program“. Do textového pole zadejte cmd a klikněte na OK.

Nyní zálohujte svůj soubor boot.ini. Nejprve zjistěte instalační cestu vaší aktuální kopie systému Windows pomocí příkazu: echo %SystemRoot%

Dále přejděte na disk s nainstalovaným operačním systémem zadáním písmen zařízení a dvojtečky. Pomocí příkazu cd přejděte do kořenového adresáře. Nyní pomocí příkazu attrib odeberte ze souboru boot.ini atributy "hidden", "read-only" a "system". Pomocí příkazu copy vytvořte záložní kopii a poté nastavte atributy.

Chcete-li zobrazit aktuální seznam možností spouštění, použijte příkaz bootcfg /query. Prohlédněte si seznam a určete prvek, na jehož základě budou vytvořena nová nastavení s možností ladění v režimu jádra. Je třeba si zapamatovat ID spouštěcí položky.

K vytvoření zaváděcí položky použijte příkaz bootcfg/copy. Chcete-li zadat identifikátor záznamu, který budete kopírovat, použijte parametr /id. Pomocí možnosti /d zadejte název položky, která se zobrazí. Nyní se musíte vrátit na seznam možností spouštění pomocí příkazu bootcfg /query a podívat se na ID přidané položky.

Nyní musíte do dříve vytvořené spouštěcí položky zahrnout možnosti pro spuštění ladicího programu jádra. Pokud budete ladit na cílovém počítači, stačí přidat volbu /debug.

Chcete-li provést vzdálené ladění připojením cílového počítače přes komunikační port k hostitelskému počítači, pak pomocí voleb /port a /baud zadejte číslo portu a přenosovou rychlost.

Pokud budete provádět vzdálené ladění přes FireWire kabel (rozhraní IEEE 1394), použijte volbu /dbg1394 pro povolení odpovídajícího režimu a volbu /ch pro zadání čísla kanálu.

Abyste se ujistili, že změny byly provedeny, zkontrolujte zaváděcí soubory pomocí příkazu bootcfg s parametrem /query. Po provedení příkazu exit zavřete okno shellu.

V případě potřeby změňte nastavení spouštění operačního systému. Otevřete Ovládací panely přes nabídku Start a v ní otevřete položku Systém. V okně „Vlastnosti systému“, které se otevře, vyberte kartu „Upřesnit“. Na této kartě vyberte sekci s názvem „Boot and Recovery“ a klikněte na tlačítko „Options“. V zobrazeném okně „Boot and Recovery“ musíte aktivovat možnost „Zobrazit seznam operačních systémů“. Obě dialogová okna zavřete tlačítkem OK.

Restartovat váš počítač. Vyberte spouštění pomocí ladicího programu. Přihlaste se a začněte pracovat na cílovém počítači nebo začněte vzdáleně ladit. Použijte nástroje jako WinDbg a KD.