HTCinside
Při publikování našich softwarových produktů všichni používáme k zápisu do EULA „Software nesmíte zpětně analyzovat, dekompilovat ani rozebírat“. V mnoha situacích však slova nejsou tou nejlepší ochranou a opravdu potřebujete zavést nějaké technické nástroje, které zabrání invertování softwaru a ochrání vaše know-how před prozrazením.
Existuje několik technologických přístupů, jak zabránit softwarovému reverznímu inženýrství: anti-debug, anti-dump a další. V tomto příspěvku se zaměříme na některé metody proti ladění, protože jsou v jádru ochrany proti zpětnému inženýrství. Připojení debuggeru ke zkoumanému procesu za účelem jeho provádění krok za krokem je velmi důležitou fází jakékoli reverzní práce – pojďme se tedy podívat, jaké nástroje můžeme použít ke ztížení života reverzorů.
Je pár věcí, které bych rád zmínil hned na začátku. První je, že neexistuje žádná univerzální nebo 100% neprůstřelná ochrana před softwarovým reverzním inženýrstvím. Vždy existuje způsob, jak se obraceč dostat dovnitř, jedinou strategií, kterou máme, je udělat jeho práci co nejtěžší a nejnáročnější.
Dále existuje několik technik proti reverznímu inženýrství a zejména metod proti ladění, včetně ochrany založené na čase nebo dokonce specifických technologií vložených do kódu, jako jsou nanomity. V tomto příspěvku budeme zvažovat pouze několik standardních přístupů specifických pro systémy založené na Windows, nejoblíbenějších.
Níže uvedené přístupy jsou popsány obecně.
Obsah
Systémy Windows nám poskytují několik připravených nástrojů k vytvoření jednoduché ochrany proti ladění. Jedna z nejjednodušších technik proti ladění je založena na volání funkce IsDebuggerPresent. Tato funkce vrátí hodnotu TRUE, pokud ladicí program v uživatelském režimu aktuálně ladí proces.
Tato funkce odkazuje na PEB (Process Environment Block, uzavřená struktura systému) a zejména na jeho pole BeingDebugged. Reverzátory při obcházení takovéto ochranné techniky využívají této skutečnosti: např. při použití injekce DLL nastaví hodnotu BeingDebugged na 0 těsně před provedením této kontroly v chráněném kódu.
Pár slov o tom, kde takovou kontrolu provést. Hlavní funkce není tou nejlepší volbou: inverzní pracovníci ji obvykle nejprve zkontrolují v rozloženém seznamu. Je lepší provést kontrolu proti ladění v TLS Callback, jak se nazývá před vstupním call pointem hlavního spustitelného modulu.
Další možností kontroly funkčnosti je CheckRemoteDebuggerPresent. Na rozdíl od výše popsané funkce kontroluje, zda jiný paralelní proces právě neladí proces. Je založen na funkci NtQueryInformationProcess a zejména na hodnotě ProcessDebugPort.
Zatímco předchozí skupina metod byla postavena na kontrole přítomnosti debuggeru, tato poskytuje aktivní ochranu před ním.
Počínaje systémem Windows 2000 přijímá funkce NtSetInformationThread nový příznak s názvem ThreadHideFromDebugger. Toto je velmi účinná technika proti ladění poskytovaná v OS Windows. Vlákno s tímto příznakem přestane odesílat oznámení o událostech ladění, včetně bodů přerušení a dalších, čímž se skryje před jakýmkoli ladicím programem. Nastavení ThreadHideFromDebugger pro hlavní vlákno výrazně zkomplikuje proces připojení debuggeru k vláknu.
Logické pokračování bylo zavedeno ve Windows Vista s funkcí NtCreateThreadEx. Má parametr CreateFlags, který mimo jiné nastavuje příznak THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER. Proces s tímto nastaveným příznakem bude před debuggerem skrytý.
Probíhající ladění lze detekovat podle změněných hodnot různých příznaků v různých systémových a procesních strukturách.
Windows NT obsahuje globální proměnnou s názvem NtGlobalFlag se sadou příznaků, které se používají pro trasování a ladění systému. Výše zmíněná struktura PEB obsahuje vlastní pole NtGlobalFlag. Během ladění se tato hodnota pole změní s několika konkrétními nastavenými příznaky. Kontrola těchto příznaků může vytvořit spouštěče pro ochranu proti ladění.
Spustitelný soubor může resetovat příznaky NtGlobalFlag struktury PEB pomocí specifické struktury s názvem IMAGE_LOAD_CONFIG_DIRECTORY, která obsahuje specifické konfigurační parametry pro zavaděč systému. Má pole GlobalFlagsClear, které resetuje příznaky NtGlobalFlag PEB. Ve výchozím nastavení není tato struktura přidána do spustitelného souboru, ale může být přidána později. Skutečnost, že spustitelný soubor nemá tuto strukturu nebo hodnotu GlobalFlagsClear je rovna 0, zatímco odpovídající pole uložené na disku nebo v paměti běžícího procesu není nulové, naznačuje přítomnost skrytého debuggeru. Tato kontrola může být implementována do spustitelného kódu.
Další skupinou příznaků je příznak procesní haldy. V odpovídající struktuře _HEAP jsou dvě pole: Flags a ForceFlags. Oba mění své hodnoty, když je odpovídající proces laděn, a tak mohou být základem anti-debug kontroly a ochrany.
Další kontrolou příznaku, kterou lze použít k detekci debuggeru, je kontrola příznaku pasti (TF). Je v registru EFLAGS. Když se TF rovná 1, CPU generuje INT 01h (výjimka «Single Step») po každém provedení instrukce podporující proces ladění.
Breakpointy jsou nezbytnou součástí každého ladícího procesu a díky jejich detekci můžeme detekovat a neutralizovat debugger. Taktika proti ladění založená na detekci bodu přerušení je jednou z nejúčinnějších a je těžké ji obejít.
Existují dva typy bodů přerušení: softwarové a hardwarové.
Softwarové body přerušení nastavuje debugger vložením instrukce int 3h do kódu. Detekční metody debuggeru jsou tedy založeny na výpočtu a kontrole kontrolního součtu odpovídající funkce.
Neexistuje žádná univerzální metoda, jak proti této ochraně bojovat – hacker bude muset najít část kódu, která počítá kontrolní součty a nahradit vrácené hodnoty všech odpovídajících proměnných.
Hardwarové body přerušení se nastavují pomocí specifických registrů ladění: DR0-DR7. Pomocí nich mohou vývojáři přerušit provádění programu a přenést řízení na debugger. Ochrana proti ladění může být postavena na kontrole hodnot těchto registrů nebo být proaktivnější a vynuceným resetem jejich hodnot zastavit ladění pomocí funkce SetThreadContext.
Structured Exception Handling neboli SEH je mechanismus, který umožňuje aplikaci přijímat oznámení o výjimečných situacích a řešit je namísto operačního systému. Ukazatele na obslužné nástroje SEH se nazývají rámce SEH a umísťují se do zásobníku. Když je vygenerována výjimka, je zpracována prvním rámcem SEH v zásobníku. Pokud neví, co s tím, je předán dalšímu v zásobníku a tak dále až do systémové manipulace.
Při ladění aplikace by měl ladicí program zachytit řízení po 3h generování int, jinak jej převezme obslužná rutina SHE. To lze použít k organizaci ochrany proti ladění: můžeme vytvořit vlastní obslužnou rutinu SEH a umístit ji na vrchol zásobníku a poté vynutit generování int 3h. Pokud náš handler získá kontrolu, proces není odladěn – jinak můžeme vynutit opatření proti ladění, když jsme detekovali debugger.
Toto je jen několik technik proti ladění z velkého množství z nich.
Osvědčeným postupem je kombinovat různé techniky proti zpětnému chodu, takže je mnohem těžší obejít ochranu. Dodatečné kontroly mohou zpomalit běh aplikace, proto jsou na základní moduly obsahující patentované technologie a know-how obvykle aplikovány ty nejsilnější ochranné techniky. Konečně je to kompromis mezi úrovní bezpečnosti kódu a výkonem aplikace.
Rád bych zmínil, že zpětné inženýrství softwaru není vždy nezákonné a někdy může být použito během výzkumného procesu pro takové úkoly, jako je zlepšení kompatibility, opravy, nezdokumentované používání systémového rozhraní atd. Právní služby reverzního inženýrství dodané profesionály se také zabývají ochranou proti ladění, ale z druhé strany – jejím obcházením.