Фундаментальные основы хакерства. Ищем переменные разных типов в чужих программах - «Новости» » Самоучитель CSS
Меню
Наши новости
Учебник CSS

Невозможно отучить людей изучать самые ненужные предметы.

Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3

Надо знать обо всем понемножку, но все о немногом.

Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы

Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)

Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода

Самоучитель CSS

Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5

Новости

Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости

Справочник CSS

Справочник от А до Я
HTML, CSS, JavaScript

Афоризмы

Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы

Видео Уроки


Наш опрос



Наши новости

       
4-09-2024, 20:47
Фундаментальные основы хакерства. Ищем переменные разных типов в чужих программах - «Новости»
Рейтинг:
Категория: Новости

стра­нице авто­ра.

 

Идентификация локальных стековых переменных


Ло­каль­ные перемен­ные раз­меща­ются в сте­ке (его так­же называ­ют авто­мати­чес­кой памятью), а уда­ляет их отту­да вызыва­емая фун­кция, ког­да она завер­шится. Рас­смот­рим под­робнее, как это про­исхо­дит. Сна­чала в стек затяги­вают­ся аргу­мен­ты, переда­ваемые фун­кции (если они есть), а свер­ху на них кла­дет­ся адрес воз­вра­та, помеща­емый туда инс­трук­цией CALL, которая вызыва­ет эту фун­кцию. Получив управле­ние, фун­кция откры­вает кадр сте­ка — сох­раня­ет преж­нее зна­чение регис­тра RBP и уста­нав­лива­ет его рав­ным регис­тру RSP (регистр — ука­затель вер­шины сте­ка). «Выше» (то есть в более млад­ших адре­сах) RBP находит­ся сво­бод­ная область сте­ка, «ниже» — слу­жеб­ные дан­ные (сох­ранен­ный RBP, адрес воз­вра­та) и аргу­мен­ты.


Читайте также - У нас самые низкие цены и самая быстрая скорость накрутки на рынке - накрутка лайков инстаграм по доступным ценам.

Сох­ранность области сте­ка, рас­положен­ной выше ука­зате­ля вер­шины сте­ка (регис­тра RSP), не гаран­тирова­на от затира­ния и иска­жения. Ее бес­пре­пятс­твен­но могут исполь­зовать, нап­ример, обра­бот­чики аппа­рат­ных пре­рыва­ний, вызыва­емые в неп­ред­ска­зуемом мес­те в неп­ред­ска­зуемое вре­мя. Да и исполь­зование сте­ка самой фун­кци­ей (для сох­ранения регис­тров или переда­чи аргу­мен­тов) при­ведет к его иска­жению. Какой выход? При­нуди­тель­но перемес­тить ука­затель вер­шины сте­ка вверх, тем самым заняв дан­ную область сте­ка. Сох­ранность памяти, находя­щей­ся «ниже» RSP, гаран­тиру­ется (име­ется в виду гаран­тиру­ется от неп­редна­мерен­ных иска­жений). Оче­ред­ной вызов инс­трук­ции PUSH занесет дан­ные на вер­шину сте­ка, не затирая локаль­ные перемен­ные.


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


Ме­ханизм раз­мещения локаль­ных перемен­ных в сте­ке

На левой кар­тинке показа­но сос­тояние сте­ка на момент вызова фун­кции. Она откры­вает кадр сте­ка, сох­раняя преж­нее зна­чение регис­тра RBP, и уста­нав­лива­ет его рав­ным RSP. На пра­вой кар­тинке изоб­ражено резер­вирова­ние 0x14 байт сте­ковой памяти под локаль­ные перемен­ные. Резер­вирова­ние дос­тига­ется переме­щени­ем регис­тра RSP «вверх» — в область млад­ших адре­сов. Фак­тичес­ки локаль­ные перемен­ные раз­меща­ются в сте­ке так, как буд­то бы они были туда помеще­ны коман­дой PUSH. При завер­шении работы фун­кция уве­личи­вает зна­чение регис­тра RSP, воз­вра­щая его на преж­нюю позицию и осво­бож­дая тем самым память, занятую локаль­ными перемен­ными. Затем она стя­гива­ет со сте­ка и вос­ста­нав­лива­ет зна­чение RBP, зак­рывая тем самым кадр сте­ка.


 

Адресация локальных переменных


