Я немного изменил вашу программу, чтобы сделать ее более наглядной:
#include <iostream>
int main( void )
{
int authentication = 0;
char cUsername[ 10 ];
char cPassword[ 10 ];
std::cout << "Username: ";
std::cin >> cUsername;
std::cout << "Pass: ";
std::cin >> cPassword;
if( std::strcmp( cUsername, "admin" ) == 0 && std::strcmp( cPassword, "adminpass" ) == 0 )
{
authentication = 1;
}
if( authentication )
{
std::cout << "Access granted\n";
std::cout << ( char )authentication;
}
else
{
std::cout << "Wrong username and password\n";
}
return ( 0 );
}
Я скомпилировал его с помощью компилятора командной строки компилятора x64 MS, без оптимизации. Итак, теперь у нас есть исполняемый файл, который мы хотим «взломать». Мы загружаем программу с помощью WinDbg (действительно хороший отладчик) и смотрим на дизассемблирование (обратите внимание, для ясности я предоставил полную информацию об отладке):
00000001`3f1f1710 4883ec68 sub rsp,68h
00000001`3f1f1714 488b0515db0300 mov rax,qword ptr [Prototype_Console!__security_cookie (00000001`3f22f230)]
00000001`3f1f171b 4833c4 xor rax,rsp
00000001`3f1f171e 4889442450 mov qword ptr [rsp+50h],rax
00000001`3f1f1723 c744243800000000 mov dword ptr [rsp+38h],0 // This gives us address of "authentication" on stack.
00000001`3f1f172b 488d156e1c0300 lea rdx,[Prototype_Console!std::_Iosb<int>::end+0x78 (00000001`3f2233a0)]
00000001`3f1f1732 488d0d47f00300 lea rcx,[Prototype_Console!std::cout (00000001`3f230780)]
00000001`3f1f1739 e8fdf9ffff call Prototype_Console!ILT+310(??$?6U?$char_traitsDstdstdYAAEAV?$basic_ostreamDU?$char_traitsDstd (00000001`3f1f113b)
00000001`3f1f173e 488d542428 lea rdx,[rsp+28h] // This gives us address of "cUsername" on stack.
00000001`3f1f1743 488d0df6f00300 lea rcx,[Prototype_Console!std::cin (00000001`3f230840)]
00000001`3f1f174a e823faffff call Prototype_Console!ILT+365(??$?5DU?$char_traitsDstdstdYAAEAV?$basic_istreamDU?$char_traitsDstd (00000001`3f1f1172)
00000001`3f1f174f 488d153e1c0300 lea rdx,[Prototype_Console!std::_Iosb<int>::end+0x6c (00000001`3f223394)]
00000001`3f1f1756 488d0d23f00300 lea rcx,[Prototype_Console!std::cout (00000001`3f230780)]
00000001`3f1f175d e8d9f9ffff call Prototype_Console!ILT+310(??$?6U?$char_traitsDstdstdYAAEAV?$basic_ostreamDU?$char_traitsDstd (00000001`3f1f113b)
00000001`3f1f1762 488d542440 lea rdx,[rsp+40h] // This gives us address of "cPassword" on stack.
00000001`3f1f1767 488d0dd2f00300 lea rcx,[Prototype_Console!std::cin (00000001`3f230840)]
00000001`3f1f176e e8fff9ffff call Prototype_Console!ILT+365(??$?5DU?$char_traitsDstdstdYAAEAV?$basic_istreamDU?$char_traitsDstd (00000001`3f1f1172)
00000001`3f1f1773 488d15321c0300 lea rdx,[Prototype_Console!std::_Iosb<int>::end+0x84 (00000001`3f2233ac)]
00000001`3f1f177a 488d4c2428 lea rcx,[rsp+28h]
00000001`3f1f177f e86c420000 call Prototype_Console!strcmp (00000001`3f1f59f0)
00000001`3f1f1784 85c0 test eax,eax
00000001`3f1f1786 751d jne Prototype_Console!main+0x95 (00000001`3f1f17a5)
00000001`3f1f1788 488d15291c0300 lea rdx,[Prototype_Console!std::_Iosb<int>::end+0x90 (00000001`3f2233b8)]
00000001`3f1f178f 488d4c2440 lea rcx,[rsp+40h]
00000001`3f1f1794 e857420000 call Prototype_Console!strcmp (00000001`3f1f59f0)
00000001`3f1f1799 85c0 test eax,eax
00000001`3f1f179b 7508 jne Prototype_Console!main+0x95 (00000001`3f1f17a5)
00000001`3f1f179d c744243801000000 mov dword ptr [rsp+38h],1
00000001`3f1f17a5 837c243800 cmp dword ptr [rsp+38h],0
00000001`3f1f17aa 7426 je Prototype_Console!main+0xc2 (00000001`3f1f17d2)
00000001`3f1f17ac 488d15151c0300 lea rdx,[Prototype_Console!std::_Iosb<int>::end+0xa0 (00000001`3f2233c8)]
00000001`3f1f17b3 488d0dc6ef0300 lea rcx,[Prototype_Console!std::cout (00000001`3f230780)]
00000001`3f1f17ba e87cf9ffff call Prototype_Console!ILT+310(??$?6U?$char_traitsDstdstdYAAEAV?$basic_ostreamDU?$char_traitsDstd (00000001`3f1f113b)
00000001`3f1f17bf 0fb6542438 movzx edx,byte ptr [rsp+38h]
00000001`3f1f17c4 488d0db5ef0300 lea rcx,[Prototype_Console!std::cout (00000001`3f230780)]
00000001`3f1f17cb e825f9ffff call Prototype_Console!ILT+240(??$?6U?$char_traitsDstdstdYAAEAV?$basic_ostreamDU?$char_traitsDstd (00000001`3f1f10f5)
00000001`3f1f17d0 eb13 jmp Prototype_Console!main+0xd5 (00000001`3f1f17e5)
00000001`3f1f17d2 488d15ff1b0300 lea rdx,[Prototype_Console!std::_Iosb<int>::end+0xb0 (00000001`3f2233d8)]
00000001`3f1f17d9 488d0da0ef0300 lea rcx,[Prototype_Console!std::cout (00000001`3f230780)]
00000001`3f1f17e0 e856f9ffff call Prototype_Console!ILT+310(??$?6U?$char_traitsDstdstdYAAEAV?$basic_ostreamDU?$char_traitsDstd (00000001`3f1f113b)
00000001`3f1f17e5 33c0 xor eax,eax
00000001`3f1f17e7 488b4c2450 mov rcx,qword ptr [rsp+50h]
00000001`3f1f17ec 4833cc xor rcx,rsp
00000001`3f1f17ef e8bc420000 call Prototype_Console!__security_check_cookie (00000001`3f1f5ab0)
00000001`3f1f17f4 4883c468 add rsp,68h
00000001`3f1f17f8 c3 ret
Теперь, когда мы знаем, как работает стек x64, можно приступить к «взлому». RSP
— это указатель стека, стек функций — это адреса выше значения RSP
(стек увеличивается до меньших адресов). Итак, мы видим, что RSP+28h
— это cUsername
, RSP+38h
— это authentication
, а RSP+40h
— это cPassword
, где 28h, 38h и 40h — шестнадцатеричные смещения. Вот небольшое изображение для иллюстрации:
-----> old RSP value // Stack frame of caller of `main` is above, stack frame of main is below
16 bytes of
"cPassword"
+40h
8 bytes of "authentication"
+38h
16 bytes of
"cUsername"
+28h
-----> RSP value = old RSP-68h
Что мы видим отсюда? Мы видим, что компилятор выровнял данные по 8-байтной границе: например, мы просили выделить 10 байт для cUsername
, а получили 16 байт - x64-битный стек выровнен по 8-байтной границе, естественно. Это означает, что для записи в authentication
нам нужно записать в cUsername
БОЛЬШЕ 16 байтов (символов). Заметьте также, что компилятор поместил cPassword
выше, чем authentication
— мы не можем перезаписать authentication
, используя cPassword
, только cUsername
.
Итак, теперь мы запускаем нашу программу и вводим Username: 0123456789abcdef1
. 0123456789abcdef
= 16 байт, следующие 1
будут помещены в младший байт authentication
— нам этого достаточно:
Username: 0123456789abcdef1
Pass: whatever
Access granted
1
person
lapk
schedule
09.01.2012
pause
, которая запускает ядерные ракеты. Поэтому мне, вероятно, не следует запускать вашу программу. - person David Schwartz   schedule 09.01.2012std::string
вместо массивов символов. - person dreamlax   schedule 09.01.2012system("command")
требуется стандартом C и наследуется C++ на всех платформах. Но его поведение полностью зависит от платформы. На некоторых платформах хакер может установить файл, который будет найден и выполнен вместо предполагаемого. - person Potatoswatter   schedule 09.01.2012cin
прекращает чтение, когда встречается с пробелами. Попробуйте вместо этого использоватьgetline(cin,cUser)
. Также проверьте эту ссылку. - person   schedule 09.01.2012