За­гадоч­ная аббре­виату­ра TLS таит в себе мно­го сек­ретов. Это — мощ­ней­шее ору­жие про­тив отладчи­ков и дизас­сем­бле­ров. В ком­бинации с упа­ков­щиками TLS прев­раща­ется в гре­мучую смесь тер­моядер­ного типа.

Что же такое TLS и чем оно гро­зит хакерам? Нач­нем изда­лека. Популяр­ные язы­ки прог­рамми­рова­ния (в том чис­ле С) под­держи­вают ста­тичес­кие и гло­баль­ные перемен­ные, исполь­зование которых дела­ет код потоко­небе­зопас­ным. Все потоки раз­деля­ют один и тот же набор гло­баль­ных/ста­тичес­ких перемен­ных, порож­дая путани­цу и хаос. Поток А положил в перемен­ную foo зна­чение X и толь­ко хотел про­читать его обратно, как вне­зап­но про­будив­ший­ся поток B записал в foo зна­чение Y, что ока­залось для A пол­ной неожи­дан­ностью.

Microsoft раз­работа­ли спе­циаль­ный механизм, име­нуемый локаль­ной памятью потока (Thread Local Storage, или сок­ращен­но TLS), пре­дос­тавля­ющий в рас­поряже­ние потоков инди­виду­аль­ные наборы гло­баль­ных/ста­тичес­ких перемен­ных. TLS под­держи­вает­ся как на уров­не явно вызыва­емых API-фун­кций (TLSAlloc, TLSFree, TLSSetValue, TLSGetValue), так и на уров­не PE-фор­мата, неяв­но обра­баты­ваемо­го сис­темным заг­рузчи­ком. PE-фор­мат под­держи­вает фун­кции обратно­го вызова (TLS-callback), авто­мати­чес­ки вызыва­емые сис­темой до переда­чи управле­ния на точ­ку вхо­да. В час­тнос­ти, это поз­воля­ет опре­делить наличие отладчи­ка или скрыт­но выпол­нить некото­рые дей­ствия. Сис­темный заг­рузчик так­же записы­вает TLS-индекс в задан­ную локацию — отличный спо­соб неяв­ной самомо­дифи­кации прог­раммы. Дизас­сем­бле­рами она не отлавли­вает­ся и заводит хакера в тупик.

TLS исполь­зует­ся в боль­шом количес­тве про­тек­торов, защит, вирусов, crackme и про­чих прог­рамм, взлом которых опи­сан в куче раз­личных тутора­лов. Одна­ко изло­жение обыч­но носит повер­хностный харак­тер — целос­тной кар­тины пос­ле проч­тения не соз­дает­ся. Поп­робу­ем это испра­вить.

www

Обя­затель­но изу­чи спе­цифи­кацию PE от Microsoft и слей «кряк­ми» с моего сай­та.

Требуется помощь читателей!

К сожале­нию, файл buckme-crackme.zip не был выложен на DVD «Хакера», а сайт Кри­са боль­ше не дос­тупен. Если у тебя сох­ранил­ся этот файл, пожалуй­ста, приш­ли его в редак­цию на адрес content@glc.ru.

 

Fundamentals

Преж­де все­го нам понадо­бит­ся спе­цифи­кация PE-фор­мата, пос­леднюю вер­сию которо­го (пред­став­ленную в виде XML) мож­но утя­нуть пря­мо из‑под носа Microsoft. Тот же самый файл, толь­ко кон­верти­рован­ный в MS Word 2000, я выложил на сво­ем сер­вере.

TLS-таб­лица опи­сыва­ется девятым (счи­тая от нуля) чет­вер­тным сло­вом в Optional Header Data Directories. Пер­вое двой­ное сло­во хра­нит в себе RVA-адрес TLS-таб­лицы. Вто­рое — ее раз­мер, который игно­риру­ется все­ми извес­тны­ми мне опе­раци­онны­ми сис­темами. Поэто­му здесь мож­но писать что угод­но, хоть 0, хоть FFFFFFFFh. Дизас­сем­бле­рам это кры­шу не сры­вает, во вся­ком слу­чае IDA-Pro, Olly и даже при­митив­ный DUMPBIN работа­ют как ни в чем не бывало. А вот про­вер­ка валид­ности раз­мера TLS-таб­лицы может появить­ся в любой момент, так что луч­ше не при­калы­вать­ся и писать то, что нуж­но.