Ад­ресация локаль­ных перемен­ных очень похожа на адре­сацию сте­ковых аргу­мен­тов, толь­ко аргу­мен­ты рас­полага­ются «ниже» RBP, а локаль­ные перемен­ные — «выше». Дру­гими сло­вами, аргу­мен­ты име­ют положи­тель­ные сме­щения отно­ситель­но RBP, а локаль­ные перемен­ные — отри­цатель­ные, поэто­му их очень лег­ко отли­чить друг от дру­га. Нап­ример, [RBP+xxx] — аргу­мент, а [RBP-xxx] — локаль­ная перемен­ная.


Ре­гистр — ука­затель кад­ра сте­ка слу­жит как бы барь­ером: по одну сто­рону от него аргу­мен­ты фун­кции, по дру­гую — локаль­ные перемен­ные.


Ад­ресация локаль­ных перемен­ных

Те­перь понят­но, почему при откры­тии кад­ра сте­ка зна­чение RSP копиру­ется в RBP, ина­че адре­сация локаль­ных перемен­ных и аргу­мен­тов зна­читель­но усложни­лась бы, а раз­работ­чики ком­пилято­ров (как это ни стран­но) тоже люди и не хотят без нуж­ды осложнять себе жизнь. Впро­чем, опти­мизи­рующие ком­пилято­ры уме­ют адре­совать локаль­ные перемен­ные и аргу­мен­ты непос­редс­твен­но через RSP, осво­бож­дая регистр RBP для более важ­ных целей.


 

Детали технической реализации


Су­щес­тву­ет мно­жес­тво вари­антов реали­зации выделе­ния и осво­бож­дения памяти под локаль­ные перемен­ные. Казалось бы, чем пло­хо оче­вид­ное SUB RSP,xxx на вхо­де и ADD RSP, xxx на выходе? А вот некото­рые ком­пилято­ры в стрем­лении отли­чать­ся ото всех осталь­ных резер­виру­ют память не умень­шени­ем, а уве­личе­нием RSP... Да, на отри­цатель­ное чис­ло, которое по умол­чанию боль­шинс­твом дизас­сем­бле­ров отоб­ража­ется как очень боль­шое положи­тель­ное. Опти­мизи­рующие ком­пилято­ры при отво­де неболь­шого количес­тва памяти изме­няют SUB на PUSH reg, что на нес­коль­ко бай­тов короче. Пос­леднее соз­дает оче­вид­ные проб­лемы иден­тифика­ции: поп­робуй раз­берись, то ли перед нами сох­ранение регис­тров в сте­ке, то ли переда­ча аргу­мен­тов, то ли резер­вирова­ние памяти для локаль­ных перемен­ных (под­робнее об этом см. раз­дел «Иден­тифика­ция механиз­ма выделе­ния памяти»).


Ал­горитм осво­бож­дения памяти так­же неод­нозна­чен. Помимо уве­личе­ния регис­тра ука­зате­ля вер­шины сте­ка инс­трук­цией ADD RSP, xxx (или в осо­бо извра­щен­ных ком­пилято­рах уве­личе­ния его на отри­цатель­ное чис­ло), час­то встре­чает­ся конс­трук­ция MOV RSP, RBP. Мы ведь пом­ним, что при откры­тии кад­ра сте­ка RSP копиро­вал­ся в RBP, а сам RBP в про­цес­се исполне­ния фун­кции не изме­нял­ся. Наконец, память может быть осво­бож­дена инс­трук­цией POP, вытал­кива­ющей локаль­ные перемен­ные одну за дру­гой в какой‑нибудь ненуж­ный регистр (понят­ное дело, такой спо­соб оправды­вает себя лишь на неболь­шом количес­тве локаль­ных перемен­ных).


На­ибо­лее рас­простра­нен­ные вари­анты реали­зации резер­вирова­ния памяти под локаль­ные перемен­ные и ее осво­бож­дение 

Идентификация механизма выделения памяти


Вы­деле­ние памяти инс­трук­циями SUB и ADD неп­ротиво­речи­во и всег­да интер­пре­тиру­ется однознач­но. Если же оно выпол­няет­ся коман­дой PUSH, а осво­бож­дение — POP, эта конс­трук­ция ста­новит­ся неот­личима от прос­того осво­бож­дения/сох­ранения регис­тров в сте­ке. Ситу­ация серь­езно осложня­ется тем, что в фун­кции при­сутс­тву­ют и нас­тоящие коман­ды сох­ранения регис­тров, сли­ваясь с коман­дами выделе­ния памяти. Как узнать, сколь­ко бай­тов резер­виру­ется для локаль­ных перемен­ных и резер­виру­ются ли они вооб­ще (может, в фун­кции локаль­ных перемен­ных и нет вов­се)?


