3

I want to get the current program counter(PC) value inside mprotect handler. From there I want to increase the value of PC by 'n' number of instruction so that the program will skip some instructions. I want to do all that for linux kernel version 3.0.1. Any help about the data structures where I can get the value of PC and how to update that value? Sample code will be appreciated. Thanks in advance.

My idea is to use some task when a memory address is being written. So my idea is to use mprotect to make the address write protected. When some code tries to write something on that memory address, I will use mprotect handler to perform some operation. After taking care of the handler, I want to make the write operation successful. So my idea was to make the memory address unprotected inside handler and then perform the write operation again. When the code returns from the handler function, the PC will point to the original write instruction, whereas I want it to point to the next instruction. So I want to increase PC by one instruction irrespective of instruction lenght.

Check the following flow

MprotectHandler(){
    unprotect the memory address on which protection fault arised
    write it again
    set PC to the next instruction of original write instruction
}

inside main function:

main(){
    mprotect a memory address
    try to write the mprotected address // original write instruction
    Other instruction    // after mprotect handler execution, PC should point here
}
azizulhakim
  • 658
  • 7
  • 24
  • What are you *actually* trying to accomplish? Are you *modifying* `mprotect`? What architecture is this for? Fixed-width or variable-width instruction set? What instructions are you trying to skip over? `mprotect` is a system call... the system call is going to return to the process after it finishes. – Jonathon Reinhart Nov 03 '13 at 04:51

2 Answers2

1

Since it is tedious to compute the instruction length on several CISC processors, I recommend a somewhat different procedure: Fork using clone(..., CLONE_VM, ...) into a tracer and a tracee thread, and in the tracer instead of

    write it again
    set PC to the next instruction of original write instruction

do a

    ptrace(PTRACE_SINGLESTEP, ...)

- after the trace trap you may want to protect the memory again.

Armali
  • 18,255
  • 14
  • 57
  • 171
1

Here is sample code demonstrating the basic principle:

#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 %p\n", 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;
}

It shows you how to update the PC in response to a page fault. It lacks the following which you have to implement yourself:

  • Instruction length decoding. As you can see I have hardcoded + 7 which happens to work on my 64bit Linux since the instruction causing the page fault is a 7 byte MOV. As Armali said in his answer, it is a tedious problem and you probably have to use an external library like libudis86 or something.
  • mprotect() handling. You have the address that caused the page fault in siginfo->si_addr and using that it should be trivial to find the address of the mprotected page and unprotect it.
Björn Lindqvist
  • 19,221
  • 20
  • 87
  • 122