Категория > Новости > Фундаментальные основы хакерства. Изучаем условные переходы в обработке современными компиляторами - «Новости»
Фундаментальные основы хакерства. Изучаем условные переходы в обработке современными компиляторами - «Новости»26-05-2022, 00:02. Автор: Jacobson |
|
странице автора. Сегодняшняя статья — продолжение предыдущей, если ее не прочитать, разобраться в коде будет трудновато. Что ж, продолжим наш заплыв в океан условий, ветвлений, отношений и компромиссов. Идентификация тернарного оператораКонструкция #include <iostream>int main() {int a = -2;int b = 2; a = (a > 0)? 1 :-1; // Тернарный оператор if (b > 0) // Ветвлениеb = 1; elseb = -1;std::cout <<"a + b = " << a+ b;}Если пропустить эту программу сквозь компилятор 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_18= dword ptr -18hvar_14= dword ptr -14hvar_10= dword ptr -10hvar_C= dword ptr -0Ch; Резервируем место для локальных переменныхsub rsp, 38h; Инициализация локальных переменных:; var_14 присваиваем -2, компилятор выразил отрицательное число; сверхбольшим положительнымmov [rsp+38h+var_14], 0FFFFFFFEh; var_18 присваиваем 2, здесь все прямолинейноmov [rsp+38h+var_18], 2; Сравниваем переменную var_14 с 0cmp [rsp+38h+var_14], 0; Переход по метке, если в результате предыдущего сравнения первый операнд меньше второго или равен емуjle short loc_140001025 ; В нашем случае все сходится, делаем прыжокmov [rsp+38h+var_10], 1jmp short loc_14000102D; --------------------------------------loc_140001025:; CODE XREF: main+19↑j; Делаем переход сюдаmov [rsp+38h+var_10], 0FFFFFFFFh ; var_10 присваиваем -1loc_14000102D:; CODE XREF: main+23↑jmov eax, [rsp+38h+var_10]mov [rsp+38h+var_14], eax ; var_14 = -1; Сравниваем var_18 с 0, припоминаем или прокручиваем экран дизассемблера вверх,; в var_18 помещено значение 2cmp [rsp+38h+var_18], 0; Переход по метке, если в результате предыдущего сравнения первый операнд меньше второго или равен емуjle short loc_140001046 ; В данном случае карты не сходятся, поэтому продолжаем выполнениеmov [rsp+38h+var_18], 1 ; Переменной var_18 присваиваем значение 1jmp short loc_14000104E ; Безусловный переход; --------------------------------------loc_140001046:; CODE XREF: main+3A↑jmov [rsp+38h+var_18], 0FFFFFFFFhloc_14000104E:; CODE XREF: main+44↑j; Прыгаем сюдаmov eax, [rsp+38h+var_18] ; EAX = 1mov ecx, [rsp+38h+var_14] ; ECX = -1add ecx, eax; -1 + 1mov eax, ecxmov [rsp+38h+var_C], eax ; Переменной var_C присваиваем сумму — значение 0; Готовим параметрыlea rdx, _Val; "a + b = "mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::cout ; _Ostr; Вызываем оператор << для вывода строкиcall std::operator<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char const *)mov ecx, [rsp+38h+var_C]mov edx, ecxmov rcx, rax; Вызываем оператор << для вывода числаcall cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(int)xor eax, eax ; Возвращаем 0add rsp, 38h ; Восстанавливаем стекretnmainendpВо дела! Компилятор оба условных оператора скомпилил в одинаковый код! Теперь переведем компилятор в режим максимальной оптимизации с приоритетом по скорости — mainproc nearsub rsp, 28hmov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::cout ; _Ostrcall std::operator<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char const *)mov rcx, raxxor edx, edxcall cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(int)xor eax, eaxadd rsp, 28hretnmainendpЧто за чертовщина?! Здесь не осталось никаких условностей! Даже если включить режим оптимизации с приоритетом размера — А что нам продемонстрирует C++Builder? Сначала без оптимизации: ; int __cdecl main(int argc, const char **argv, const char **envp)public mainmainproc near; DATA XREF: __acrtused+29↑ovar_20= qword ptr -20hvar_18= dword ptr -18hvar_14= dword ptr -14hvar_10= qword ptr -10hvar_8= dword ptr -8var_4= dword ptr -4; Открываем кадр стекаpush rbp; Выделяем память под локальные переменныеsub rsp, 40h; Запоминаем адрес дна стекаlea rbp, [rsp+40h]mov eax, 0FFFFFFFFh ; EAX = -1mov r8d, 1; R8D = 1; Инициализация локальных переменныхmov [rbp+var_4], 0mov [rbp+var_8], ecxmov [rbp+var_10], rdxmov [rbp+var_14], 0FFFFFFFEh ; var_14 = -2mov [rbp+var_18], 2; var_18 = 2mov ecx, [rbp+var_14]; ECX = -2; Сравниваем ECX с 0cmp ecx, 0Ага! Используется команда условной пересылки (за подробностями обратись к предыдущей статье): G значит Greater. Другими словами, данные копируются, если первый операнд больше второго (в таком случае флаги cmovg eax, r8dmov [rbp+var_14], eax ; var_14 = -1; Сравнение var_18 с 0cmp [rbp+var_18], 0; Переход по метке, если в результате предыдущего сравнения первый операнд меньше второго или равен емуjle short loc_4013ED ; В данном случае карты не сходятся (2 > 0), поэтому продолжаем выполнениеmov [rbp+var_18], 1 ; var_18 = 1jmp short loc_4013F4 ; Безусловный прыжок; --------------------------------------loc_4013ED:; CODE XREF: main+42↑jmov [rbp+var_18], 0FFFFFFFFhloc_4013F4:; CODE XREF: main+4B↑j; Безусловный прыжок сюда; Подготовка параметров для вызова оператора выводаlea rcx, a008_free_listlea rdx, aAB; "a + b = "; Вывод строки оператором <<call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc ; std::operator<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char const*)mov r8d, [rbp+var_14] ; R8D = -1add r8d, [rbp+var_18] ; R8D = -1 + 1; Готовим параметры для вызова оператора выводаmov rcx, raxmov edx, r8d; Вывод числа (суммы) оператором <<call _ZNSolsEi; std::ostream::operator<<(int)mov [rbp+var_4], 0mov [rbp+var_20], rax; Возвращаем 0mov eax, [rbp+var_4]; Восстанавливаем стекadd rsp, 40h; Закрываем кадр стекаpop rbpretnmainendpБыло интересно увидеть результат работы C++Builder, в особенности использование команды условной пересылки, которая позволила уменьшить количество условных переходов с двух до одного. За подробностями обратись к подготовленному Visual C++ неоптимизированному дизассемблерному листингу, чтобы увидеть оба условных перехода в действии. Перейти обратно к новости |