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

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

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

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

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

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

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

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

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

Новости

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

Справочник CSS

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

Афоризмы

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

Видео Уроки


Видео уроки
Популярные статьи
Наш опрос



Наши новости


РЕКЛАМА


ВАША РЕКЛАМА
ДОБАВИТЬ БАННЕР


29-07-2022, 00:02
Фундаментальные основы хакерства. Находим математические операторы в дизассемблированных программах - «Новости»
Рейтинг:
Категория: Новости

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

 

Идентификация оператора +


В общем слу­чае опе­ратор + тран­сли­рует­ся либо в машин­ную инс­трук­цию ADD, «перема­лыва­ющую» целочис­ленные опе­ран­ды, либо, с уче­том наличия в про­цес­соре под­дер­жки SSE (а без нее про­цес­соры уже дав­ным‑дав­но не выпус­кают­ся), в инс­трук­цию ADDSS, обра­баты­вающую вещес­твен­ные зна­чения оди­нар­ной точ­ности, и ADDSD — двой­ной точ­ности.


Оп­тимизи­рующие ком­пилято­ры могут заменять ADD xxx, 1 более ком­пак­тной коман­дой INC xxx, а конс­трук­цию c = a + b + const тран­сли­ровать в машин­ную инс­трук­цию LEA c, [a + b + const]. Такой трюк поз­воля­ет одним махом скла­дывать нес­коль­ко перемен­ных, воз­вра­тив получен­ную сум­му в любом регис­тре обще­го наз­начения, — необя­затель­но в левом сла­гаемом, как это тре­бует мне­мони­ка коман­ды ADD. Одна­ко LEA не может быть непос­редс­твен­но деком­пилиро­вана в опе­ратор +, пос­коль­ку она исполь­зует­ся не толь­ко для опти­мизи­рован­ного сло­жения (что, в общем‑то, толь­ко побоч­ный про­дукт ее деятель­нос­ти), но и по сво­ему пря­мому наз­начению — для вычис­ления эффектив­ного сме­щения.


Рас­смот­рим при­мер demo_plus, демонс­три­рующий исполь­зование опе­рато­ра + со зна­чени­ями оди­нар­ной точ­ности:


#include <iostream>
int main()
{
float a = 0.7f, b = 1.4f, c;
c = a + b;
std::cout << c << std::endl;
c = c + 0.3f;
std::cout << c << std::endl;
}
Фундаментальные основы хакерства. Находим математические операторы в дизассемблированных программах - «Новости»
Ре­зуль­тат выпол­нения demo_plus

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


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


; int __cdecl main(int argc, const char **argv, const char **envp)
mainproc near; CODE XREF: __scrt_common_main_seh+107↓p
; DATA XREF: .pdаta:ExceptionDir↓o
var_c= dword ptr -18h
var_a= dword ptr -14h
var_b= dword ptr -10h
; Резервируем память для локальных переменных
sub rsp, 38h
; Загружаем в регистр XMM0 значение из сегмента данных только для чтения
movss xmm0, cs:__real@3f333333
; Перекладываем это значение из регистра в переменную var_a
movss [rsp+38h+var_a], xmm0
; Загружаем в регистр следующее по порядку значение
movss xmm0, cs:__real@3fb33333
; Перекладываем его в переменную var_b
movss [rsp+38h+var_b], xmm0
; Первое значение возвращаем в регистр XMM0 из переменной var_a
movss xmm0, [rsp+38h+var_a]
; Складываем содержимое XMM0 со значением переменной var_b
addss xmm0, [rsp+38h+var_b]
; Копируем сумму var_a и var_b в переменную var_c, следовательно, var_c = var_a + var_b
movss [rsp+38h+var_c], xmm0
; Готовим параметры для передачи оператору <<
; Второй слева переменная var_c
movss xmm1, [rsp+38h+var_c]
; Первый слева формат вывода
mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::cout
; Собственно вызов оператора вывода строки
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(float)
; Плюс вывод символа новой строки
lea rdx, std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &)
mov rcx, rax
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> &(*)(std::basic_ostream<char,std::char_traits<char>> &))
; Загружаем в XMM0 значение переменной var_c...
movss xmm0, [rsp+38h+var_c]
; ...прибавляем к этому значению значение из сегмента данных только для чтения
addss xmm0, cs:__real@3e99999a
; Обновляем var_c: var_c = var_c + const
movss [rsp+38h+var_c], xmm0
; Готовим параметры для передачи оператору <<
; Второй слева переменная var_c
movss xmm1, [rsp+38h+var_c]
; Первый слева формат вывода
mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::cout
; Собственно вызов оператора вывода строки
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(float)
; Плюс вывод символа новой строки
lea rdx, std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &)
mov rcx, rax
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> &(*)(std::basic_ostream<char,std::char_traits<char>> &))
xor eax, eax
add rsp, 38h
retn
mainendp

А теперь пос­мотрим, как будет выг­лядеть тот же самый при­мер, ском­пилиро­ван­ный с клю­чом /Ox (мак­сималь­ная опти­миза­ция):


mainproc near; CODE XREF: __scrt_common_main_seh+107↓p
; DATA XREF: .pdаta:ExceptionDir↓o
sub rsp, 28h
; Как ловко! Компилятор подсчитал сумму во время компиляции
; и подставил ее непосредственно для вывода
; С чего бы ему идти на такие хитрости без последствий?
; Значения-то представляют собой константы,
; которые хранятся в сегменте данных только для чтения
movss xmm1, cs:__real@40066666
mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::cout
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(float)
mov rcx, rax
lea rdx, std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &)
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> &(*)(std::basic_ostream<char,std::char_traits<char>> &))
; Второе для вывода значение с ним такая же история, как с первым значением
movss xmm1, cs:__real@40199999
mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::cout
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(float)
mov rcx, rax
lea rdx, std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &)
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> &(*)(std::basic_ostream<char,std::char_traits<char>> &))
xor eax, eax
add rsp, 28h
retn
mainendp

