V případě skrývání objektů v souborových systémech je situace oproti registru komplikovanější, zejména proto, že celá architektura se skládá z více vrstev a i způsob komunikace mezi jednotlivými aktéry probíhá odlišným způsobem. Než začneme diskutovat aspekty samotného skrývání, je třeba si o o těchto rozdílech trochu více rozepsat.
Objekty ovladačů a zařízení
Základní kameny v implementaci souborových systémů a okolního aparátu tvoří entity objektu ovladače (driver object) a objektu zařízení (device object). Ne že by se tyto entity neuplatňovaly i v jiných oblastech jádra, ale například v oblasti filtrování registrových operací je jejich použití takřka nulové.
Objekt ovladače je datová struktura uchovávající důležité informace o určitém ovladači. Jedná se například o polohu jeho namapovaného souboru v paměti, jeho jméno a seznam adres rutin, které má systém vyvolat, pokud je potřeba, aby ovladač obsloužil požadavek určitého typu.
Objekt zařízení reprezentuje jakousi schránku schopnou přijímat požadavky. Každý objekt zařízení je vlastněný některým objektem ovladače; ne všechny objekty ovladače ale musí vlastnit nějaké objekty zařízení, protože k provádění některých úkonů nejsou zapotřebí.
Přijímání požadavku objektem zařízení probíhá následujícím způsobem:
- požadavek je doručen zadanému objektu zařízení,
- jádro systému nalezne objekt ovladače, který cílové zařízení vlastní a
- předá řízení rutině uvedené v objektu ovladače pro obsluhu daného požadavku. Konkrétní rutina se vybírá podle jeho typu.
Objekt zařízení lze tedy chápat i jako jakési čidlo, které zaznamenává, co se v okolí děje. Objekt ovladače zprostředkovává spojení tohoto čidla s řídícím systémem (mozkem) tvořeným souborem příslušného ovladače.
Zásobníky zařízení
Celkem často při tvorbě ovladačů narazíte na nutnost vědět, jaké požadavky přicházejí na určité zařízení, jež ale vašemu ovladači nepatří. Nemusí se přitom jednat pouze o zlý úmysl (odposloucháváním, jaké požadavky přijímají zařízení klávesnic lze vytvořit poměrně dobře skrytý keylogger); nástroje šifrující obsah disků (TrueCrypt, Bitlocker aj.) musí nejen vědět, jaké požadavky na jimi šifrovaná zařízení přicházejí, ale dokonce potřebují takové požadavky i měnit (dešifrovat načtená data, šifrovat zapisovaná).
Jádro systému za tímto účelem exportuje mechanismus, který ovladačům dovoluje zavěšení nad cílové zařízení, čímž dojde k vytvoření tzv. zásobníku zařízení (device stack). Opakováním tohoto procesu může například vzniknout zásobník z obrázku 1.
Nová zařízení lze připojovat pouze na aktuální vrchol zásobníku. Obvykle ovladač, jehož zařízení se nachází na dně, plně implementuje určitou funkcionalitu (třeba souborový systém), kdežto ostatní ovladače se zařízeními nad ním plní roli tzv. filtrů; zajímají se pouze o určitou množinu požadavků a určitým směrem rozšiřují implementaci poskytovanou ovladačem na dně.
V případě, že určitá entita chce zaslat požadavek na určité zařízení, neměla by tak činit přímo. Místo toho by jí slušné chování mělo donutit předat požadavek zařízení na vrcholu zásobníku, kde se pravé cílové zařízení nachází. Jjak naznačuje předchozí věta, jdná se však spíše o dobrý zvyk, který se v drtivé většině případů dodržuje, než o neporušitelné pravidlo. Nic teoreticky nebrání ovladači zasílat požadavky přímo na určité zařízení a na vrchol zásobníku se neohlížet. Pak se samozřejmě ovladače se zařízeními umístěnými nad tím cílovým o takovém požadavku nedozví (nemohou jej ale ani pozměnit či zablokovat).
Aby zásobníky zařízení správně fungovaly, musí ovladače v nich zainteresované dodržovat velmi důležitou zásadu: požadavky, jež neumějí nebo nemohou obsloužit, musí být odeslány na zařízení nacházející se níže v zásobníku. V ideálním případě pak každý požadavek začíná svoji dráhu u zařízení na vrcholu zásobníku. Zásobníkem propadá, dokud se nenajde ovladač schopný jeho obsluhy. Následně zpráva o obsluze požadavku (ať už byla úspěšná či nikoliv) cestuje opačným směrem až na vrchol.