TLS-таб­лица может находить­ся в любой сек­ции с атри­бута­ми [IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE] (нап­ример, в сек­ции дан­ных). Некото­рые лин­керы помеща­ют TLS-таб­лицу в спе­циаль­ную сек­цию .TLS или .TLS$ — это дела­ется из чис­то эсте­тичес­ких сооб­ражений. Сис­темный заг­рузчик не про­веря­ет имя сек­ции. Прав­да, некото­рые упа­ков­щики не обра­баты­вают TLS, рас­положен­ные вне сек­ции .TLS, но это уже их лич­ные проб­лемы. Тем более что ряд упа­ков­щиков, что такое TLS, не зна­ет вооб­ще.

Формат TLS-таблицы для файлов PE32/PE32+
Фор­мат TLS-таб­лицы для фай­лов PE32/PE32+

Фун­кции обратно­го вызова вызыва­ются сис­темным заг­рузчи­ком при ини­циали­зации/тер­минации про­цес­са, а так­же при соз­дании/завер­шении потока. Они име­ют тот же самый про­тотип, что и DllMain:

Про­тотип фун­кций обратно­го вызова
typedef VOID (NTAPI *PIMAGE_TLS_CALLBACK) (
PVOID DllHandle, // Дескриптор модуля
DWORD Reason, // Причина вызова
PVOID Reserved // Зарезервировано
);

Двой­ное сло­во Reason, информи­руя фун­кцию обратно­го вызова, по какой при­чине она была выз­вана, при­нима­ет сле­дующие зна­чения.

Возможные значения параметра Reason
Воз­можные зна­чения парамет­ра Reason

С фун­кци­ями обратно­го вызова все понят­но. Сис­темный заг­рузчик прос­то вызыва­ет их одну за дру­гой, игно­рируя воз­вра­щаемые зна­чения и даже не тре­буя очис­тки аргу­мен­тов из сте­ка, — кра­сота!

А вот с TLS-индексом все чуть‑чуть слож­нее. Двой­ное сло­во по адре­су FS:[2Ch] ука­зыва­ет на TLS-мас­сив, содер­жащий дан­ные локаль­ной памяти потока для всех модулей. Что­бы не воз­никало путани­цы, сис­темный заг­рузчик при ини­циации модуля записы­вает по адре­су Address of Index индекс дан­ного модуля. То есть локаль­ная память потока находит­ся по адре­су FS:[2Ch][index*4]. Теоре­тичес­ки index может при­нимать любые зна­чения, извес­тные толь­ко одной опе­раци­онной сис­теме, но прак­тичес­ки он равен нулю для пер­вого модуля и уве­личи­вает­ся на еди­ницу для всех пос­леду­ющих. Если наш файл не заг­ружа­ет никаких DLL, исполь­зующих TLS, индекс будет равен нулю (с высокой сте­пенью веро­ятности, но без вся­ких гаран­тий). Как же его мож­но исполь­зовать на прак­тике? Самое надеж­ное — записать в сек­цию дан­ных чис­ло типа 12345678h и нат­равить на него индекс. Пос­ле ини­циали­зации при­ложе­ния мы получим что‑то «отличное от» — и дизас­сем­бле­ры это не засекут!

Те­оре­тичес­кую часть будем счи­тать закон­ченной, прис­тупим к прак­тичес­ким заняти­ям.

 

Ручное создание TLS

Для работы с TLS нам необ­ходим ком­пилятор и лин­кер, под­держи­вающий обоз­начен­ную тех­нологию. Недос­татка в таковых нет, хотя нет и пол­ноцен­ной под­дер­жки TLS. Воз­можно, мы захотим прик­рутить TLS к уже упа­кован­ной/зап­ротек­ченной прог­рамме, сле­дова­тель­но, нам жиз­ненно необ­ходимо научить­ся соз­давать его руками. В слу­чае EXE с уби­тыми фик­сами это очень прос­то. С DLL уже будет слож­нее, так как при­дет­ся пра­вить таб­лицу переме­щаемых эле­мен­тов. Тут тоже есть свои хит­рости и трю­ки… но сна­чала — EXE.

