Съвети и трикове за програмиране Специални трикове за (не толкова) начинаещи системни инженери
Съдържанието на статията
Искате ли да станете уверен потребител на вашия компютър? Лесно ли е да намерите бутона „Старт“ и да стартирате играта „Миночистач“? Искате ли приятелите ви да ви гледат със завист, защото сте започнали да използвате желаните клавишни комбинации Alt + TAB? Ако да, кажете ми защо взехте списанието на по-големия си брат? Той ли ти го купи? Върнете веднага списанието!
Дадохте ли? Добре. В тази статия ще пилим с циркуляр такива неща като Driver Signature Enforcement, което притесни много писатели на драйвери навреме, както и TLS - Thread Local Storage, такъв интегрален и скрит съсед на всеки PE файл.
Нещо относно прилагането на подписите на водача
Както знаете, започвайки с Windows Vista, момчетата от Microsoft положиха много усилия да заредят неподписани (чети - чужди) драйвери в системата (за Vista говорим за 64-битовата версия). На първо място, това е направено, вероятно, за защита на системата от руткитове, чиято основна бойна част зависи от драйвера на ядрото. В резултат това доведе до всякакви проблеми за разработчиците на системи, участващи в разработването на драйвери. Сега, за да заредите успешно драйвера в Windows Vista/7, беше необходим цифров подпис и той трябваше да бъде купен за много пари от компании като Verisign или Thawte.
Разработчиците на руткитите бяха леко разстроени от тази новина. Вместо да се разстройват, те започнаха да търсят „леви“ пътеки и вратички, за да заредят неподписани драйвери в системата. По традиция го направиха, а първата сред тях беше любимата ни красавица - Рутковска.
Няма да ви разказвам за тези методи, можете да прочетете за тях в интернет. Вместо да ядем чужда риба, по-добре вземете въдицата си и вижте какво можем да ловим от този забавен механизъм - Прилагане на подпис на водача.
Нейното „сърце“ е библиотеката ci.dll, която както винаги се намира в папката /% systemroot%/system32. Той експортира следните функции:
- CiCheckSignedFile
- CiFindPageHashesInCatalog
- CiFindPageHashesInSignedFile
- CiFreePolicyInfo
- CiGetPEInformation
- CiInitialize
- CiVerifyHashInCatalog
Най-интересната функция тук е CiInitialize, тя се импортира от ядрото по време на процеса на инициализация на системата, нейният псевдокод изглежда по следния начин:
VOID SepInitializeCodeIntegrity ()
ULONG CiOptions;
memset (g_CiCallbacks, 0, 3 * sizeof (SIZE_T));
CiOptions = 4 | 2;
ако (KeLoaderBlock)
ако (* (ULONG *) (KeLoaderBlock + 84))
if (SepIsOptionPresent ((KeLoaderBlock + 84),
L "DISABLE_INTEGRITY_CHECKS"))
CiOptions = 0;
if (SepIsOptionPresent ((KeLoaderBlock + 84),
L "ИЗПИТВАНЕ"))
CiOptions | = 8;
>
CiInitialize (CiOptions, (KeLoaderBlock + 32), & g_CiCallbacks);
>
>
Най-интересното тук е, че CiInitialize връща три указателя за функция обратно към ядрото:
g_CiCallbacks [0] = CI! CiValidateImageHeader,
g_CiCallbacks [1] = CI! CiValidateImageData,
g_CiCallbacks [2] = CI! CiQueryInformation.
Нека си спомним това и да разгледаме пълния стек от извиквания на функции на много ранния етап от инициализацията на системата:
nt! SepInitializeCodeIntegrity
nt! SepInitializationPhase1 + 0x1a1
nt! SeInitSystem + 0x29
nt! Phase1InitializationDiscard + 0x7ce
nt! Phase1Initialization + 0xd
nt! PspSystemThreadStartup + 0x9e
nt! KiThreadStartup + 0x19
Както можете да видите тук, SepInitializeCodeIntegrity (или по-скоро CiInitialize) създава някои условия, необходими в бъдеще за успешно зареждане на системата. Ако се задълбочим в CiInitialize, ще видим, че тази функция проверява валидността на драйверите в Boot Driver List (т.е. зарежда се при стартиране). Ако по време на този процес бъдат открити грешки, процесът на изтегляне ще бъде спрян.
Продължаваме да разглеждаме процеса на зареждане на драйвери в системата. Стекът от системни функции в този случай в Vista/7 ще изглежда така:
nt! MmLoadSystemImage
nt! MiObtainSectionForDriver
nt! MiCreateSectionForDriver
nt! MmCheckSystemImage
nt! NtCreateSection
nt! MmCreateSection
nt! MiValidateImageHeader
nt! SeValidateImageHeader
nt! _g_CiCallbacks [0] т.е. CI! CiValidateImageData
Интересуваме се от SeValidateImageHeader - той проверява дали драйверът има цифров подпис.
Тя го прави по следния начин. Първо се проверява променливата nt! G_CiEnabled (мисля, че няма нужда да декриптирам значението й:)) и, ако е зададена на TRUE, ще провери стойността на указателя nt! G_CiCallbacks [0]. Ако последното не е NULL, то ще извика тази функция и ще върне контрола. Ако nt! G_CiCallbacks [0] е празно, то ще върне състоянието 0xc0000428, което на човешки език съответства на „Windows не може да провери цифровия подпис на този файл“.
Ако променливата nt! G_CiEnabled е FALSE, тогава функцията ще разпредели един байт в паметта, ще копира указател към първия си аргумент там и след това ще върне STATUS_SUCCESS с чиста съвест. Всичко! По такъв прост начин WIndows Vista/7 проверява наличието и валидността на цифров подпис, за да зареди драйвера.
Заключение: Проверката дали даден драйвер ще бъде зареден в системата зависи само от една променлива. И ако някой иска да изключи тази проверка, трябва само да пренапише един байт в паметта. Вярно е, че ще бъде доста трудно да се направи това, защото нито nt! G_CiEnabled, нито nt! G_CiCallbacks се експортират от ядрото и ще бъде проблематично да ги намерите.
Какво знаете за TLS? Локалното съхранение на нишки е може би най-документираната от най-недокументираните функции на Windows.
Какво знаем за TLS? Често се използва от програмисти в многонишкови приложения. Рихтер дава пример в своята библия за системно програмиране - всеки поток в TLS е свързан с датата и часа, когато е създаден. В момента на унищожаване на потока можете да изчислите времето, през което е съществувал потокът. Сценариите, в които има данни, свързани едновременно с програмата като цяло, и по-специално с отделен поток, налагат използването на TLS. Например, да предположим, че процес притежава някакъв масив. Всеки елемент от масива, заедно със съдържанието му, съответства на отделен поток. Как нишката знае кой индекс в глобалния масив е нейният? Да, можете да предадете параметър на функцията ThreadProc като индекс ... но това са всички добре познати страни на монетата. Какво не е на повърхността на TLS? Какво не знаем за него?
Форматът PE поддържа функции за обратно извикване на TLS, които автоматично се извикват от системата преди прехвърляне на контрола към входната точка. По-специално, това ви позволява да определите наличието на дебъгер или да извършите някои тайни действия. Ако PE файлът има свои собствени обратни извиквания, те могат да модифицират TLS таблицата по време на изпълнение. Това означава, че ако имате инсталиран един обратен разговор, той може лесно да добавя други обратни обаждания по време на изпълнение.
TLS се използва в голям брой протектори, защити, вируси, crackme и други програми, които са в сферата на нашите общи интереси.
Blacklight използва някои техники за отстраняване на грешки, започвайки със създаването на таблица за обратно извикване на TLS (Thread Local Storage). Обратното извикване на TLS на Blacklight се опитва да измами дебъгера, като създава копие на основния процес (вилица), преди обектът на процеса да бъде напълно създаден.
Някои вируси се въвеждат изключително чрез модифициране само на четири байта - указател към TLS таблица, намираща се в паметта (в една от системните DLL), където се намира указателят към командата за прехвърляне на контрол към кода на черупката.
Разбира се, такава техника на внедряване работи само за версията на операционната система, за която е изострена, но антивирусите не откриват такива вируси. Или изобщо да игнорирате промяната в таблицата на директориите.
Между другото, ако се интересувате от използването на TLS като техника за отстраняване на грешки, прочетете подробностите в отличната статия на К. Касперски „Изчерпателно ръководство за готвене и напукване на TLS“ (http://www.xakep.ru/magazine/ xa/118/080/1. asp).
Заключение
Надяваме се, че четенето на тази статия ви е направило напреднал потребител на компютър. Какво прави напредналият потребител? Той взема дебъгер и взима всичко. От това той става още по-напреднал потребител:). Като цяло, придвижете се напред, прочетете] [и може Силата да бъде с вас!
На диска можете да намерите основна, цели 186 KB, работа на Matthew Conover'a от Symantec, посветена на изучаването на механизма за сигурност в ядрото на Vista - „Windows Vista Kernel Mode Security“, както и няколко статии по темата на статията.