Obrázek 1: Schéma zásobníku zařízení
Ovladače zavěšené nad určitým zařízením mají nad požadavky jemu zasílané (ale ne zasílané přímo) absolutní kontrolu. Mohou je monitorovat, blokovat, měnit vstupní a výstupní parametry či emulovat. Situace je tedy podobná jako v případě manipulace s registrovými operacemi.
Manipulace se souborovými operacemi
Na obrázku 2 vidíte zásobníky zařízení, které se zapojují do akce u souborových a diskových operací. Požadavky jimi obvykle procházejí zleva doprava, ačkoliv je možné některé zásobníky úplně přeskočit. Aplikace může komunikovat přímo s diskovým zařízením, a tak se vyhnout ovladači souborového systému a správci svazků. Ovladače v zásadě mohou přeskočit až do zásobníku reprezentujícího hardware diskového zařízení nebo rozhraní, kterým je toto zařízení k počítači připojeno.
Požadavek na souborovou operaci je nejprve odeslán ovladači souborového systému, kde se nachází soubor či adresář, jehož se daná operace týká. Ovladač souborového systému (např. ntfs.sys pro NTFS či fastfat.sys pro FAT) je zodpovědný za provedení zadané operace. Pokud se do tohoto zásobníku zavěsí například zařízení ovladače útočníka, může pozměňovat výsledky operací tak, aby došlo ke skrytí určitých souborů a složek.
Pokud nelze požadavek obsloužit na místě, ovladač souborového systému jej pošle do zásobníku správce svazků. Správce svazků již nemá potuchy o souborech či adresářích. Většina jeho zařízení reprezentuje konkrétní svazky (diskové jednotky, potažmo oddíly, často bývají označené velkým písmenem jao A:, C: atd.), jejichž obsah interpretují právě ovladače souborových systémů. Ovladač souborového systému obvykle překládá souborové operace do série čtení a zápisů, případně jiných typů požadavků, kterým správce svazků (nebo některý z ovladačů v dalších zásobnících) rozumí.
Do zásobníku správce svazků se obvykle zavěšují například ovladače dovolující šifrování svazků a disků, protože mohou sledovat a modifikovat pokusy o čtení a zápis dat v rámci jednoho svazku. Patří mezi ně i Bitlocker. Neméně známý šifrovací nástroj TrueCrypt při šifrování disků a diskových oddílů postupuje jiným způsobem. Svá zařízení zavěšuje až do zásobníku reprezentujícího celé diskové zařízení (tedy všechny oddíly či jejich části na něm). V případě šifrovaných kontejnerů se správcem svazků stává sám ovladač TrueCryptu a po potřebných úkonech odesílá požadavky ovladači souborového systému, na kterém je příslušný kontejner umístěn.
Správce svazků upravuje přijaté požadavky tak, aby jim rozuměla zařízení v zásobníku reprezentujícímu celé diskové zařízení, na kterém se příslušný svazek nachází. Dolní část tohoto zásobníku se již odlišuje podle toho, jak je daný disk připojen (USB, ATA, SATA, SCSI aj.). Cílem ovladače se zařízením na dně zásobníku je opět přetransformovat požadavky tak, aby jim rozuměly ovladače v posledním zásobníku. V případě zásobníků na obrázku 2 se jedná o ovladače zodpovědné za obsluhu USB Mass Storage zařízení a USB hubu, ke kterému jsou tato zařízení připojena.

