gpt4 book ai didi

C++ 缓冲区溢出

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:25:28 25 4
gpt4 key购买 nike

我正在尝试自学 C++ 中的缓冲区溢出和利用。我充其量只是一个中级 C++ 人,所以请多多包涵。我已经学习了一些教程,但这里有一些示例代码来说明我的问题:

#include <string>
#include <iostream>

using namespace std;

int main()
{
begin:
int authentication = 0;
char cUsername[10], cPassword[10];
char cUser[10], cPass[10];

cout << "Username: ";
cin >> cUser;

cout << "Pass: ";
cin >> cPass;

strcpy(cUsername, cUser);
strcpy(cPassword, cPass);

if(strcmp(cUsername, "admin") == 0 && strcmp(cPassword, "adminpass") == 0)
{
authentication = 1;
}
if(authentication)
{
cout << "Access granted\n";
cout << (char)authentication;
}
else
{
cout << "Wrong username and password\n";
}

system("pause");
goto begin;
}

我知道这里有各种糟糕的 juju cin << String等等...无论如何,当我在 A 中输入太多字母(例如,一吨 cUser 的)时和 cPass ,我刚从 Visual Studio 收到访问冲突。但是,如果我输入 20ish A的,然后是一个空格,然后是另一个 A进入cUser , 它跳过询问我 cPass (假设因为它在空格字符导致之前对 cin 的调用返回后被填充)并且只授予我访问权限。

什么时候,为什么,数据会溢出到“身份验证”中,为什么只有当我有空间而不是当我有一百万时才会发生 A的...当我在 cUser 的输入中使用空格时,我从来没有得到“访问冲突” .

最佳答案

我稍微修改了你的程序以使其更具说明性:

#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 编译器编译它,没有优化。所以现在我们有一个我们想要“破解”的exe。我们使用 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+28hcUsernameRSP+38hauthentication RSP+40hcPassword,其中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 字节边界上对齐数据:例如,我们要求为 cUsername 分配 10 个字节,但我们得到了 16 个字节 - x64 位堆栈自然地在 8 字节边界上对齐。这意味着为了写入 authentication,我们需要写入 cUsername 超过 16 个字节(符号)。另请注意,编译器将 cPassword 置于 authentication 之上 - 我们无法使用 cPassword 覆盖 authentication,只能使用 c用户名.

现在我们运行我们的程序并输入用户名:0123456789abcdef10123456789abcdef = 16 字节,下一个 1 将被放入 authentication 的低字节 - 对我们来说足够了:

Username: 0123456789abcdef1
Pass: whatever
Access granted
1

关于C++ 缓冲区溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8782852/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com