我想在mprotect处理程序中获取当前的程序计数器(PC)值。从那里我想将PC的值增加"n"条指令数,以便程序将跳过一些指令。我想为 linux 内核版本 3.0.1 做所有这些事情。关于我可以在其中获取PC值以及如何更新该值的数据结构的任何帮助?示例代码将不胜感激。提前谢谢。
我的想法是在写入内存地址时使用一些任务。所以我的想法是使用 mprotect 使地址写保护。当某些代码尝试在该内存地址上写入某些内容时,我将使用 mprotect 处理程序来执行某些操作。处理完处理程序后,我想使写入操作成功。所以我的想法是使处理程序中的内存地址不受保护,然后再次执行写入操作。当代码从处理程序函数返回时,PC 将指向原始写入指令,而我希望它指向下一条指令。所以我想增加一条指令,无论指令长度如何。
检查以程
MprotectHandler(){
unprotect the memory address on which protection fault arised
write it again
set PC to the next instruction of original write instruction
}
内部主功能:
main(){
mprotect a memory address
try to write the mprotected address // original write instruction
Other instruction // after mprotect handler execution, PC should point here
}
多个 CISC 处理器上计算指令长度很繁琐,因此我建议采用稍微不同的过程:使用 clone(..., CLONE_VM, ...)
分叉到跟踪器和跟踪线程中,并在跟踪器中而不是
write it again
set PC to the next instruction of original write instruction
做一个
ptrace(PTRACE_SINGLESTEP, ...)
- 在跟踪陷阱之后,您可能需要再次保护内存。
下面是演示基本原理的示例代码:
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/ucontext.h>
static void
handler(int signal, siginfo_t* siginfo, void* uap) {
printf("Attempt to access memory at address %pn", siginfo->si_addr);
mcontext_t *mctx = &((ucontext_t *)uap)->uc_mcontext;
greg_t *rsp = &mctx->gregs[15];
greg_t *rip = &mctx->gregs[16];
// Jump past the bad memory write.
*rip = *rip + 7;
}
static void
dobad(uintptr_t *addr) {
*addr = 0x998877;
printf("I'm a survivor!n");
}
int
main(int argc, char *argv[]) {
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
sigemptyset(&act.sa_mask);
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigaction(SIGSEGV, &act, NULL);
// Write to an address we don't have access to.
dobad((uintptr_t*)0x1234);
return 0;
}
它向您展示如何更新 PC 以响应页面错误。它缺少您必须自己实现的以下内容:
- 指令长度解码。如您所见,我已经硬编码了
+ 7
,它恰好适用于我的 64 位 Linux,因为导致页面错误的指令是 7 字节MOV
。正如 Armali 在他的回答中所说,这是一个乏味的问题,你可能不得不使用像 libudis86 之类的外部库。 -
mprotect()
处理。您有导致页面错误的地址siginfo->si_addr
,使用它查找受保护页面的地址并取消保护应该很简单。