内存越界定位mprotect
Posted 叮咚咕噜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内存越界定位mprotect相关的知识,希望对你有一定的参考价值。
mprotect()函数可以修改调用进程内存页的保护属性,设置某个地址区域为只可读不可写的,如果调用进程尝试以违反保护属性的方式访问该内存,则内核会发出一个SIGSEGV信号给该进程。
函数介绍
- 头文件:#include <sys/mman.h>
- 函数定义:
int mprotect(void *addr, size_t len, int prot);
- 入参:
- addr:内存地址要求是一个内存页的首地址,简而言之为页大小(一般是 4KB == 4096字节)整数倍。
- len:被修改保护属性区域的长度,页大小整数倍。修改区域范围[addr, addr+len-1]。
- prot:可以取以下几个值,并可以用“|”将几个属性结合起来使用:
- PROT_READ:内存段可读;
- PROT_WRITE:内存段可写;
- PROT_EXEC:内存段可执行;
- PROT_NONE:内存段不可访问。
- 返回值:0;成功,-1;失败(并且errno被设置)
- EACCES:无法设置内存段的保护属性。当通过 mmap(2) 映射一个文件为只读权限时,接着使用 mprotect() 标志为 PROT_WRITE这种情况就会发生。
- EINVAL:addr不是有效指针,或者不是系统页大小的倍数。
- ENOMEM:内核内部的结构体无法分配。
- 注意 : 入参1函数的地址要是页对齐的,保护的长度也需要时页对齐的
测试代码
//mmc.cpp 以下两个例子都是可以使用,只是逻辑处理有点差异
#if 1 //**************示例1*************
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#define handle_error(msg) \\
do { perror(msg); exit(EXIT_FAILURE); } while (0)
static char *buffer;
static void handler(int sig, siginfo_t *si, void *unused)
{
printf("Got SIGSEGV at address: 0x%lx\\n",
(long) si->si_addr);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
char *p;
int pagesize;
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
handle_error("sigaction");
pagesize = sysconf(_SC_PAGE_SIZE);
if (pagesize == -1)
handle_error("sysconf");
/* Allocate a buffer aligned on a page boundary;
initial protection is PROT_READ | PROT_WRITE */
buffer = (char *)memalign(pagesize, 4 * pagesize);
if (buffer == NULL)
handle_error("memalign");
printf("Start of region: 0x%lx\\n", (long) buffer);
if (mprotect(buffer + pagesize * 2, pagesize,PROT_READ) == -1)
handle_error("mprotect");
for (p = buffer ; ; )
*(p++) = 'a';
#if 0
/* 测试发现len需要为页大小倍数,如果不为页大小倍数情况下,系统会匹配最大页大小倍数,
比如页大小为4k,len小于4k,修改范围[addr, addr+4k-1],如果大于4k小于8k,修改范围[addr, addr+8k-1],
以此类推。
*/
if (mprotect(buffer, 4097,PROT_READ) == -1)
handle_error("mprotect");
int i = 0;
/* i<0x4000 (4 * pagesize) */
for (p = buffer ;i<0x4000 ;p++)
{
if (i++ < 8192)
continue;
*p = 'a';
}
printf("End of region: 0x%lx\\n", (long) buffer+i);
#endif
printf("Loop completed\\n"); /* Should never happen */
exit(EXIT_SUCCESS);
}
测试结果:
qiuhui@ubuntu:~/work/share/mprotect-gdb$ g++ -g mmc.cpp
qiuhui@ubuntu:~/work/share/mprotect-gdb$ ./a.out
Start of region: 0x900000
Got SIGSEGV at address: 0x902000
qiuhui@ubuntu:~/work/share/mprotect-gdb$
- 上述程序,buff起始地址0x900000,保护地址为0x902000,当对0x902000进行写操作时触发死机
核心思想
在死机使用的过程中,死机的地址没办法做到正好是页对齐的,所以需要我们对代码进行修改:
- 在被踩的内存前添加一段“替死鬼”内存,并在上面设置“陷阱”揪出踩内存的罪魁祸首:被保护的内存是只读属性,发生内存被写则中断操作。
参考
真是的案例可以参考下:
Linux程序内存越界定位分析总结
定位多线程内存越界问题实践总结
以上是关于内存越界定位mprotect的主要内容,如果未能解决你的问题,请参考以下文章