我想写一个信号处理程序来捕获SIGSEGV.我使用保护内存块进行读写
char *buffer;
char *p;
char a;
int pagesize = 4096;
mprotect(buffer,pagesize,PROT_NONE)
Run Code Online (Sandbox Code Playgroud)
这可以保护从缓冲区开始的内存大小字节的内存,防止任何读取或写入.
其次,我尝试读取内存:
p = buffer;
a = *p
Run Code Online (Sandbox Code Playgroud)
这将生成一个SIGSEGV,我的处理程序将被调用.到现在为止还挺好.我的问题是,一旦调用处理程序,我想通过这样做来改变内存的访问写入
mprotect(buffer,pagesize,PROT_READ);
Run Code Online (Sandbox Code Playgroud)
并继续正常运行我的代码.我不想退出该功能.在将来写入相同内存时,我想再次捕获信号并修改写入权限,然后记录该事件.
这是代码:
#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)
char *buffer;
int flag=0;
static void handler(int sig, siginfo_t *si, void *unused)
{
printf("Got SIGSEGV at address: 0x%lx\n",(long) si->si_addr);
printf("Implements the handler only\n");
flag=1;
//exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{ …Run Code Online (Sandbox Code Playgroud) 在Linux(或Solaris)上,有一种比手动解析更好的方法/proc/self/maps来确定您是否可以读取,写入或执行存储在内存中一个或多个地址的任何内容?
例如,在Windows中你有VirtualQuery.
在Linux中,我可以mprotect更改这些值,但我无法读回它们.
此外,是否有任何方法可以知道这些权限何时发生变化(例如,当有人使用mmap我背后的文件时),而不是做一些非常具有侵略性的事情并ptrace在进程中的所有线程上使用并拦截任何syscall可能影响内存的尝试地图?
更新:
不幸的是,我在JIT中使用它,它几乎没有关于它正在执行的代码的信息,以获得常量的近似值.是的,我意识到我可以有一个可变数据的常量映射,比如Linux使用的vsyscall页面.我可以放心地假设任何未包含在初始解析中的内容都是可变且危险的,但我对这个选项并不完全满意.
现在我所做的是阅读/proc/self/maps并构建一个结构,我可以通过二进制搜索来获得给定地址的保护.每当我需要了解一些不在我的结构中的页面时,我会重新阅读/ proc/self/maps,假设它已经同时添加,或者我将要进行段错误.
它似乎解析文本以获取此信息,而不知道它何时更改是非常苛刻的.(/dev/inotify几乎不适用于任何事情/proc)
我正在尝试从Linux上的纯Python代码调用内联机器代码.为此,我将代码嵌入到字节文字中
code = b"\x55\x89\xe5\x5d\xc3"
Run Code Online (Sandbox Code Playgroud)
然后调用mprotect()via ctypes以允许执行包含代码的页面.最后,我尝试ctypes用来调用代码.这是我的完整代码:
#!/usr/bin/python3
from ctypes import *
# Initialise ctypes prototype for mprotect().
# According to the manpage:
# int mprotect(const void *addr, size_t len, int prot);
libc = CDLL("libc.so.6")
mprotect = libc.mprotect
mprotect.restype = c_int
mprotect.argtypes = [c_void_p, c_size_t, c_int]
# PROT_xxxx constants
# Output of gcc -E -dM -x c /usr/include/sys/mman.h | grep PROT_
# #define PROT_NONE 0x0
# #define PROT_READ 0x1
# #define PROT_WRITE 0x2
# #define PROT_EXEC …Run Code Online (Sandbox Code Playgroud) 我试图在这里略微调整规则和malloc缓冲区,然后将函数复制到缓冲区.
调用缓冲函数有效,但是当我试图调用其中的另一个函数时,该函数会抛出分段错误.
有什么想法?
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
int foo(int x)
{
printf("%d\n", x);
}
int bar(int x)
{
}
int main()
{
int foo_size = bar - foo;
void* buf_ptr;
buf_ptr = malloc(1024);
memcpy(buf_ptr, foo, foo_size);
mprotect((void*)(((int)buf_ptr) & ~(sysconf(_SC_PAGE_SIZE) - 1)),
sysconf(_SC_PAGE_SIZE),
PROT_READ|PROT_WRITE|PROT_EXEC);
int (*ptr)(int) = buf_ptr;
printf("%d\n", ptr(3));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
此代码将抛出一个段错误,除非我将foo函数更改为:
int foo(int x)
{
//Anything but calling another function.
x = 4;
return x;
}
Run Code Online (Sandbox Code Playgroud)
注意:
代码成功复制foo到缓冲区,我知道我做了一些假设,但在我的平台上他们没问题.
我一直试图先使用mprotect反对阅读,然后写作.
这是我的代码
#include <sys/types.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int pagesize = sysconf(_SC_PAGE_SIZE);
int *a;
if (posix_memalign((void**)&a, pagesize, sizeof(int)) != 0)
perror("memalign");
*a = 42;
if (mprotect(a, pagesize, PROT_WRITE) == -1) /* Resp. PROT_READ */
perror("mprotect");
printf("a = %d\n", *a);
*a = 24;
printf("a = %d\n", *a);
free (a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在Linux下这里是结果:
以下是输出PROT_WRITE:
$ ./main
a = 42
a = 24
Run Code Online (Sandbox Code Playgroud)
并为 PROT_READ
$ ./main
a = 42
Segmentation …Run Code Online (Sandbox Code Playgroud) 当两个进程共享打开的一段内存shm_open然后对其进行mmap-ed 时,在一个进程中对共享内存的一部分进行操作是否会影响另一个进程在同一部分上mprotect看到的权限?换句话说,如果一个进程将共享内存段的一部分设置为只读,那么它对于另一进程也将变为只读吗?
这基本上就是我想要做的,
#include <sys/mman.h>
int zero() {
return 0;
}
int main(int argc, const char *argv[]) {
return mprotect((void *) &zero, 4096, PROT_READ | PROT_WRITE);
}
Run Code Online (Sandbox Code Playgroud)
所以我试图使代码可写,本质上。这在当前的 macOS(Catalina 10.15.2)上不起作用,它只是返回-1并设置errno为EACCES,据我所知,这是因为缺乏权利/代码签名。我找到了我需要设置的权利,但我不知道如何去做,也不知道如何实际签署它..
如果我运行codesign -d --entitlements :- <path_to_app>,它会失败并显示code object is not signed at all,即使我已经尝试在 Xcode 中配置签名一段时间(我有证书等等)。那么我应该怎么做呢?实际上用 Xcode 签署它并不明显,所以我相当无能为力。
我在一个Linux内核模块中,比如说,我分配了一些内存vmalloc().我想让内存具有读,写和执行权限.干净,适当的方式是什么?基本上,这通常相当于调用mprotect(),但在内核空间中.
如果我做的页面走,pgd_offset(),pud_offset(),pmd_offset(),pte_offset_map(),然后pte_mkwrite(),我碰上链接错误,当我试图在2.6.39.此外,似乎如果我正在进行页面漫步,它是一个黑客,应该有一个更清洁,更合适的方法.
我的内核模块将是一个可加载模块,因此我无法使用内部符号.
在此先感谢您的指导.
出于并发/并行 GC 的目的,我对 mprotect 系统调用提供的内存顺序保证感兴趣(即 mprotect 多线程的行为或 mprotect 的内存模型)。我的问题是(假设没有编译器重新排序或有足够的编译器障碍)
如果线程 1 由于线程 2 上的 mprotect 而触发地址上的段错误,我能否确定在线程 1 中的段错误信号处理程序中可以观察到系统调用之前,一切都发生在线程 2 上?如果在线程 1 上执行加载之前在信号处理程序中放置了一个完整的内存屏障怎么办?
如果线程 1 在线程 2 设置为 PROT_NONE 的地址上执行易失性加载并且没有触发段错误,那么这是否足以在两者之间发生关系之前发生。或者换句话说,如果两个线程都这样做(*ga开始为0,p是一个以只读方式开始的页面对齐地址)
// thread 1
*ga = 1;
*(volatile int*)p; // no segfault happens
// thread 2
mprotect(p, 4096, PROT_NONE); // Or replace 4096 by the real userspace-visible page size
a = *ga;
Run Code Online (Sandbox Code Playgroud)
是否可以保证a在线程 2 上会出现1?(假设在线程 1 上没有观察到段错误并且没有其他代码修改*ga)
我最感兴趣的是 Linux 行为,尤其是 …
我想知道是否有办法在Linux进程的地址空间中写入保护每个页面(从进程本身的内部,通过
mprotect()).通过"每一页",我的意思是进程的地址空间的每个页面都可以由用户模式下运行的普通程序写入 - 所以,程序文本,常量,全局变量和堆 - 但是我会对常量,全局变量和堆很满意.我不想写保护堆栈 - 这似乎是一个坏主意.
一个问题是我不知道从哪里开始写保护内存.看一下/proc/pid/maps,它显示了给定pid使用的内存部分,它们似乎始终以地址0x08048000和程序文本开头
.(在Linux中,据我所知,进程的内存是使用底部的程序文本,然后是常量,然后是全局,然后是堆,然后是一个不同大小的空白空间,具体取决于大小堆栈或堆栈,然后堆栈从虚拟地址的内存顶部向下生长0xffffffff.)有一种方法可以告诉堆顶部的位置(通过调用sbrk(0),它只返回指向当前"break"的指针,即,堆的顶部),但不是真正告诉堆开始的方式.
如果我尝试保护所有页面0x08048000直到休息,我最终会收到mprotect: Cannot allocate memory错误.我不知道为什么mprotect要分配内存 - 而Google并不是很有帮助.有任何想法吗?
顺便说一句,我想这样做的原因是因为我想创建一个在程序运行期间写入的所有页面的列表,我能想到这样做的方法是写保护所有页面,让任何尝试的写入导致写入错误,然后实现写入错误处理程序,将页面添加到列表,然后删除写保护.我想我知道如何实现处理程序,只要我能找出要保护的页面以及如何操作.
谢谢!