mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。 原型: int mprotect(const void *start, size_t len, int prot); 第一个参数:需改写属性的内存中开始地址 第二个参数:需改写属性的内存长度 第三个参数 Prot=7 是可读可写可执行的最高权限 一般用于在利用shellcode方式进行栈溢出时,但是bss段没有足够的权限来写入shellcode时,可以利于mprotect函数来改写bss段的权限 例题分析 可以看到只开启了nx保护 程序中有直接可以读取flag的get_flag函数,但这次我们使用mprotect的方式来直接拿shell 使用gdb中的vmmap先来看一下bss段的权限 ? mprotect来修改指定bss段地址的权限,在利用pop esi;pop edi;pop ebp;ret来返回到指定地址如read函数上以读取shellcode到以修改权限的bss段地址上,最后再次利用
mprotect系统调用是修改内存页属性的,他修改的内容包括vma的内容和页表项内容。linux用vma链表管理一个进程使用的虚拟地址空间。下面是实现代码。 /* * linux/mm/mprotect.c * * (C) Copyright 1994 Linus Torvalds */ #include <linux/stat.h> #include (vma, newflags, newprot); else // start重合则修改start到end的设置 error = mprotect_fixup_start (vma, end, newflags, newprot); // 结束地址重合 else if (end == vma->vm_end) error = mprotect_fixup_end (vma, start, newflags, newprot); else // 中间部分重合 error = mprotect_fixup_middle(vma, start, end
mprotect函数详解 在Linux中,mprotect()函数可以用来修改一段指定内存区域的保护属性 函数原型如下: #include <unistd.h> #include <sys/mmap.h > int mprotect(const void *start, size_t len, int prot); mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为 mprotect函数的利用 这个函数利用方式为将目标地址:.got.plt或.bss段 修改为可读可写可执行 例题分析 not_the_same_3dsctf_2016 原题链接:https://buuoj.cn 返回地址覆盖需要三个连续的pop地址,因为mprotect函数需要传入三个地址,使用ROPgadget来获取地址 ROPgadget --binary not_the_same_3dsctf_2016 ) + p32(got_plt_size) + p32(got_plt_type) # mprotect的三个参数 payload += p32(elf.symbols['read']) #
溢出的话使用rop.可以mprotect改bss段,然后shellcode。使用通用gadget。或者自己构造。 = libc_base + libc.symbols[b"__mprotect"] print("puts:", hex(puts)) print("libc_base:", hex( mprotect_got指向system要注意,在进入mprotect系统调用时qemu会检查第一个参数的地址是否页对齐,对齐了才会call mprotect_got上的指针。 (mprotect_got)) mprotect_libc = u64(p.recv(8)) libc_base = mprotect_libc - libc.symbols[ b"__mprotect"] system = libc_base + libc.symbols[b"system"] print("[*] mprotect_libc:", hex(mprotect_libc
打开虚拟机发现保护全开,是64位的,拖进IDA反汇编后查看伪代码,在如下图中的位置: 可以发现,程序使用mmap开辟了空间,并向该内存读入了0x11个字节,并且使用mprotect来修改该区域的权限, 讲一下mprotect函数,函数原型为int mprotect(const void *start, size_t len, int prot);, 该函数有三个参数,分别为: 要修改的内存地址 ==> start 要修改的内存大小 ==> len 以及赋予多大的权限 ==> prot 也就是说,mprotect是把从addr开始的长度为length的内存空间的保护属性修改为prot的值。 需要注意的是mprotect修改权限时,是以一个内存页为最小单位的,也就是说,如果写入的长度没有达到一个内存页的大小(0x1000),mprotect认为修改一整个页的权限。 扩展即可,mprotect所对应的系统调用号可以在/usr/include/x86_64-linux-gnu/asm/unistd_64.h中所找到。
}) = 0 mmap(NULL, 2208864, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fac07bc3000 mprotect }) = 0 mmap(NULL, 3932736, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fac07802000 mprotect (0x7fac07bb8000, 16384, PROT_READ) = 0 mprotect(0x7fac07dd9000, 4096, PROT_READ) = 0 mprotect(0x600000 , 4096, PROT_READ) = 0 mprotect(0x7fac07fff000, 4096, PROT_READ) = 0 munmap(0x7fac07fec000, 72041 0x239f000) = 0x239f000 brk(0) = 0x239f000 mprotect
与memfd_create等现有内存保护方案不同,mseal专门针对远程代码执行攻击设计,其安全保证体现在:禁止通过mprotect修改权限位阻止通过munmap取消映射限制mmap(MAP_FIXED len, unsigned long flags)关键实现流程:通过check_mm_seal()验证目标内存范围有效性使用apply_mm_seal()遍历VMA并设置VM_SEALED标志内核在mm/mprotect.c vma_is_sealed(vma))) return false; return true;}漏洞防御场景强化NX防护典型攻击链:喷射shellcode到非可执行区域通过ROP调用mprotect exec_offset = (void*)((int64_t)&shellcode & ~(getpagesize()-1));mseal(exec_offset, getpagesize()); // 密封内存区域mprotect
文章目录 一、mmap 创建内存映射 二、munmap 删除内存映射 三、mprotect 设置虚拟内存区域访问权限 一、mmap 创建内存映射 ---- mmap 系统调用函数 , 用于 创建 " 内存映射 munmap 系统调用 的作用是 删除内存映射 , 其函数原型如下 : #include <sys/mman.h> int munmap(void* start,size_t length); 三、mprotect 设置虚拟内存区域访问权限 ---- mprotect 系统调用 的作用是 设置虚拟内存区域访问权限 , 其函数原型如下 : #include <sys/mman.h> int mprotect(const
那么我们下面的代码通过两种方式来修改只读数据的页表,一种是我们通过访问一个字符设备来修改页表(字符设备驱动程序所作的工作就是遍历各级页表,然后将相关的叶子表项修改为可写),一种是通过mprotect来实现 ((void *)addr, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); //mprotect方式修改内存访问属性 if (ret) { perror( "fail to mprotect"); return -1; } printf("<lien:%d> mprotect PROT_WRITE ok! perror("fail to mprotect"); return -1; } printf("\n<lien:%d> mprotect PROT_READ ok! 3.使用mprotect方式来修改页表属性。
提取所有内存数据由于反调试检测线程的libexec.bin库文件在程序启动后加载,具有延迟性,而mprotect又是在程序启动时就加载,所以可以在反调试生效前,hook mprotect提取加载时执行的所有代码拦截 mprotect 调用,检查内存权限更改,并强制将某些内存区域设置为 rwx,然后尝试进行内存转储,提取所有执行的代码,其中包括了解密后的dex文件var mprotect = Module.findExportByName (null, "mprotect"); if (mprotect) { Interceptor.attach(mprotect, { onEnter: function (标记可读内存) var mprotect = Module.findExportByName(null, "mprotect"); if (mprotect) { Interceptor.attach (mprotect, { onEnter: function (args) { this.addr = ptr(args[0].toString(
* pBase = (void*)(0xFFFFF000 & (int)pFunc); 然后 , 修改内存页属性 , 修改为 可读 | 可写 | 可执行 , 避免因为内存访问权限问题导致操作失败 ; mprotect 函数只能对整个页内存的属性进行修改 , 每个 内存页 大小都是 4KB ; /* 修改整个内存页属性 , 修改为 可读 | 可写 | 可执行 , * 避免因为内存访问权限问题导致操作失败 * mprotect 函数只能对整个页内存的属性进行修改 * 每个 内存页 大小都是 4KB */ int ret = mprotect(pBase, 0x1000, PROT_WRITE | PROT_READ
mprotect函数 ? 这个函数本身我是不了解的,所以去查了一下,进行了详尽的了解 mprotect int mprotect(const void *addr,size_t len,int prot) 看上面的参数应该前两个还是很好理解的 这让我有一些难受了 新思路 首先利用mprotect函数进行一次改写,这里的改写操作就要用到arm64的一些调用规定的涉猎了这里我就不进行详细的解说贴出一个网址大家可以自己学习一下:https://www.sohu.com
}}) = 0 mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fc028100000 mprotect 0x7fc0289009d0) = 55 mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fc0237f0000 mprotect 0x7fc023ff09d0) = 56 mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fc022fe0000 mprotect 0x7fc0237e09d0) = 57 mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fc0227d0000 mprotect
.}) = 0 mmap2(NULL, 83236, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x76f4f000 mprotect (0x76f36000, 8192, PROT_READ) = 0 mprotect(0x76f4f000, 20480, PROT_READ|PROT_WRITE) = 0 mprotect(0x76f4f000 , 20480, PROT_READ|PROT_EXEC) = 0 cacheflush(0x76f4f000, 0x76f54000, 0, 0x15, 0x7eb5f0b0) = 0 mprotect (0x76f3a000, 8192, PROT_READ) = 0 mprotect(0x76f53000, 20480, PROT_READ|PROT_WRITE) = 0 mprotect(0x76f53000 (0x76ea2000, 8192, PROT_READ) = 0 mprotect(0x76ebb000, 20480, PROT_READ|PROT_WRITE) = 0 mprotect(0x76ebb000
(0x7fe8f442b000, 16384, PROT_READ) = 0 mprotect(0x616000, 4096, PROT_READ) = 0 mprotect(0x7fe8f465a000 (0x207faa20000, 8192, PROT_READ|PROT_WRITE) = 0 mprotect(0x207faa20000, 8192, PROT_READ|PROT_EXEC) = 0 mprotect(0x207faa21000, 4096, PROT_READ|PROT_WRITE) = 0 mprotect(0x207faa21000, 4096, PROT_READ|PROT_EXEC 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7faf9c716000 [00007faf9cafb517] mprotect mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c7ebec000 15:19:25 mprotect
.}) = 0 mmap(NULL, 1072448, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff4e9ee4000 mprotect ..}) = 0 mmap(NULL, 3944896, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff4e9a07000 mprotect ..}) = 0 mmap(NULL, 2109744, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff4e9803000 mprotect (0x7ff4e9dc0000, 16384, PROT_READ) = 0 mprotect(0x7ff4e9a05000, 4096, PROT_READ) = 0 mprotect(0x600000 , 4096, PROT_READ) = 0 mprotect(0x7ff4e9fec000, 4096, PROT_READ) = 0 munmap(0x7ff4e9ed1000, 74350
= (void*)(0xFFFFF000 & (int)pFunc); /* 修改整个内存页属性 , 修改为 可读 | 可写 | 可执行 , * 避免因为内存访问权限问题导致操作失败 * mprotect 函数只能对整个页内存的属性进行修改 * 每个 内存页 大小都是 4KB */ int ret = mprotect(pBase, 0x1000, PROT_WRITE | PROT_READ | PROT_EXEC); /* 修改内存页属性失败的情况 */ if (ret == -1) { perror("mprotect:"); return -1; } #if defined
.}) = 0 mmap(NULL, 1072448, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff4e9ee4000 mprotect ..}) = 0 mmap(NULL, 3944896, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff4e9a07000 mprotect ..}) = 0 mmap(NULL, 2109744, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ff4e9803000 mprotect (0x7ff4e9dc0000, 16384, PROT_READ) = 0 mprotect(0x7ff4e9a05000, 4096, PROT_READ) = 0 mprotect(0x600000 , 4096, PROT_READ) = 0 mprotect(0x7ff4e9fec000, 4096, PROT_READ) = 0 munmap(0x7ff4e9ed1000, 74350
void *)(pos+(i*4)), (void *)*p)) return -1; return 0; } 写入栈以后,shellcode并不能执行,因为当前Android都开启了栈执行保护,需要先通过mprotect (),来修改栈的可执行权限: // 计算栈顶指针 regs.ARM_sp = regs.ARM_sp – n*4 – sizeof(sc); // 调用mprotect()设置栈可执行 regs.ARM_r0 \n”); regs.ARM_lr = codeaddr; // lr指向shellcode,mprotect()后执行 regs.ARM_pc = mprotectaddr; } // 旧版本Android 没有栈保护,Android 2.3引入 else { regs.ARM_pc = codeaddr; } 这段代码首先计算栈顶位置,接着将栈 起始地址/栈大小/权限位 3个参数压栈,然后调用mprotect ()函数设置栈的可执行权限,最后将lr寄存器设置为栈上代码的起始地址,这样当mprotect()函数返回后就可以正常执行栈上代码了。
Ret2shellcode 当bss段没有足够权限时,可以使用mprotect来修改权限 因为需要在一次溢出中利用多个函数,所以要找到gadgetspop esi;pop edi;pop ebp;ret ; payload布局为: mprotect函数+pop *;ret+参数1\2\3+返回地址[read函数]+pop *;ret+参数1\2\3+返回地址[shellcode_addr] Ret2syscall