От­ветить на этот воп­рос поз­воля­ет поиск обра­щений к ячей­кам памяти, лежащих «выше» регис­тра RBP, то есть с отри­цатель­ными отно­ситель­ными сме­щени­ями. Рас­смот­рим два при­мера.


Фундаментальные основы хакерства. Ищем переменные разных типов в чужих программах - «Новости»

В левом из них никако­го обра­щения к локаль­ным перемен­ным не про­исхо­дит вооб­ще, а в пра­вом наличес­тву­ет конс­трук­ция MOV [RBP-4],0x666, копиру­ющая зна­чение 0x666 в локаль­ную перемен­ную var_4. А раз есть локаль­ная перемен­ная, для нее кем‑то дол­жна быть выделе­на память. Пос­коль­ку инс­трук­ций SUB RSP, xxx и ADD RSP, xxx в теле фун­кций не наб­люда­ется, подоз­рение пада­ет на PUSH RCX, так как сох­ранен­ное содер­жимое регис­тра RCX рас­полага­ется в сте­ке на четыре бай­та «выше» RBP. В дан­ном слу­чае подоз­рева­ется лишь одна коман­да — PUSH RCX, пос­коль­ку PUSH RBP на роль «резер­ватора» не тянет. Но как быть, если подоз­рева­емых нес­коль­ко?


Оп­ределить количес­тво выделен­ной памяти мож­но по сме­щению самой «высокой» локаль­ной перемен­ной, которую уда­ется обна­ружить в теле фун­кции. То есть, отыс­кав все выраже­ния типа [RBP-xxx], выберем наиболь­шее сме­щение «xxx» — в общем слу­чае оно рав­но количес­тву бай­тов выделен­ной под локаль­ные перемен­ные памяти. В час­тнос­тях же встре­чают­ся объ­явленные, но не исполь­зуемые локаль­ные перемен­ные. Им выделя­ется память (хотя опти­мизи­рующие ком­пилято­ры прос­то выкиды­вают такие перемен­ные за ненадоб­ностью), но ни одно­го обра­щения к ним не про­исхо­дит, и опи­сан­ный выше алго­ритм под­сче­та объ­ема резер­виру­емой памяти дает занижен­ный резуль­тат. Впро­чем, эта ошиб­ка никак не ска­зыва­ется на резуль­татах ана­лиза прог­раммы.


Инициализация локальных переменных

Су­щес­тву­ет два спо­соба ини­циали­зации локаль­ных перемен­ных: прис­воение необ­ходимо­го зна­чения инс­трук­цией MOV (нап­ример, MOV [RBP-04], 0x666) и непос­редс­твен­ное затал­кивание зна­чения в стек инс­трук­цией PUSH (нап­ример, PUSH 0x777). Пос­леднее поз­воля­ет выгод­но ком­биниро­вать выделе­ние памяти под локаль­ные перемен­ные с их ини­циали­заци­ей (разуме­ется, толь­ко в том слу­чае, если этих перемен­ных нем­ного).


По­пуляр­ные ком­пилято­ры в подав­ляющем боль­шинс­тве слу­чаев выпол­няют опе­рацию ини­циали­зации с помощью MOV, а PUSH более харак­терен для ассем­блер­ных извра­щений, которые встре­чают­ся, нап­ример, в защитах, име­ющих задачу сбить хакеров с тол­ку. Ну, если такой при­ем и сму­тит хакера, то толь­ко начина­юще­го.


Размещение массивов и структур

Мас­сивы и струк­туры раз­меща­ются в сте­ке пос­ледова­тель­но в смеж­ных ячей­ках памяти, при этом мень­ший индекс мас­сива (эле­мент струк­туры) лежит по мень­шему адре­су, но — вни­мание — адре­сует­ся боль­шим модулем сме­щения отно­ситель­но регис­тра ука­зате­ля кад­ра сте­ка. Это не покажет­ся уди­витель­ным, если вспом­нить, что локаль­ные перемен­ные адре­суют­ся отри­цатель­ными сме­щени­ями, сле­дова­тель­но, [RBP-0x4][RBP-0x10].


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


Выравнивание в стеке

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


Лег­ко доказать, что, если млад­ший бит равен нулю, чис­ло чет­ное. Что­бы быть уве­рен­ным, что зна­чение ука­зате­ля вер­шины сте­ка делит­ся на два без остатка, дос­таточ­но лишь сбро­сить его млад­ший бит. Сбро­сив два бита, мы получим зна­чение, заведо­мо крат­ное четырем, три — вось­ми и так далее.


Сбра­сыва­ет биты в подав­ляющем боль­шинс­тве слу­чаев инс­трук­ция AND. Нап­ример, AND RSP, FFFFFFF0 дела­ет RSP крат­ным шес­тнад­цати. Как было получе­но это зна­чение? Перево­дим 0xFFFFFFF0 в дво­ичный вид, получа­ем 11111111 11111111 11111111 11110000. Видишь четыре нуля на кон­це? Зна­чит, четыре млад­ших бита любого чис­ла будут мас­кирова­ны и оно раз­делит­ся без остатка на 24 = 16.


Хо­тя с локаль­ными перемен­ными мы уже неод­нократ­но встре­чались при изу­чении прош­лых при­меров, не помеша­ет сде­лать это еще один раз:


local_vars_identified
#include <stdlib.h>int MyFunc(int a, int b) { int c; // Локальная переменная типа int char x[50]; // Массив (демонстрирует схему размещения массивов в памяти) c = a + b; // Заносим в сумму аргументов и b _itoa_s(c, &x[0], sizeof(x), 0x10); // Переводим сумму и в строку printf("%x == %s == ", c, &x[0]); // Выводим строку на экран return c;}int main() { // Объявляем локальные переменные и для того, чтобы // продемонстрировать механизм их инициализации компилятором. // Такие извращения понадобились для того, чтобы запретить // оптимизирующему компилятору помещать локальную переменную // в регистр (см. «Идентификация регистровых переменных») int a = 0x666; int b = 0x777; int c[1]; // Так как функции printf передается указатель на с, // а указатель на регистр быть передан не может, // компилятор вынужден оставить переменную в памяти c[0] = MyFunc(a, b); printf("%xn", &c[0]); return 0;}

Ре­зуль­тат выпол­нения прог­раммы local_vars_identified показан на кар­тинке ниже.


Об­рати вни­мание, так как третье зна­чение в выводе — адрес, при каж­дом запус­ке при­ложе­ния его зна­чение будет менять­ся

Ре­зуль­тат ком­пиляции ком­пилято­ром Microsoft Visual C++ 2019 в режиме реаль­ного вре­мени с отклю­чен­ной опти­миза­цией дол­жен выг­лядеть так:



int MyFunc(int, int) proc near
Value
= dword ptr -58h
DstBuf
= byte ptr -50h
var_18
= qword ptr -18h



Ло­каль­ные перемен­ные рас­полага­ются по отри­цатель­ному сме­щению отно­ситель­но RBP, а аргу­мен­ты фун­кции — по положи­тель­ному. Выше мы обсужда­ли, что боль­шинс­тво ком­пилято­ров (в том чис­ле Visual C++) опти­мизи­руют код, осво­бож­дая регистр RBP, явля­ющий­ся ука­зате­лем кад­ра сте­ка, что­бы он исполнял роль допол­нитель­ного регис­тра обще­го наз­начения. При этом фун­кцию RBP начина­ет выпол­нять RSP (ука­затель на вер­шину сте­ка). Заметь так­же: чем выше рас­положе­на перемен­ная, тем боль­ше модуль ее сме­щения.


arg_0= dword ptr 8
arg_8= dword ptr 10h
Инициализируем аргументы в памяти, загружая их значения из регистров
mov [rsp+arg_8], edx
О том, что это аргументы, а не нечто иное, говорит их положительное смещение относительно регистра RBP
mov [rsp+arg_0], ecx
Уменьшаем значение RSP на 0x78, резервируя 0x78 байт под локальные переменные
sub rsp, 78h
mov rax, cs:__security_cookie
xor rax, rsp
mov [rsp+78h+var_18], rax
Вновь копируем аргументы в регистры для удобства последующих операций
mov eax, [rsp+78h+arg_8]
mov ecx, [rsp+78h+arg_0]
Складываем значения аргументов
add ecx, eax
mov eax, ecx

А вот и пер­вая локаль­ная перемен­ная! На то, что это имен­но она и есть, ука­зыва­ет ее отри­цатель­ное сме­щение отно­ситель­но регис­тра RBP. Почему отри­цатель­ное? А пос­мотри, как IDA опре­дели­ла Value. По моему лич­ному мне­нию, было бы нам­ного наг­ляднее, если бы отри­цатель­ные сме­щения локаль­ных перемен­ных под­черки­вались более явно.


