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