Категория > Новости > Фундаментальные основы хакерства. Разбираем самодеятельность компиляторов при трансляции оператора выбора - «Новости»
Фундаментальные основы хакерства. Разбираем самодеятельность компиляторов при трансляции оператора выбора - «Новости»12-06-2022, 00:01. Автор: Григорий |
странице автора.Ищем операторы switch-case-break в бинарном кодеДля улучшения читабельности программ в язык C был введен оператор множественного выбора — Легко показать, что
Если изобразить это ветвление в виде логического дерева, то образуется характерная «косичка». Трансляция оператора switch в общем случае Казалось бы, идентифицировать Однако в реальной жизни все происходит совсем не так. Компиляторы (даже неоптимизирующие) транслируют #include <stdio.h>int main(){ int a = 0x666; switch (a) { case 0:printf("a == 0");break; case 1:printf("a == 1");break; case 2:printf("a == 2");break; case 0x666:printf("a == 666h");break; default:printf("Default"); }}Вывод приложения switch_casesТеперь посмотрим в IDA на результат дизассемблирования. Дерево распустило ветки во все стороны. МОЖНО сделать однозначный вывод: в дизассемблируемой программе присутствует оператор множественного выбора switch-case mainproc near ; CODE XREF: __scrt_common_main_seh+107↓p; DATA XREF: .pdаta:0000000140004018↓o; Объявляем две локальные переменные,; но почему две, если в исходном коде объявлена только одна?var_18= dword ptr -18hvar_14= dword ptr -14h; Резервируем место для локальных переменныхsub rsp, 38h; Инициализируем локальные переменные:; var_14 присваиваем значение 0х666, следовательно, это переменная amov [rsp+38h+var_14], 666hmov eax, [rsp+38h+var_14]Переменной mov [rsp+38h+var_18], eax; Сравниваем значение var_18 с нулемcmp [rsp+38h+var_18], 0; Если сравнение успешно, переходим в блок кода, выводящий в консоль "a == 0"; Этот код получен трансляцией ветки case 0: printf("a == 0");; Иначе продолжаем выполнениеjzshort loc_140001115; Сравниваем значение var_18 с 1cmp [rsp+38h+var_18], 1; В случае успеха прыгаем внутрь блока кода для вывода "a == 1"; Этот код получен трансляцией ветки case 1: printf("a == 1");; Иначе продолжаем выполнениеjzshort loc_140001123; Сравниваем значение var_18 с 2cmp [rsp+38h+var_18], 2; В случае равенства выводим "a == 2"; Этот код получен трансляцией ветки case 2: printf("a == 2");; Иначе продолжаем выполнениеjzshort loc_140001131; Сравниваем var_18 и 0x666cmp [rsp+38h+var_18], 666h; Если равно, выводим "a == 666h"; Этот код получен трансляцией ветки case 0x666: printf("a == 666h");jzshort loc_14000113FЕсли мы досюда добрались, значит, ни одно условие не сработало, поэтому выполняем дефолтное действие: делаем безусловный переход в блок кода для вывода строчки Default. Этот код получен трансляцией ветки jmp short loc_14000114D; ------------------------------------------------loc_140001115:; CODE XREF: main+19↑j; printf("a == 0");lea rcx, _Format ; "a == 0"call printfА вот этот безусловный переход, выносящий управление за пределы jmp short loc_140001159 ; break; ------------------------------------------------loc_140001123:; CODE XREF: main+20↑j; printf("a == 1");lea rcx, aA1; "a == 1"call printfjmp short loc_140001159 ; break; ------------------------------------------------loc_140001131:; CODE XREF: main+27↑j; printf("a == 2");lea rcx, aA2; "a == 2"call printfjmp short loc_140001159 ; break; ------------------------------------------------loc_14000113F:; CODE XREF: main+31↑j; printf("a == 666h");lea rcx, aA666h ; "a == 666h"call printfjmp short loc_140001159 ; break; ------------------------------------------------loc_14000114D:; CODE XREF: main+33↑j; printf("Default");lea rcx, aDefault ; "Default"call printfloc_140001159: ; Конец SWITCH ; CODE XREF: main+41↑j; main+4F↑j ...; Возвращаем 0xor eax, eax; Восстанавливаем стекadd rsp, 38hretnmainendpВыглядит довольно прямолинейно. Дизассемблерный листинг можно условно разделить на две части: первая часть – сам оператор выбора, откуда каждое условие передает управление во вторую часть — конкретную ветку, соответствующую этому условию. Для сравнения взглянем, какой код построит C++Builder 10 на основе этой же программы: public mainmainproc near ; DATA XREF: __acrtused+29↑o; Как много локальных переменных!var_38= dword ptr -38hvar_34= dword ptr -34hvar_30= dword ptr -30hvar_2C= dword ptr -2Chvar_28= dword ptr -28hvar_24= dword ptr -24hvar_20= dword ptr -20hvar_1C= dword ptr -1Chvar_18= dword ptr -18hvar_14= dword ptr -14hvar_10= qword ptr -10hvar_8= dword ptr -8var_4= dword ptr -4; Открываем кадр стекаpush rbp; Резервируем место для локальных переменныхsub rsp, 60h; В RBP сохраняем указатель на дно стекаlea rbp, [rsp+60h]; Инициализируем локальные переменные:mov [rbp+var_4], 0mov [rbp+var_8], ecxmov [rbp+var_10], rdx; var_14 присваиваем значение 0х666, следовательно, это переменная amov [rbp+var_14], 666h; В ECX помещаем значение var_14mov ecx, [rbp+var_14]; Следующим элегантным образом сравниваем значение var_14 с нулемtest ecx, ecxКоманда mov [rbp+var_18], ecxПерейти обратно к новости |