Obrázek 2: Zásobníky zařízení zainteresované v souborových operacích
Výhody a nevýhody
Potenciální útočník má tedy širokou možnost výběru místa, kam jeho ovladač zavěsí zařízení, aby mohl provádět souborové a diskové záškodnictví. Pokud je jeho cílem skrývání, měl by se zavěsit na co možná nejnižší úroveň, protože lze předpokládat, že detektory diskových nesrovnalostí se budou také snažit pracovat co nejblíže k hardware. Taková blízkost s sebou ale, nejen pro útočníka, přináší řadu nevýhod.
První problém může nastat v případě, že obsah oblasti (svazku, oddílu, disku), kde chce útočník skrývat svoji přítomnost, podléhá softwarovému šifrování realizovanému například pomocí výše zmíněných nástrojů. Pokud se útočník zavěsí níže než zařízení provádějící šifrování a dešifrování dat, jeví se mu obsah jako pseudonáhodná (a tudíž z hlediska významu nesmyslná) posloupnost bajtů. Pokud tedy části, které potřebuje skrýt, detekuje na základě obsahu, musí se k nim dostat jiným způsobem. Obdobná situace samozřejmě platí i pro „hodné hochy“ – z pseudonáhodné posloupnosti se těžko pozná, zda popisuje skrytý soubor, nebo zda se jedná o nepoužívaný blok.
Další problém nízkoúrovňového zavěšení spočívá ve ztrátě kontextu. Na takové úrovni se totiž většina operací na disku jeví jako čtení a zápisy bloků dat. Co ale tyto operace znamenají jako celek, nelze z jejich monitorování jednoduše určit. Jednotlivé požadavky s sebou nenesou žádnou informaci, kterého souboru se týkají, protože takovému údaji nikdo v nízkoúrovňových zásobnících nerozumí.
V neposlední řadě se útočník setká i s problémem rozdílnosti hardware. Flash disky vložené do USB portu se ovládají jinak než ty připojené přes rozhraní IDE či SATA. Běžné aplikace a i ovladače o těchto rozdílech nemusí nic vědět, protože jim stačí komunikovat buď přes souborové operace, nebo přinejhorším s vrcholem zásobníku reprezentujícího obecné diskové zařízení, kde se ještě rozdíly v hardware neprojevují. Je třeba ale také mít na paměti, že i tyto entity mohou využít požadavků určených pro řízení pouze určitého druhu hardware, i když jim ovladače nacházející se na vyšších úrovních nebudou rozumět – požadavky na základě výše řečeného pravidla propadnou až k ovladačům, jež specifičnosti hardware rozumějí. Útočník si tedy například musí být vědom, že operaci čtení je možné provést pomocí obecného požadavku typu IRP_MJ_READ, ale i přes speciální zprávu IOCTL_SCSI_PASS_THROUGH, pokud je daný disk připojen přes rozhraní SCSI, nebo se tváří jako USB Mass Storage, které se v horní polovině nejnižšího zásobníku tváří jako SCSI-kompatibilní.
I když se útočníkovi podaří vyrovnat všemi výše popsanými neduhy, stále nemá vyhráno, i když tentokrát se jedná spíše o můj subjektivní názor než objektivní fakt. Detektoru nesrovnalostí stačí z disku data pouze číst a následně aplikovat parser příslušného souborového systému. Útočník navíc musí obsah, který skrývá, chránit před poškozením. Je-li zavěšen „proklatě nízko“, ovladač souborového systému o jeho skrývaných datech nic neví, a tak se je může pokusit přepsat i zcela neúmyslně (například se rozhodne na daném místě uložit data nového souboru). Útočník tedy musí být na pozoru i v případě, že se jej odhalit nikdo nesnaží. Z této úvahy vyplývá, že napsat detektor nesrovnalostí v souborovém systému je jednodušší než vytvořit záškodnický ovladač, který by jej obalamutil (nebo alespoň působil na stejné úrovni).
Z výše popsaných důvodů pořádně provedené skrývání souborů a složek nevypadá jako příliš lukrativní podnik. Pro útočníka existují i alternativy:
- Filtr souborového systému. Útočník zůstane u skrývání složek a souborů, ale vzdá se nízkoúrovňového filtrování požadavků. Uvažuje tak, že dokud uživatel nepojme podezření, skrývání realizované na základě filtru souborového systému (přítomnost v zásobníku na nejvyšší úrovni) plně postačuje. A pokud uživatel podezření pojme, opatření proti odhalení jsou tak drahá, že se je pro daný projekt nevyplatí realizovat.
- Skrývání jednotlivých bloků. Útočník se také může rozhodnout, že místo do souborů, bude svá tajná data ukládat přímo na disk. Najde si nepoužívanou oblast (například mezeru mezi dvěma oddíly nebo nevyužité místo na konci disku), kam bude svá data přímo ukládat a číst. Jelikož taková oblast nemá pro nikoho kromě útočníka žádnou strukturu, stačí mu příslušně manipulovat s operacemi čtení a zápisu na ni mířícími, a tím zajistit maximální úroveň utajení.
- Skrývání malých struktur. Další možnost pravděpodobně realizovatelnou i při nízkoúrovňovém zavěšení představuje skrývání datových struktur souborového systému, které se nacházejí v rámci jednoho bloku (sektoru, clusteru). V případě souborového systému NTFS se jedná o alternativní datové proudy (alternate data streams) či rozšířené atributy (extended attributes). Definice těchto struktur zabírá pár desítek bajtů v rámci záznamu popisujícího objekt souborového systému, ke kterému jsou přidruženy. Pro útočníka by neměl být velký problém najít soubor či adresář, do kterého pravděpodobně nikdo nebude zapisovat, přidružit k němu některou z těchto struktur a zapsat do ní potřebná data. Navenek však bude záznam o objektu zobrazovat tak, jako by k němu žádná data nepřidružoval.
Rozhodnutí implementovat skrývání v rámci filtru souborového systému sice útočníkovi práci zjednoduší (zejména v případě, že se tento počin rozhodne realizovat prostřednictvím nadstavby, kterou Windows pro tvorbu filtrů souborového systému poskytují, a vytvoří tzv. minifiltr souborového systému – file system minifilter driver), ale ne na úroveň triviality. Měl by například počítat s tím, že k objektům souborového sytému lze získat přístup nejen na základě jména. U některých souborových systémů lze k tomuto účelu využít i interního ID cílového objektu. Na druhou stranu, filtry souborového systému dovolují provádět podobné věci, jako umí ukázkový program RegHider – skrývat a emulovat obsah na základě aplikace, která o něj žádá.
Útočník by se mohl také pokusit realizovat jiné nepěkné kousky. Jednou z možností, která by podle mého názoru stála za prozkoumání, je zmanipulování symbolických odkazů. Aplikace totiž běžně přistupují k souborům přes cesty začínající písmenem svazku (například C:WindowsTemp). Tato písmena však nejsou reálnými jmény zařízení reprezentujících dané svazky, nýbrž pouze symbolickými odkazy na ně. Pokud by útočník nasměroval tyto odkazy na svá zařízení (ať už přímou modifikací paměti jádra, nebo jejich smazáním a znovuvytvořením), mohl by svést většinu požadavků na souborové operace „na svoje území“ mimo zásobníky zařízení, které se jejich zpracováním standardně zabývají. Útočník by se do těchto zásobníků nemusel vůbec zavěšovat nebo instalovat svůj minifiltr.
Při zavěšování do zásobníku určitého zařízební musí nejen útočník, ale kdokoliv, dávat pozor, zda zařízení na dně zásobníku podporuje Plug&Play. Pokud ano, každý ovladač mající své zařízení v daném zásobníku musí rozumět požadavkům zasílaným správcem Plug&Play a adekvátně na ně reagovat. Mezi adekvátní reakci patří zejména odpojení zařízení ze zásobníku, pokud se dozvíte, že fyzické zařízení zásobníkem reprezentované bylo ze systému odebráno. POdobná, ale daleko jednodušší pravidla platí i v zásobnících souborových systémů.
Závěr
Celá tato poněkud teoretičtější kapitola pojednává o tom, jaké mechanismy jádro Windows uplatňuje při provádění souborových a diskových operací. Stejné koncepty se však používají i u jiných druhů zařízení: klávesnic, myší, paralelních a sériových portů, USB zařízení a najdete určitě i další příklady. Pokud tedy plánujete vytvořit ovladač, který s některými z těchto zařízení nějakým způsobem spolupracuje, měli byste vědět, co to je objekt ovladače, objekt zařízení či zásobník zařízení. Je ale potřeba mít se na pozoru před úskalími, jejichž popis tvoří zřejmě větší část této kapitoly. A to nemusíte vůbec patřit do skupiny ošklivých a efektivních útočníků.