Категория > Новости > Фундаментальные основы хакерства. Находим математические операторы в дизассемблированных программах - «Новости»
Фундаментальные основы хакерства. Находим математические операторы в дизассемблированных программах - «Новости»29-07-2022, 00:02. Автор: Аникита |
странице автора.Идентификация оператора +В общем случае оператор + транслируется либо в машинную инструкцию Оптимизирующие компиляторы могут заменять Рассмотрим пример 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;}![]() Прежние версии компиляторов по умолчанию позволяли использовать неинициализированные переменные. Теперь же это не так, и компилятор обрывает трансляцию программы, встретив неинициализированную переменную. Это лишает его возможности загружать значение откуда угодно, тем самым защищая программиста от фатальных ошибок, которые могут произойти, если он забудет присвоить значение объявленной переменной. Результат трансляции этого примера компилятором 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↓ovar_c= dword ptr -18hvar_a= dword ptr -14hvar_b= dword ptr -10h; Резервируем память для локальных переменныхsub rsp, 38h; Загружаем в регистр XMM0 значение из сегмента данных только для чтенияmovss xmm0, cs:__real@3f333333; Перекладываем это значение из регистра в переменную var_amovss [rsp+38h+var_a], xmm0; Загружаем в регистр следующее по порядку значениеmovss xmm0, cs:__real@3fb33333; Перекладываем его в переменную var_bmovss [rsp+38h+var_b], xmm0; Первое значение возвращаем в регистр XMM0 из переменной var_amovss xmm0, [rsp+38h+var_a]; Складываем содержимое XMM0 со значением переменной var_baddss xmm0, [rsp+38h+var_b]; Копируем сумму var_a и var_b в переменную var_c, следовательно, var_c = var_a + var_bmovss [rsp+38h+var_c], xmm0; Готовим параметры для передачи оператору <<; Второй слева — переменная var_cmovss 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, raxcall 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 + constmovss [rsp+38h+var_c], xmm0; Готовим параметры для передачи оператору <<; Второй слева — переменная var_cmovss 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, raxcall 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, eaxadd rsp, 38hretnmainendpА теперь посмотрим, как будет выглядеть тот же самый пример, скомпилированный с ключом mainproc near; CODE XREF: __scrt_common_main_seh+107↓p; DATA XREF: .pdаta:ExceptionDir↓osub rsp, 28h; Как ловко! Компилятор подсчитал сумму во время компиляции; и подставил ее непосредственно для вывода; С чего бы ему идти на такие хитрости без последствий?; Значения-то представляют собой константы,; которые хранятся в сегменте данных только для чтенияmovss xmm1, cs:__real@40066666mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::coutcall cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(float)mov rcx, raxlea 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@40199999mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::coutcall cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(float)mov rcx, raxlea 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, eaxadd rsp, 28hretnmainendpВ оптимизированном варианте нет ни намека на сложение или другие арифметические операции! С возведенным флагом Embarcadero C++Builder генерирует похожий код, а в случае оптимизации — еще хуже. Поэтому приводить результаты его труда бессмысленно — никаких новых «изюминок» они в себе не несут. Идентификация оператора –В общем случае оператор – транслируется либо в машинную инструкцию Покажем это на примере 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;}![]() Неоптимизированный вариант будет выглядеть приблизительно так: mainproc near; CODE XREF: __scrt_common_main_seh+107↓p; DATA XREF: .pdаta:ExceptionDir↓ovar_c= qword ptr -28hvar_a= qword ptr -20hvar_b= qword ptr -18h; Резервируем память для локальных переменныхsub rsp, 48h; Загружаем в регистр XMM0 значение из сегмента данных только для чтенияmovsd xmm0, cs:__real@4008cccccccccccd; Перекладываем это значение из регистра в переменную var_amovsd [rsp+48h+var_a], xmm0; Загружаем в регистр, заменяя имеющееся там значение следующим по порядку значениемmovsd xmm0, cs:__real@3ff999999999999a; Перекладываем его в переменную var_bmovsd [rsp+48h+var_b], xmm0; Из переменной var_a возвращаем значение в регистр XMM0movsd xmm0, [rsp+48h+var_a]; Вычитаем из var_a значение переменной var_b, записывая результат в XMM0subsd xmm0, [rsp+48h+var_b]; Записываем в var_c разность var_a и var_b:; var_c = var_a — var_bmovsd [rsp+48h+var_c], xmm0movsd xmm1, [rsp+48h+var_c]mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::coutcall 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, raxcall 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 значение, взятое из сегмента данных только для чтения; При этом результат записываем в регистр XMM0subsd xmm0, cs:__real@4024000000000000; Обновляем содержимое переменной var_c:; var_c = var_c — constmovsd [rsp+48h+var_c], xmm0movsd xmm1, [rsp+48h+var_c]mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::coutcall 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, raxcall 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, eaxadd rsp, 48hretnmainendpА теперь рассмотрим оптимизированный вариант того же примера: mainproc near; CODE XREF: __scrt_common_main_seh+107↓p; DATA XREF: .pdаta:ExceptionDir↓osub rsp, 28h; Компилятор подсчитал разность во время трансляции и подготовил ее значение для выводаmovsd xmm1, cs:__real@3ff8000000000000mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::coutcall cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(double)mov rcx, raxlea 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@c021000000000000mov rcx, cs:std::basic_ostream<char,std::char_traits<char>> std::coutcall cs:std::basic_ostream<char,std::char_traits<char>>::operator<<(double)mov rcx, raxlea 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, eaxadd rsp, 28hretnmainendpEmbarcadero C++Builder генерирует практически идентичный код без оптимизации и намного хуже с включенной оптимизацией, поэтому здесь он не рассматривается. Идентификация оператора /В общем случае оператор / транслируется либо в машинную инструкцию Перейти обратно к новости |