В опти­мизи­рован­ном вари­анте нет ни намека на сло­жение или дру­гие ариф­метичес­кие опе­рации! С воз­веден­ным фла­гом /O2 (мак­сималь­ная опти­миза­ция по ско­рос­ти) ком­пилятор соз­дает точ­но такой же код.


Embarcadero C++Builder генери­рует похожий код, а в слу­чае опти­миза­ции — еще хуже. Поэто­му при­водить резуль­таты его тру­да бес­смыс­ленно — никаких новых «изю­минок» они в себе не несут.


 

Идентификация оператора –


В общем слу­чае опе­ратор – тран­сли­рует­ся либо в машин­ную инс­трук­цию SUB (если опе­ран­ды — целочис­ленные зна­чения), либо в инс­трук­цию SUBSS (если опе­ран­ды — вещес­твен­ные зна­чения оди­нар­ной точ­ности) или в SUBSD (ког­да опе­ран­ды двой­ной точ­ности). Опти­мизи­рующие ком­пилято­ры могут заменять SUB xxx, 1 более ком­пак­тной коман­дой DEC xxx, а конс­трук­цию SUB a, const тран­сли­ровать в ADD a, –const, которая ничуть не ком­пак­тнее и нис­коль­ко не быс­трей (и та и дру­гая укла­дыва­ется в один такт). Одна­ко хозя­ин (ком­пилятор) — барин.


По­кажем это на при­мере demo_minus, демонс­три­рующем исполь­зование опе­рато­ра – со зна­чени­ями двой­ной точ­ности:


#include <iostream>
int main()
{
double a = 3.1, b = 1.6, c;
c = a - b;
std::cout << c << std::endl;
c = c - 10;
std::cout << c << std::endl;
}
Ре­зуль­тат выпол­нения demo_minus

Не­опти­мизи­рован­ный вари­ант будет выг­лядеть приб­лизитель­но так:


mainproc near; CODE XREF: __scrt_common_main_seh+107↓p
; DATA XREF: .pdаta:ExceptionDir↓o
var_c= qword ptr -28h
var_a= qword ptr -20h
var_b= qword ptr -18h
; Резервируем память для локальных переменных
sub rsp, 48h
; Загружаем в регистр XMM0 значение из сегмента данных только для чтения
movsd xmm0, cs:__real@4008cccccccccccd
; Перекладываем это значение из регистра в переменную var_a
movsd [rsp+48h+var_a], xmm0
; Загружаем в регистр, заменяя имеющееся там значение следующим по порядку значением
movsd xmm0, cs:__real@3ff999999999999a
; Перекладываем его в переменную var_b
movsd [rsp+48h+var_b], xmm0
; Из переменной var_a возвращаем значение в регистр XMM0
movsd xmm0, [rsp+48h+var_a]
; Вычитаем из var_a значение переменной var_b, записывая результат в XMM0
subsd xmm0, [rsp+48h+var_b]
; Записываем в var_c разность var_a и var_b:
; var_c = var_a var_b
movsd [rsp+48h+var_c], xmm0
movsd xmm1, [rsp+48h+var_c]
mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::cout
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(double)
lea rdx, std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &)
mov rcx, rax
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))
; Загружаем в регистр XMM0 значение переменной var_c готовим к вычислению
movsd xmm0, [rsp+48h+var_c]
; Вычитаем из var_c значение, взятое из сегмента данных только для чтения
; При этом результат записываем в регистр XMM0
subsd xmm0, cs:__real@4024000000000000
; Обновляем содержимое переменной var_c:
; var_c = var_c const
movsd [rsp+48h+var_c], xmm0
movsd xmm1, [rsp+48h+var_c]
mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::cout
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(double)
lea rdx, std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &)
mov rcx, rax
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))
xor eax, eax
add rsp, 48h
retn
mainendp

А теперь рас­смот­рим опти­мизи­рован­ный вари­ант того же при­мера:


mainproc near; CODE XREF: __scrt_common_main_seh+107↓p
; DATA XREF: .pdаta:ExceptionDir↓o
sub rsp, 28h
; Компилятор подсчитал разность во время трансляции и подготовил ее значение для вывода
movsd xmm1, cs:__real@3ff8000000000000
mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::cout
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(double)
mov rcx, rax
lea rdx, std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &)
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))
; Результат второй разности также подсчитан во время компиляции
movsd xmm1, cs:__real@c021000000000000
mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::cout
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(double)
mov rcx, rax
lea rdx, std::endl<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &)
call cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))
xor eax, eax
add rsp, 28h
retn
mainendp

Embarcadero C++Builder генери­рует прак­тичес­ки иден­тичный код без опти­миза­ции и нам­ного хуже с вклю­чен­ной опти­миза­цией, поэто­му здесь он не рас­смат­рива­ется.


 

Идентификация оператора /


В общем слу­чае опе­ратор / тран­сли­рует­ся либо в машин­ную инс­трук­цию DIV (без­зна­ковое целочис­ленное деление), либо в IDIV (целочис­ленное деление со зна­ком), либо в DIVSS (деление вещес­твен­ных зна­чений оди­нар­ной точ­ности) или DIVSD (деление вещес­твен­ных зна­чений двой­ной точ­ности).


Теги: CSS

Просмотров: 65
Комментариев: 0:   29-07-2022, 00:02
Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь. Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

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



Другие новости по теме:
Комментарии для сайта Cackle