内存飞踩问题的几点思考

2023-05-16

1、程序编译,链接后生成二进制可执行程序。二进制可执行文件以elf格式实现排列。可以通过readelf -S xxxx查看具体section的划分,粗略划分如下图所示。

在这些section中,代码段是只读的,自然也就不存在代码(指令)被改写的情况。数据段,堆,栈区具有读写的属性,但是数据段和堆一般存放的是数据,不涉及到指令的问题。剩下的栈区,存放的既有数据,又有代码(指令),可能存在代码(指令)被改写,即内存被飞踩现象。

2、栈空间数据被修改情况

2.1、先做个实验(ARM 32环境)

2.1.1、栈区空间

#include <unistd.h>
#include <stdio.h>

int do_nothing(int a)
{
	return a;
}

int max(int a,int b)
{
	int c = 0;
	
	do_nothing(c);
	
	return a + b;
}

int main()
{
	int res = 0;
	int a = 1;
	int b = 2;
	
	res = max(a, b);
	
	return res;
}
arm-linux-gcc func.c -o func
arm-linux-objdump -d func > func.txt

查看反汇编结果

00010490 <max>:
   10490:	e92d4800 	push	{fp, lr}         ;把bl <max>的下一条指令地址lr保存到栈区
   10494:	e28db004 	add	fp, sp, #4
   10498:	e24dd010 	sub	sp, sp, #16          ;确定好栈区大小
   1049c:	e50b0010 	str	r0, [fp, #-16]
   104a0:	e50b1014 	str	r1, [fp, #-20]	; 0xffffffec
   104a4:	e3a03000 	mov	r3, #0

增加max函数的局部变量个数,再查看相对应的反汇编

int max(int a,int b)
{
	int c = 0;
	int d = 0;
	int e = 0;
	
	c = d;
	do_nothing(c);
	
	return a + b;
}
00010490 <max>:
   10490:	e92d4800 	push	{fp, lr}
   10494:	e28db004 	add	fp, sp, #4
   10498:	e24dd018 	sub	sp, sp, #24		;栈区空间增大
   1049c:	e50b0018 	str	r0, [fp, #-24]	; 0xffffffe8
   104a0:	e50b101c 	str	r1, [fp, #-28]	; 0xffffffe4
   104a4:	e3a03000 	mov	r3, #0

由此可见,栈区的大小在程序编译的时候就已经确定。存放着函数退出后下一条指令的地址和局部变量的数值。

2.1.2、栈区数据

#include <stdio.h>
#include <string.h>

int do_nothing()
{
	printf("nothing\n");
}

int func(int i)
{
	int a = 6;
	int b = 0;
	int *p = NULL;
	char num[3] = {0x6d, 0x04, 0x01};
	
	p = &b;
	printf("b addr=0x%x, func=0x%x\n", p, do_nothing);
	printf("stack value:0x%x, 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", *(p + 1), *(p + 2), *(p + 3), *(p + 4), *(p + 5), *(p + 6), *(p + 7), *(p + 8));
//	memcpy((char *)(p+8), num, 3);
	
	return a * i;
}

int main(void)
{
	int i = 1;

	func(i);
	
	i++;
	
	return i;
}

从局部变量中,获取栈区地址,打印栈区里面的部分数据。

第一个红框存放着局部变量的数据,第二个红框存放着退出函数后下一条指令的地址,可以从反汇编中确认。

2.1.3、修改栈区数据,其中存放的下一条指令的地址0x10545被改写,程序运行是不是就会出现异常。

上图截图中绿框所示,对应着是另外一个函数do_nothing的地址,假如把栈区0x10545的数据改写成do_nothing的地址,应该就会执行do_nothing的函数。

释放2.1.2代码的memcpy处注释,运行程序,程序果然执行了do_nothing的函数

 2.2、上述假如被踩的数据是其他值,程序就可能会直接崩溃。而当程序出现崩溃时,数据可能在崩溃前的某一时刻就被修改,出现崩溃的现场并不是第一现场,很难定位。如下面的代码:

void fun(void) {
	char arr[5];
	
	strcpy(arr, "are you ok.are you ok.");
	
	return;
}

2.3、可以在编译的时候添加-fstack-protector-all编译选项,减少定位难度(哪位大侠有更好的定位手段,在评论区求赐教),不至于代码跑飞,艰难查找到第一现场。

gcc stack.c -fstack-protector-all -o stack

 没有添加-fstack-protector-all编译选项出现异常时的日志

此core_dump很难查找到问题原因,因为出现异常的地方不是第一现场。 

2.4、栈区被踩问题的定位手段

栈区数据被飞踩问题定位手段_sydyh43的博客-CSDN博客

3、GOT表被修改情况

3.1、可以先看看下面的这篇分析

动态库*.so的延时绑定分析_sydyh43的博客-CSDN博客

打开git源码中的Makefile屏蔽和main函数的#if 0既可。

3.2、如以下的代码

int arr[2];        //arr数组是全局变量

void fun(void) {
	arr[-8] = 0xFF;
	
	return;
}

3.3、这种内存被踩的情况一般不会出现,因为编译器默认会把relacation后的GOT表设置成只读。

4、综上所述,关于内存被踩的问题,需要编译的时候做好准备。在编译的时候添加编译选项,出现问题的时候可以保留第一现场。编译选项添加情况可以通过checksec.sh脚本校验,详见:

linux程序的常用保护机制 | 上善若水

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

内存飞踩问题的几点思考 的相关文章

随机推荐

  • 20从零开始学Java之牛闪闪的for循环是怎么用的?

    作者 xff1a 孙玉昌 xff0c 昵称 一一哥 xff0c 另外 壹壹哥 也是我哦 千锋教育高级教研员 CSDN博客专家 万粉博主 阿里云专家博主 掘金优质作者 配套项目资料 https github com SunLtd LearnJ
  • 21从零开始学Java之while与do-while循环的用法有什么不同?

    作者 xff1a 孙玉昌 xff0c 昵称 一一哥 xff0c 另外 壹壹哥 也是我哦 千锋教育高级教研员 CSDN博客专家 万粉博主 阿里云专家博主 掘金优质作者 配套项目资料 https github com SunLtd LearnJ
  • NVIDIA刷机+部署ROS操作指南

    说明 xff1a Ubuntu18 04 43 ROS melodic 一 NVIDIA刷机操作步骤 准备工作 xff1a 准备一台台式机作为主机 xff0c 且确保安装了ubuntu双系统或者虚拟机 xff0c 再为NVIDIA控制器准备
  • 权限认证基础:区分Authentication,Authorization以及Cookie、Session、Token

    1 认证 Authentication 和授权 Authorization 的区别是什么 xff1f 这是一个绝大多数人都会混淆的问题 首先先从读音上来认识这两个名词 xff0c 很多人都会把它俩的读音搞混 xff0c 所以我建议你先先去查
  • 22从零开始学Java之你知道return、break与continue的区别吗?

    作者 xff1a 孙玉昌 xff0c 昵称 一一哥 xff0c 另外 壹壹哥 也是我哦 千锋教育高级教研员 CSDN博客专家 万粉博主 阿里云专家博主 掘金优质作者 配套项目资料 https github com SunLtd LearnJ
  • 23从零开始学Java之foreach循环该怎么用?

    作者 xff1a 孙玉昌 xff0c 昵称 一一哥 xff0c 另外 壹壹哥 也是我哦 千锋教育高级教研员 CSDN博客专家 万粉博主 阿里云专家博主 掘金优质作者 配套项目资料 https github com SunLtd LearnJ
  • 24从零开始学Java之如何正确地使用一维数组

    作者 xff1a 孙玉昌 xff0c 昵称 一一哥 xff0c 另外 壹壹哥 也是我哦 千锋教育高级教研员 CSDN博客专家 万粉博主 阿里云专家博主 掘金优质作者 配套开源项目资料 https github com SunLtd Lear
  • 25从零开始学Java之数组扩容与数组拷贝的实现过程与原理分析

    作者 xff1a 孙玉昌 xff0c 昵称 一一哥 xff0c 另外 壹壹哥 也是我哦 千锋教育高级教研员 CSDN博客专家 万粉博主 阿里云专家博主 掘金优质作者 配套开源项目资料 https github com SunLtd Lear
  • 26从零开始学Java之如何对数组进行排序与二分查找?

    作者 xff1a 孙玉昌 xff0c 昵称 一一哥 xff0c 另外 壹壹哥 也是我哦 千锋教育高级教研员 CSDN博客专家 万粉博主 阿里云专家博主 掘金优质作者 配套开源项目资料 https github com SunLtd Lear
  • 27从零开始学Java之详解复杂的二维数组与多维数组

    作者 xff1a 孙玉昌 xff0c 昵称 一一哥 xff0c 另外 壹壹哥 也是我哦 千锋教育高级教研员 CSDN博客专家 万粉博主 阿里云专家博主 掘金优质作者 前言 在前几篇文章中 xff0c 壹哥给大家介绍了Java里的一维数组 x
  • 28从零开始学Java之面向对象和面向过程到底有哪些区别?

    作者 xff1a 孙玉昌 xff0c 昵称 一一哥 xff0c 另外 壹壹哥 也是我哦 千锋教育高级教研员 CSDN博客专家 万粉博主 阿里云专家博主 掘金优质作者 前言 壹哥相信 xff0c 经过你对前面文章中技术点的学习 xff0c 现
  • 29从零开始学Java之如何正确创建Java里的类?

    作者 xff1a 孙玉昌 xff0c 昵称 一一哥 xff0c 另外 壹壹哥 也是我哦 千锋教育高级教研员 CSDN博客专家 万粉博主 阿里云专家博主 掘金优质作者 前言 在上一篇文章中 xff0c 壹哥给大家介绍了面向对象和面向过程的概念
  • 全面图解路由器接口及连接(图)

    路由器所在的网络位置比较复杂 xff0c 既可是内部子网边缘 xff0c 也可位于内 外部网络边缘 同时为了实现强大的适用性 xff0c 它需要连接各种网络 xff0c 这样 xff0c 它的接口也就必须多种多样 对于这些 xff0c 不要
  • blktrace,blkparse,btt工具的制作和使用

    1 软件包交叉编译安装 1 1 blktrace源码下载路径 https git kernel dk cgit blktrace 1 2 源码安装 tar zxvf blktrace 1 2 0 tar gz cd blktrace 1 2
  • cartographer之ceres编译

    1 首先下载ceres xff1b 2 进入ceres目录 xff1b 3 mkdir build amp amp cd build 4 cmake DEIGENSPARSE 5 make 6 sudo make install
  • 一个空文件夹和空文件占多少空间?

    用于显示文件夹和文件大小的命令 span class token function du span h 显示目前在 Linux 系统上的文件系统磁盘使用情况统计 span class token function df span i 一 问
  • 虚拟地址如何访问到物理地址

    环境 xff1a 32bit CPU 一 通过二级页表映射的方式访问物理地址 1 取一级页表的基地址Abase1 2 取虚拟地址的前12bit 31 20 地址O1 3 计算得到新地址Apgd 61 Abase1 amp 0xFFFFF00
  • 添加自定义的section

    一 基本知识点 编译出来的程序 xff08 o so exe ko等等 xff09 都是以elf格式进行排列保存的 elf文件分析情况 xff1a https blog csdn net edonlii article details 87
  • 如何打印堆栈

    一 打印堆栈可以方便问题定位 xff0c 找到具体的函数调用流程 二 打印堆栈的方法 2 1 用户态 include lt stdio h gt include lt stdlib h gt include lt stddef h gt i
  • 内存飞踩问题的几点思考

    1 程序编译 xff0c 链接后生成二进制可执行程序 二进制可执行文件以elf格式实现排列 可以通过readelf S xxxx查看具体section的划分 xff0c 粗略划分如下图所示 在这些section中 xff0c 代码段是只读的