стра­нице авто­ра. Идентификация локальных стековых переменных Ло­каль­ные перемен­ные раз­меща­ются в сте­ке (его так­же называ­ют авто­мати­чес­кой памятью), а уда­ляет их отту­да вызыва­емая фун­кция, ког­да она завер­шится. Рас­смот­рим под­робнее, как это про­исхо­дит. Сна­чала в стек затяги­вают­ся аргу­мен­ты, переда­ваемые фун­кции (если они есть), а свер­ху на них кла­дет­ся адрес воз­вра­та, помеща­емый туда инс­трук­цией CALL, которая вызыва­ет эту фун­кцию. Получив управле­ние, фун­кция откры­вает кадр сте­ка — сох­раня­ет преж­нее зна­чение регис­тра RBP и уста­нав­лива­ет его рав­ным регис­тру RSP (регистр — ука­затель вер­шины сте­ка). «Выше» (то есть в более млад­ших адре­сах) RBP находит­ся сво­бод­ная область сте­ка, «ниже» — слу­жеб­ные дан­ные (сох­ранен­ный RBP, адрес воз­вра­та) и аргу­мен­ты. Читайте также - У нас самые низкие цены и самая быстрая скорость накрутки на рынке - накрутка лайков инстаграм по доступным ценам. Сох­ранность области сте­ка, рас­положен­ной выше ука­зате­ля вер­шины сте­ка (регис­тра RSP), не гаран­тирова­на от затира­ния и иска­жения. Ее бес­пре­пятс­твен­но могут исполь­зовать, нап­ример, обра­бот­чики аппа­рат­ных пре­рыва­ний, вызыва­емые в неп­ред­ска­зуемом мес­те в неп­ред­ска­зуемое вре­мя. Да и исполь­зование сте­ка самой фун­кци­ей (для сох­ранения регис­тров или переда­чи аргу­мен­тов) при­ведет к его иска­жению. Какой выход? При­нуди­тель­но перемес­тить ука­затель вер­шины сте­ка вверх, тем самым заняв дан­ную область сте­ка. Сох­ранность памяти, находя­щей­ся «ниже» RSP, гаран­тиру­ется (име­ется в виду гаран­тиру­ется от неп­редна­мерен­ных иска­жений). Оче­ред­ной вызов инс­трук­ции PUSH занесет дан­ные на вер­шину сте­ка, не затирая локаль­ные перемен­ные. По окон­чании работы фун­кция обя­зана вер­нуть RSP на преж­нее мес­то, ина­че инс­трук­ция RET сни­мет со сте­ка отнюдь не адрес воз­вра­та, а вооб­ще невесть что (зна­чение самой «вер­хней» локаль­ной перемен­ной) и передаст управле­ние «в кос­мос». Ме­ханизм раз­мещения локаль­ных перемен­ных в сте­кеНа левой кар­тинке показа­но сос­тояние сте­ка на момент вызова фун­кции. Она откры­вает кадр сте­ка, сох­раняя преж­нее зна­чение регис­тра RBP, и уста­нав­лива­ет его рав­ным RSP. На пра­вой кар­тинке изоб­ражено резер­вирова­ние 0x14 байт сте­ковой памяти под локаль­ные перемен­ные. Резер­вирова­ние дос­тига­ется переме­щени­ем регис­тра RSP «вверх» — в область млад­ших адре­сов. Фак­тичес­ки локаль­ные перемен­ные раз­меща­ются в сте­ке так, как буд­то бы они были туда помеще­ны коман­дой PUSH. При завер­шении работы фун­кция уве­личи­вает зна­чение регис­тра RSP, воз­вра­щая его на преж­нюю позицию и осво­бож­дая тем самым память, занятую локаль­ными перемен­ными. Затем она стя­гива­ет со сте­ка и вос­ста­нав­лива­ет зна­чение RBP, зак­рывая тем самым кадр сте­ка. Адресация локальных переменных Ад­ресация локаль­ных перемен­ных очень похожа на адре­сацию сте­ковых аргу­мен­тов, толь­ко аргу­мен­ты рас­полага­ются «ниже» RBP, а локаль­ные перемен­ные — «выше». Дру­гими сло­вами, аргу­мен­ты име­ют положи­тель­ные сме­щения отно­ситель­но RBP, а локаль­ные перемен­ные — отри­цатель­ные, поэто­му их очень лег­ко отли­чить друг от дру­га. Нап­ример, _

Теги: CSS

Просмотров: 524
Комментариев: 0:   4-09-2024, 20:47
Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь. Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

 
Еще новости по теме:



Другие новости по теме: