栈溢出原理与实践之读书笔记

admin 2025年7月1日16:35:48评论0 views字数 5652阅读18分50秒阅读模式

栈溢出原理与实践之读书笔记

本文为看雪论坛优秀‍‍‍文章
看雪论坛作者ID:瑞皇

新的一年想把0day漏洞安全这本书读完,会一步一步踏踏实实的学完,和大家共享笔记。


环境我想用vs2019运行,老的编译器很多人不用了,渐渐的会被淘汰。用新的编译器可以锻炼下编译选项的功底。

我的笔记是配合书写的,理论部分会少用笔墨,着重在实验上。

1

基础知识

这一部分书上讲解的足够了,如果暂时看不懂,多看看就好。功夫不负有心人,迟早学会的事情。

2

栈溢出原理与实践

2.1 系统栈的工作原理

这一部分书上讲解的足够了,如果暂时看不懂,多看看就好。功夫不负有心人,迟早学会的事情。

2.2 修改邻接变量

运行环境:

VS2019 X86 Debug

运行设置:

属性->c/c++->常规->sdl检查关闭
属性->c/c++->代码运行->基本运行时检查->关闭【堆栈帧 (/RTCs)】
然后运行代码,输入qqqqqqqq即,可完成简单的溢出覆盖。
栈溢出原理与实践之读书笔记
#include <stdio.h>#include <stdlib.h>#include <string.h>#define PASSWORD "1234567"int verify_password(char* password){    int authenticated;    char buffer[8];// add local buffto be overflowed    authenticated = strcmp(password, PASSWORD);    strcpy(buffer, password);//over flowed here!    return authenticated;}void main(){    int valid_flag = 0;    char password[1024];    while (1)    {        printf("please input password: ");        scanf("%s", password);        valid_flag = verify_password(password);        if (valid_flag)        {            printf("incorrect password!nn");        }        else        {            printf("Congratulation! You have passed the verification!n");            break;        }    }}

实验情况:SDL关闭和头文件的增加为了让程序代码跑起来。

堆栈帧 (/RTCs)的关闭,则是为了让程序不在运行时检查程序。

