Ког­да в оче­ред­ной раз на форуме спро­сили, почему уста­нов­ленная точ­ка оста­нова не сра­баты­вает или при­водит прог­рамму к кра­ху, я не выдер­жал и нер­вно зас­тучал по кла­виату­ре, попытав­шись отве­тить на этот воп­рос раз и нав­сегда. Приш­лось соб­рать воеди­но огромное количес­тво раз­рознен­ной инфы по дей­стви­тель­но акту­аль­ной теме!
 

Вокруг INT 03h

Прог­рам­мная точ­ка оста­нова на исполне­ние (software breakpoint on execution) физичес­ки пред­став­ляет собой одно­бай­товую про­цес­сорную инс­трук­цию CCh (INT 03h), внед­ряемую дебаг­гером непос­редс­твен­но в отла­жива­емый код с неиз­бежной переза­писью ори­гиналь­ного содер­жимого. Встре­тив­шись с INT 03h, про­цес­сор генери­рует исклю­чение типа EXCEPTION_BREAKPOINT, перех­ватыва­емое отладчи­ком. Он оста­нав­лива­ет выпол­нение прог­раммы и авто­мати­чес­ки вос­ста­нав­лива­ет содер­жимое бай­та, «испорчен­ного» точ­кой оста­нова.

Имен­но так и пос­тупа­ет «Оль­га» при нажатии кла­виши F2 и SoftICE по F7. Дос­тоинс­тво прог­рам­мных точек оста­нова в том, что их количес­тво огра­ниче­но толь­ко архи­тек­турны­ми осо­бен­ностя­ми отладчи­ка (то есть прак­тичес­ки неог­раничен­но). В то вре­мя как аппа­рат­ных точек оста­нова, под­держи­ваемых про­цес­сором на желез­ном уров­не, все­го четыре.

Не­дос­таток же прог­рам­мных точек оста­нова в том, что они тре­буют модифи­кации отла­жива­емо­го кода, а это лег­ко обна­ружи­вает лома­емая прог­рамма три­виаль­ным под­сче­том кон­троль­ной сум­мы. При­чем защита может не толь­ко задетек­тить бряк, но и снять его, вос­ста­новив исходное зна­чение «бряк­нутого» бай­та вруч­ную. Бряк, естес­твен­но, не сра­бота­ет, хотя отладчик про­дол­жит под­све­чивать бряк­нутую стро­ку, вво­дя хакера в заб­лужде­ние (отладчик хра­нит спи­сок точек оста­нова внут­ри сво­его тела и не про­веря­ет при­сутс­твие INT 03h в отла­жива­емом коде).

Мно­гие отладчи­ки (в том чис­ле и «Оль­га») уста­нав­лива­ют в точ­ку вхо­да (Entry Point) прог­рам­мный бряк. Его лег­ко обна­ружить из фун­кции DllMain ста­тичес­ки при­лин­кован­ной динами­чес­кой биб­лиоте­ки, воз­вра­тив при­нуди­тель­ный ноль — что озна­чает «ошиб­ка ини­циали­зации» и при­водит к ава­рий­ному завер­шению отла­жива­емо­го при­ложе­ния задол­го до того, как точ­ка вхо­да получит управле­ние.

При­мер, демонс­три­рующий детек­цию отладчи­ка из ста­тичес­ки при­лин­кован­ной DLL
BOOL WINAPI dllmain(
HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved)
{
#define PE_off 0x3C // PE magic word raw offset
#define EP_off 0x28 // Relative Entry Point filed offset
#define SW_BP 0xCC // Software breakpoint opcode
char buf[_MAX_PATH];
DWORD pe_off, ep_off;
BYTE* base_x, *ep_adr;
// Obtain exe base address
GetModuleFileName(0, buf, _MAX_PATH);
// Manual PE-header parsing to find EP value
base_x = (BYTE*) GetModuleHandle(buf);
pe_off = *((DWORD*)(base_x + PE_off));
ep_off = *((DWORD*)(base_x + pe_off + EP_off));
ep_adr = base_x + ep_off; // RVA to VA
// Check EP for software breakpoint (some debuggers set software breakpoint on EP to get control)
if (*ep_adr == SW_BP)
return 0; // 0 means DLL initialization fails
return 1;
}

К сожале­нию, оту­чить «Оль­гу» ста­вить бря­ки в точ­ку вхо­да очень неп­росто (если вооб­ще воз­можно), и при­ходит­ся пус­кать­ся на хит­рости. Откры­ваем лома­емый exe в HIEW и внед­ряем в точ­ку вхо­да двух­бай­товую коман­ду EBh FEh, соот­ветс­тву­ющую машин­ной инс­трук­ции l1: jmp short l1. Это при­водит к зацик­ливанию прог­раммы и дает нам воз­можность при­атта­чить дебаг­гер к отла­жива­емо­му про­цес­су. Как вари­ант, мож­но впен­дюрить INT 03h во вто­рую (третью, чет­вертую) коман­ду от точ­ки вхо­да, запус­тив прог­рамму «вжи­вую» (вне отладчи­ка).

Пос­коль­ку исклю­чение, генери­руемое INT 03h, некому обра­баты­вать, опе­раци­онная сис­тема вып­левыва­ет сооб­щение о кри­тичес­кой ошиб­ке, пред­лагая запус­тить JIT (Just-in-Time) отладчик. В его роли может выс­тупить и «Оль­га» (Options → Just-In-Time Debugging → Make Olly just-in-time debuuger). Кста­ти говоря, подоб­ная тех­ника носит наз­вание Break’n’Enter и доволь­но широко рас­простра­нена. В час­тнос­ти, ее под­держи­вают PE-TOOLS и мно­гие дру­гие хакер­ские ути­литы.

Превращаем «Ольгу» в Just-in-Time-отладчик
Прев­раща­ем «Оль­гу» в Just-in-Time-отладчик

Но вер­немся к нашим баранам. Попыт­ка уста­новить прог­рам­мную точ­ку оста­нова на самомо­дифи­циру­ющий­ся (упа­кован­ный или зашиф­рован­ный) код ведет к кра­ху, при­чем безо вся­кого учас­тия со сто­роны защиты. Наде­юсь, не нуж­но объ­яснять почему? Ну хорошо. Возь­мем три­виаль­ный крип­тор, работа­ющий через byte XOR 66h. Допус­тим, мы уста­нав­лива­ем бряк на коман­ду PUSH EAX (50h). Пос­ле шиф­ровки она прев­раща­ется в 36h. Пред­ста­вим, что поверх 36h уста­нов­лена точ­ка оста­нова — CCh. Тог­да пос­ле рас­шифров­ки (CCh XOR 66h) мы получим AAh (STOSB). Это, естес­твен­но, вызовет крах, так как мы не толь­ко потеря­ли PUSH EAX, но еще и регис­тры ES:EDI смот­рят черт зна­ет куда, вызывая ACCESS VIOLATION!

Вот так срабатывают программные точки останова
Вот так сра­баты­вают прог­рам­мные точ­ки оста­нова

Впро­чем, тут воз­можны детали. Иног­да прог­рамму рас­шифро­выва­ет не прик­ладной код, находя­щий­ся непос­редс­твен­но в лома­емой прог­рамме, а драй­вер защиты, исполня­ющий­ся совер­шенно в дру­гом кон­тек­сте. Кста­ти, о кон­тек­стах.

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

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

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

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

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


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

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

    Подписаться

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