Пи­шем прос­тую прог­рамму типа «Hello, world!». Ком­пилиру­ем ее и откры­ваем получен­ный файл в HIEW. Идем в начало сек­ции .data (по Enter перехо­дим в hex-режим, F8 — для вызова PE-заголов­ка, F6 — Object Table, под­водим кур­сор к .data и жмем Enter). Про­пус­каем ини­циали­зиро­ван­ные дан­ные, под­гоняя кур­сор к адре­су .406100h (в дру­гом слу­чае адрес может быть иным), где и пишем такую магичес­кую пос­ледова­тель­ность:

0 61 40 00 | 20 61 40 00 | 30 61 40 00 | 60 61 40 00

Лад­но, на самом деле она никакая не магичес­кая. Пер­вая пара двой­ных слов озна­чает начало/конец бло­ка дан­ных локаль­ной памяти потока, который может находить­ся в любой области памяти, дос­тупной на чте­ние. Третье двой­ное сло­во — адрес двой­ного сло­ва, куда заг­рузчик запишет TLS-индекс. В нашем слу­чае это 00406130h, где мы в HIEW ста­вим 66666666h (что­бы убе­дить­ся, что заг­рузчик дей­стви­тель­но переза­писы­вает это зна­чение). Пос­леднее двой­ное сло­во — ука­затель на таб­лицу фун­кций обратно­го вызова, рас­положен­ную по адре­су 00406160h и содер­жащую ука­затель на единс­твен­ный callback по адре­су 00406190h, за которым сле­дует ноль (ука­зыва­ющий, что дру­гих callback’ов здесь нет и не пред­видит­ся).

Что же каса­ется самого callback’а, то, подог­нав кур­сор к адре­су 00406190h, лег­ким нажати­ем Enter’а мы перехо­дим в режим ассем­бле­ра. А тут пишем DEC D,[00406140], Enter, RET. Пос­ле чего сох­раня­ем изме­нения по F9 и выходим, пред­варитель­но полюбо­вав­шись на резуль­тат нашей работы (см. лис­тинг 2). Ну а кому лень возить­ся с HIEW, может вос­поль­зовать­ся готовым фай­лом hello-TLS.exe.

TLS, соз­данный вруч­ную (hex-дамп)
.00406100: 10 61 40 00-20 61 40 00-30 61 40 00-60 61 40 00 >a@ a@ 0a@ `a@
.00406110: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.00406120: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.00406130: 66 66 66 66-00 00 00 00-00 00 00 00-00 00 00 00
.00406140: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.00406150: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.00406160: 90 61 40 00-00 00 00 00-00 00 00 00-00 00 00 00 Рa@
.00406170: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.00406180: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.00406190: FF 0D 40 61-40 00 C3 00-00 00 00 00-00 00 00 00 d@a@ +

info

Ис­клю­чения, воз­ника­ющие внут­ри TLS callback’ов, давят­ся сис­темой на авто­мате. Но зато отлавли­вают­ся отладчи­ками (той же «Оль­гой»). При этом хакер не понима­ет, как это может работать. Что тут думать — надо жать Shift-F9 для переда­чи управле­ния на точ­ку вхо­да!

Ос­тает­ся толь­ко занес­ти TLS в таб­лицу дирек­торий. В HIEW это дела­ется так: откры­ваем файл, перехо­дим в hex-режим, давим F8 для вызова PE-заголов­ка, а сле­дом — F10 для вызова дирек­тории таб­лиц. Под­гоня­ем кур­сор к TLS и редак­тиру­ем его по F3, вво­дя RVA-адрес начала TLS-таб­лицы (в нашем слу­чае — 6100h) и раз­мер (мож­но брать любой).

Редактирование директории таблиц для «подключения» TLS
Ре­дак­тирова­ние дирек­тории таб­лиц для «под­клю­чения» TLS
 

Боевое крещение

Заг­ружа­ем hello-TLS.exe в отладчик (нап­ример, в «Оль­гу») и ходим по адре­су 00406100h. Мы чет­ко видим, что двой­ное сло­во 66666666h по адре­су 00406130h мис­тичес­ким обра­зом обра­тилось в ноль, зато нулевое двой­ное сло­во по адре­су 00406140h, умень­шив­шись на еди­ницу, прев­ратилось в FFFFFFFFh — резуль­тат записи индекса и вызова callback’а, соот­ветс­твен­но. Это про­изош­ло до того, как мы успе­ли выпол­нить хотя бы одну коман­ду, стоя в точ­ке вхо­да.

Результат работы рукотворного TLS
Ре­зуль­тат работы рукот­ворно­го TLS

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии