main.cpp
известный всем код:#include <stdio.h> void victimFunction() { char text[80]; printf("Enter your text: "); scanf("%s", text); printf("Your text is '%s'", text); } int main() { victimFunction(); return 0; }Код - практически классика жанра. Под строку
text
на стеке выделяется буфер в 80 байтов. Если этот буфер переполнить слишком длинной строкой - эта строка затрет адрес возврата на стеке и управление из функции victimFunction()
вернется не в main()
, а "куда надо" ©. "Все очевидно! Любой поймет!" - думаю я. "Какой же я молодец!" - думаю я.Любуясь простотой творения рук своих, я уже прикидываю что и как я буду с важным видом показывать и объяснять. Запускаю компиляцию в Release Solution Configuration... и тут прилетает маленькая, но жестокая птичка обломинго. Disassembly показывает:
#include <stdio.h> void victimFunction() { char text[80]; printf("Enter your text: "); scanf("%s", text); printf("Your text is '%s'", text); } int main() { 01261000 push ebp 01261001 mov ebp,esp 01261003 sub esp,54h 01261006 mov eax,dword ptr [___security_cookie (1263000h)] 0126100B xor eax,ebp 0126100D mov dword ptr [ebp-4],eax 01261010 push esi victimFunction(); 01261011 mov esi,dword ptr [__imp__printf (126209Ch)] 01261017 push offset string "Enter your text: " (12620F4h) 0126101C call esi 0126101E lea eax,[ebp-54h] 01261021 push eax 01261022 push offset string "%s" (1262108h) 01261027 call dword ptr [__imp__scanf (12620A4h)] 0126102D lea ecx,[ebp-54h] 01261030 push ecx 01261031 push offset string "Your text is '%s'" (126210Ch) 01261036 call esi return 0; } 01261038 mov ecx,dword ptr [ebp-4] 0126103B add esp,14h 0126103E xor ecx,ebp 01261040 xor eax,eax 01261042 pop esi 01261043 call __security_check_cookie (126104Ch) 01261048 mov esp,ebp 0126104A pop ebp 0126104B retТо есть во-первых компилятор Visual C++ начиная ещё по-моему с 2003-го по умолчанию считает
/GS
, а значит автоматически применяет защиту стека на основе canary word (видите в коде вызовы security_*_cookie
? - это оно!). Во вторых victimFunction()
вызывается единожды, а значит отличный кандидат что бы стать inline в процессе оптимизации кода. Ну она собственно и становится inline.
В результате этот код для простой демонстрации переполнения буфера получается явно непригодным. Почему я не вспомнил про эти элементарные в общем-то вещи до того как скомпилировал код? Наверное какое-то временное помутнение рассудка (ну или я просто дебил, но развивать эту тему как-то не особенно хочется - поэтому лучше пусть будет помутнение :)).
Ладно, постепенно у меня в голове проясняется. В настройках проекта отключаю
В результате получается вполне годный для демонстрации простого переполнения код:
#include <stdio.h> void victimFunction() { 010C1000 push ebp 010C1001 mov ebp,esp 010C1003 sub esp,50h char text[80]; printf("Enter your text: "); 010C1006 push offset ___xi_z+30h (10C20F4h) 010C100B call dword ptr [__imp__printf (10C209Ch)] 010C1011 add esp,4 scanf("%s", text); 010C1014 lea eax,[text] 010C1017 push eax 010C1018 push offset ___xi_z+44h (10C2108h) 010C101D call dword ptr [__imp__scanf (10C20A4h)] 010C1023 add esp,8 printf("Your text is '%s'", text); 010C1026 lea ecx,[text] 010C1029 push ecx 010C102A push offset ___xi_z+48h (10C210Ch) 010C102F call dword ptr [__imp__printf (10C209Ch)] 010C1035 add esp,8 } 010C1038 mov esp,ebp 010C103A pop ebp 010C103B ret int main() { 010C1040 push ebp 010C1041 mov ebp,esp victimFunction(); 010C1043 call victimFunction (10C1000h) return 0; 010C1048 xor eax,eax } 010C104A pop ebp 010C104B retЭто я к чему все пишу? Да ни к чему, просто так :) Не отключайте
/GS
, а то придет бабайка-переполняйка, вирусов свежих принесёт - вот!
No comments:
Post a Comment