Systémové programovanie Windows Dynamické knihovny Andrea Číková Martin Osovský O čem to dnes bude? • K čemu jsou dobré? • Jaký mají vztah ke spustitelným souborům a procesům? • Jak se sestavují? • Jak se načítají? • Jak se hledají? • Rebasing a binding • API hooking Význam dll knihoven • Velký význam ve windows – sdílení kódu ▫ kernel32.dll – procesy, vlákna a paměť ▫ user32.dll – okna a zprávy ▫ gdi32.dll – grafika a texty ▫ advapi32.dll, comdlg32.dll, comctl32.dll ▫ další pro ještě specializovanější věci (RPC ap.) ▫ C runtime library Příklady použití • rozšíření možností aplikací (pluginy, umožnění úprav třetím stranám, API) • zjednodušení procesu vývoje (ale aplikace by měla mít co nejméně souborů) • šetření pamětí (dll se do paměti načítá jen jednou – crt) • sdílení resourců (dialogy, ikony, řetězce) • lokalizace • rozdíly v platformách • zvláštní vlastnosti kódu v knihovnách ▫ dá se hookovat ▫ hostují COM a ActiveX komponenty ▫ mohou hostovat .NET knihovny DLL a spouštěná aplikace • dll je PE modul, ke kterému se loader chová jinak než k exe modulu • dll se mapuje do adresového prostoru procesu ▫ implicitně při načítání aplikace ▫ explicitně voláním funkce • po načtení je kód k disposici všem vláknům procesu • sdílení kódu používá copy on write (globální a statické proměnné) • dll se rozpustí v paměti procesu, který ji načetl DLL a paměť • statická vs. dynamická crt • kód, který uvolňuje paměť by měl být pro jistotu ve stejném modulu jako ten, který ji rezervoval Sestavování a implicitní načítání • je třeba dostat symboly do tabulky exportů dll modulu – declspec(dllexport) • je třeba, aby se neprovádělo pozmněňování jmen – extern „C‟ • v cílovém modulu je třeba deklarovat funkci jako extern nebo lépe jako declspec(dllimport) • exportované funkce jsou v souboru .lib, který se linkuje do cílového modulu (pro dll je to jen seznam symbolů, jinak statická knihovna) • je třeba použít sdílený hlavičkový soubor Sestavování • jiná možnost je použít def soubor se sekcí EXPORT následovanou jmény symbolů • nebo parametr linkeru /export • utilita DumpBin s parametry imports nebo exports Načítání knihovny • knihovna se načte do paměťového prostoru na tzv. module base address • modul specifikuje preferred base address • každý symbol má relative virtual address • z import sekce exe se načte, které knihovny se mají načíst • ty se načtou a s jejich import sekcemi se provede totéž • pak se spočítají skutečné adresy symbolů a ty se vloží do import sekce exe (binding) Načítání knihovny – pokr. • pokud nějaký modul nelze vložit na jeho PBA, provede se tzv. rebasing • rebasing lze provést pro všechny moduly předem pomocí rebase.exe • je to složitý proces (musí se měnit všechny adresy u všech instrukcí v modulu) • kvůli copy on write to zabírá místo v systémovém page file • binding lze provést předem pomocí bind.exe • zápis do import sekce také přesune paměť do systémového page file Ruční načítání • LoadLibraryEx • parametr flags: ▫ DONT_RESOLVE_DLL_REFERENCES ▫ LOAD_LIBRARY_AS_DATAFILE ▫ LOAD_LIBRARY_AS_IMAGE_RESOURCE • LoadLibrary - bez flags • Uvolnění knihovny FreeLibrary • Proces si drží počet odkazů na nějakou knihovnu (je-li 0, uvolní ji z paměti - virtuální) Načítání symbolu • je třeba mít typedef na příslušný typ funkce a proměnnou tohoto typu • GetProcAddress načte adresu příslušného symbolu v DLL • symbol voláme normální notací volání funkce Vstupní bod DLL • DLL může obsahovat funkci DllMain • ta se volá ▫ když se knihovna načte (hlavní vlákno, nebo vlákno, které zavolalo LoadLibrary) ▫ když se vytvoří vlákno a knihovna je již načtena (příslušné vlákno) ▫ když končí vlákno a knihovna je ještě načtena (příslušné vlákno) ▫ když se knihovna uvolní z paměti (hlavní vlákno, nebo vlákno, které zavolalo FreeLibrary) Vstupní bod • parametr reason rozlišuje, proč byla DllMain zavolána • při hodnotě DLL_PROCESS_ATTACH se neignoruje návratová hodnota, pokud je FALSE ▫ při implicitním načítání zhavaruje celý proces ▫ LoadLibrary vrátí NULL Ďakujeme za pozornosť