如何使用sigsegv捕获内存读取和写入?
如何欺骗Linux认为内存读/写成功? 我正在编写一个C ++库,以便所有读/写操作都可以重定向到最终用户并进行透明处理。 任何时候写入或读取变量时,库都需要捕获该请求,并将其发送到可处理来自那里的数据的硬件模拟。
请注意,我的图书馆依赖于以下平台:
Linux ubuntu 3.16.0-39-generic#53〜14.04.1 -Ubuntu SMP x86_64 GNU / Linux
gcc(Ubuntu 4.8.2-19ubuntu1)4.8.2
当前方法:捕获SIGSEGV并增加REG_RIP
我目前的方法涉及使用mmap()
获取内存区域,并使用mprotect()
关闭访问。 我有一个SIGSEGV处理程序来获取包含内存地址的信息,在其他地方导出读/写,然后递增上下文REG_RIP。
void handle_sigsegv(int code, siginfo_t *info, void *ctx)
{
void *addr = info->si_addr;
ucontext_t *u = (ucontext_t *)ctx;
int err = u->uc_mcontext.gregs[REG_ERR];
bool is_write = (err & 0x2);
// send data read/write to simulation...
// then continue execution of program by incrementing RIP
u->uc_mcontext.gregs[REG_RIP] += 6;
}
这适用于非常简单的情况,例如:
int *num_ptr = (int *)nullptr;
*num_ptr = 10; // write segfault
但对于任何更复杂的事情,我都会收到一个SIGABRT:
30729非法指令(核心转储)./$target
在SIGSEGV处理程序中使用mprotect()
如果我不增加REG_RIP, handle_sigsegv()
将被内核一次又一次地调用,直到内存区域变得可用于读取或写入。 我可以针对该特定地址运行mprotect()
,但有多个注意事项:
mprotect()
将在一天结束时执行读取或写入内存,使我的库的用例无效。 编写设备驱动程序
我还试图编写一个设备模块,以便库可以在char设备上调用mmap()
,驱动程序将在其中处理读取和写入操作。 这在理论上是有道理的,但我还没有能够(或者没有这方面的知识)抓住每一次加载/存储处理器问题的设备。 我试图覆盖映射的vm_operations_struct
和/或inode的address_space_operations
结构,但只会在页面发生故障或页面刷新到后台存储时调用读/写操作。
也许我可以在无处写入数据的设备(类似于/dev/null
)上使用mmap()
和mprotect()
如上所述),然后创建一个进程来识别读/写并从那里路由数据(? )。
利用syscall()
并提供一个恢复器组合函数
以下内容来自segvcatch
project1,它将segfaults转换为异常。
#define RESTORE(name, syscall) RESTORE2(name, syscall)
#define RESTORE2(name, syscall)
asm(
".textn"
".byte 0n"
".align 16n"
"__" #name ":n"
" movq $" #syscall ", %raxn"
" syscalln"
);
RESTORE(restore_rt, __NR_rt_sigreturn)
void restore_rt(void) asm("__restore_rt") __attribute__
((visibility("hidden")));
extern "C" {
struct kernel_sigaction {
void (*k_sa_sigaction)(int, siginfo_t *, void *);
unsigned long k_sa_flags;
void (*k_sa_restorer)(void);
sigset_t k_sa_mask;
};
}
// then within main ...
struct kernel_sigaction act;
act.k_sa_sigaction = handle_sigegv;
sigemptyset(&act.k_sa_mask);
act.k_sa_flags = SA_SIGINFO|0x4000000;
act.k_sa_restorer = restore_rt;
syscall(SYS_rt_sigaction, SIGSEGV, &act, NULL, _NSIG / 8);
但是这最终的功能与常规的sigaction()
配置没有区别。 如果我没有设置恢复功能,即使内存区域仍然不可用,信号处理程序也不会被多次调用。 也许在这里我可以用内核信号做一些其他的诡计。
同样,库的整个目标是透明地处理对内存的读写操作。 也许有更好的方式做事,或许用ptrace()
或甚至更新生成segfault信号的内核代码,但重要的部分是最终用户的代码不需要改变。 我已经看到使用setjmp()
和longjmp()
在segfault之后继续使用的示例,但这需要将这些调用添加到每个内存访问。 将段错误转换为try / catch也是一样。
1个segvcatch项目
您可以使用mprotect并通过让SIGSEGV处理程序在标志寄存器中设置T标志来避免您注意到的第一个问题。 然后,您添加一个SIGTRAP处理程序,恢复mprotected内存并清除T标志。
T标志使处理器单步执行,因此当SEGV处理程序返回时,它将执行该单个指令,然后立即执行TRAP。
这仍然会给你带来第二个问题 - 读/写指令实际上会发生。 您可以通过在两个信号处理程序中的指令之前和/或之后仔细修改内存来解决该问题。
链接地址: http://www.djcxy.com/p/27105.html