如果不关闭堆栈帧 (/RTCs),原本buffer[8]会因为检查多出8字节。然后程序报错,我原本以为时字符对齐的问题,检查后发现不是。是运行时检查的问题。
栈溢出原理与实践之读书笔记
栈溢出原理与实践之读书笔记
关闭堆栈帧 (/RTCs)的汇编代码:
int verify_password(char* password){004015F0  push        ebp 004015F1  mov         ebp,esp 004015F3  sub         esp,50h 004015F6  mov         eax,dword ptr [__security_cookie (0407004h)] 004015FB  xor         eax,ebp 004015FD  mov         dword ptr [ebp-4],eax 00401600  push        ebx 00401601  push        esi 00401602  push        edi 00401603  mov         ecx,offset _06E17EB3_Test@cpp (0409008h) 00401608  call        @__CheckForDebuggerJustMyCode@4 (0401285h)     int authenticated;    char buffer[8];// add local buffto be overflowed    authenticated = strcmp(password, PASSWORD);0040160D  push        offset string "1234567" (0405B30h) 00401612  mov         eax,dword ptr 
		
输入密码查看隐藏内容

00401615 push eax 00401616 call _strcmp (040103Ch) 0040161B add esp,8 0040161E mov dword ptr [authenticated],eax strcpy(buffer, password);//over flowed here!00401621 mov eax,dword ptr
输入密码查看隐藏内容

00401624 push eax 00401625 lea ecx,[buffer] 00401628 push ecx 00401629 call _strcpy (040119Ah) 0040162E add esp,8 return authenticated;00401631 mov eax,dword ptr [authenticated] }00401634 pop edi 00401635 pop esi 00401636 pop ebx 00401637 mov ecx,dword ptr [ebp-4] 0040163A xor ecx,ebp 0040163C call @__security_check_cookie@4 (040111Dh) 00401641 mov esp,ebp 00401643 pop ebp 00401644 ret
开启堆栈帧 (/RTCs)的汇编代码:
int verify_password(char* password){00E317A0  push        ebp 00E317A1  mov         ebp,esp 00E317A3  sub         esp,0E0h 00E317A9  push        ebx 00E317AA  push        esi 00E317AB  push        edi 00E317AC  lea         edi,[ebp-20h] 00E317AF  mov         ecx,8 00E317B4  mov         eax,0CCCCCCCCh 00E317B9  rep stos    dword ptr es:[edi] 00E317BB  mov         eax,dword ptr [__security_cookie (0E3A004h)] 00E317C0  xor         eax,ebp 00E317C2  mov         dword ptr [ebp-4],eax 00E317C5  mov         ecx,offset _06E17EB3_Test@cpp (0E3C008h) 00E317CA  call        @__CheckForDebuggerJustMyCode@4 (0E3132Fh)     int authenticated;    char buffer[8];// add local buffto be overflowed    authenticated = strcmp(password, PASSWORD);00E317CF  push        offset string "1234567" (0E37B30h) 00E317D4  mov         eax,dword ptr 
		
输入密码查看隐藏内容

00E317D7 push eax 00E317D8 call _strcmp (0E31046h) 00E317DD add esp,8 00E317E0 mov dword ptr [authenticated],eax strcpy(buffer, password);//over flowed here!00E317E3 mov eax,dword ptr
输入密码查看隐藏内容

00E317E6 push eax 00E317E7 lea ecx,[buffer] 00E317EA push ecx 00E317EB call _strcpy (0E31212h) 00E317F0 add esp,8 return authenticated;00E317F3 mov eax,dword ptr [authenticated] }00E317F6 push edx 00E317F7 mov ecx,ebp 00E317F9 push eax 00E317FA lea edx,ds:[0E31828h] 00E31800 call @_RTC_CheckStackVars@8 (0E311EFh) 00E31805 pop eax 00E31806 pop edx 00E31807 pop edi 00E31808 pop esi 00E31809 pop ebx 00E3180A mov ecx,dword ptr [ebp-4] 00E3180D xor ecx,ebp 00E3180F call @__security_check_cookie@4 (0E31154h) 00E31814 add esp,0E0h 00E3181A cmp ebp,esp 00E3181C call __RTC_CheckEsp (0E31253h) 00E31821 mov esp,ebp 00E31823 pop ebp 00E31824 ret 00E31825 nop dword ptr [eax] 00E31828 add dword ptr [eax],eax 00E3182A add byte ptr [eax],al 00E3182C xor byte ptr [eax],bl 00E3182E jecxz __$EncStackInitStart+84h (0E31830h) 00E31830 in al,0FFh 00E31832 ?? ??????}00E31833 dec dword ptr [eax] 00E31835 add byte ptr [eax],al 00E31837 add byte ptr [eax+ebx],bh 00E3183A jecxz __$EncStackInitStart+90h (0E3183Ch) 00E3183C bound esi,qword ptr [ebp+66h] 00E3183F jb 00001843
通过代码的比较,我们可以发现,函数使用@_RTC_CheckStackVars@8进行检测,跟进去发现该函数容纳了两个_RTC_CheckStackVars.
当检测不是0CCCCCCCCh的时候,会报错,并进入_RTC_StackFailure函数。
006E1DD4  mov         ecx,dword ptr [ebx+4] 006E1DD7  mov         eax,dword ptr [frame] 006E1DDA  mov         edx,dword ptr [ecx+edi] 006E1DDD  cmp         dword ptr [edx+eax-4],0CCCCCCCCh 006E1DE5  jne         _RTC_CheckStackVars+39h (06E1DF9h) 006E1DE7  mov         eax,dword ptr [ecx+edi+4] 006E1DEB  add         eax,edx 006E1DED  mov         edx,dword ptr [frame] 006E1DF0  cmp         dword ptr [eax+edx],0CCCCCCCCh 006E1DF7  je          _RTC_CheckStackVars+49h (06E1E09h) 006E1DF9  push        dword ptr [ecx+edi+8] 006E1DFD  mov         eax,dword ptr [ebp+4] 006E1E00  push        eax 006E1E01  call        _RTC_StackFailure (06E1352h)
栈溢出原理与实践之读书笔记
栈溢出原理与实践之读书笔记

2.3 控制程序的执行流程

运行环境:VS2019 X86 Debug

运行设置:

属性->c/c++->常规->sdl检查关闭
属性->c/c++->代码运行->基本运行时检查->关闭【堆栈帧 (/RTCs)】

实验情况:

修改上述配置后,发现无法正常运行,单步调试发现fopen断点出现问题。搜索查询后发现有些编译器不支持rw+的格式。这里我们将rw+修改为r+即可正确运行。r和r+的区别是r+拥有写权限。我大胆猜测,rw+不支持的原因是功能设计上的重复。
栈溢出原理与实践之读书笔记
在这里我们运行还会遇到一个问题:
栈溢出原理与实践之读书笔记
我们需要把栈保护天使GS关闭。
属性->c/c++->代码运行->安全检查->关闭【禁用安全检查 (/GS-)】
这个时候代码就会报书上期望我们出现的错误,返回值出现错误。理论方面书上说的已经很全面,这里就简单画个图。
栈溢出原理与实践之读书笔记
最终结果如下图,符合书上预期:
栈溢出原理与实践之读书笔记

2.4 向进程中植入代码

运行环境:VS2019 X86 Debug

运行设置:

属性->c/c++->常规->sdl检查关闭
属性->c/c++->代码运行->基本运行时检查->关闭【堆栈帧 (/RTCs)】
属性->c/c++->代码运行->安全检查->关闭【禁用安全检查 (/GS-)】

实验情况:

创建一个文件,password.txt,构造shellcode。这里按照书上就好,然后看下面的步骤。
栈溢出原理与实践之读书笔记
配置需要进行修改,因为要静态获取buffer地址,aslr随机基址要关闭,因为要在数据区执行代码,数据执行保护(DEP)也要关闭。
属性->链接器->高级->随机基址->否
属性->链接器->高级->数据执行保护(DEP)->否
password.txt,有两个地方需要修改,根据书中描述。
一个是Messagebox的地址,在下图中会讲解如何寻找。
一个是buffer的地址,下文中也会详细介绍细节。
栈溢出原理与实践之读书笔记
我们在程序中添加messagebox,这样我们的代码就会调用User32.dll,我们使用dependency可以获取该模块函数地址。
栈溢出原理与实践之读书笔记根据书中方法,计算正确值。
>>> hex(0x69E00000+0x83670)'0x69e83670'
但是可以看到,程序并不能正确运行,这问题常出在现在的windows操作系统里,优先基址常常不是实际加载地址,在PE格式中DLL会给出一个优先加载地址,当程序并未占用该地址时,优先按照该基址进行计算。占用的话,会重新申请空间。

问题发生了,如何解决,这里使用Ollydbg查看模块地址。
栈溢出原理与实践之读书笔记
栈溢出原理与实践之读书笔记
下面是Ollydbg的正确解决方案
步骤1:点击E字母
栈溢出原理与实践之读书笔记
步骤2:找到User32,此时使用该模块地址计算,可以获得正确地址,成功弹出messagebox
如果不想算,就按一下User32,快捷键ctrl+n,找到messageboxA一样可以。
>>> hex(0x75cc0000+0x83670)'0x75d43670'
栈溢出原理与实践之读书笔记
栈溢出原理与实践之读书笔记
栈溢出原理与实践之读书笔记

栈溢出原理与实践之读书笔记

看雪ID:瑞皇

https://bbs.pediy.com/user-home-848111.htm

*本文由看雪论坛 瑞皇 原创,转载请注明来自看雪社区
栈溢出原理与实践之读书笔记

# 

原文始发于微信公众号(看雪学苑):栈溢出原理与实践之读书笔记

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年7月1日16:35:48
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   栈溢出原理与实践之读书笔记http://cn-sec.com/archives/810031.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息