Reputation: 745
I have looked at the various topics relating to this, but couldn't find this specific issue I am having.
Things I looked at: Injecting code into executable at runtime C SIGSEGV Handler & Mprotect Can I write-protect every page in the address space of a Linux process? How to write a signal handler to catch SIGSEGV?
I am able to handle SIGSEGV gracefully when the protection needs to be set to either PROT_READ or PROT_WRITE in the handler. However, when I try to inject instructions with mmap, and then use mprotect to set it to PROT_READ only, and then I execute the instructions via inline assembly, it causes a SIGSEGV as intended, but the handler is unable to get the originating address causing the signal, so I am unable to mprotect it to PROT_READ | PROT_EXEC.
Example:
void sigHandler(int signum, siginfo_t *info, void *ptr) {
printf("Received signal number: %d\n", signum);
printf("Signal originates from process %lu\n",
(unsigned long)info->si_pid);
printf("SIGSEGV caused by this address: ? %p\n", info->si_addr);
char * alignedbaseAddr = (((unsigned int)(info->si_addr)) >> 12) * getPageSize();
printf("Aligning to %p\n", alignedbaseAddr);
//flip this page to be r+x
mprotect(alignedbaseAddr, getPageSize(), PROT_READ | PROT_EXEC);
}
void setupSignalHandler() {
action.sa_sigaction = sigHandler;
action.sa_flags = SA_SIGINFO;
sigemptyset(&action.sa_mask);
sigaction(SIGSEGV, &action, NULL);
}
int main(int argc, char *argv[]) {
char * baseAddr = (char*)mmap(NULL, getDiskSize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(baseAddr == MAP_FAILED) {
perror("Unable to mmap.");
}
printf("Process address space is %d\n", getDiskSize());
//no-op filler
for(int i = 0; i < (getDiskSize()) - 1; i++) {
baseAddr[i] = 0x90;
}
//ret instruction
baseAddr[i] = 0xc3;
if( mprotect(baseAddr, getDiskSize(), PROT_READ) == -1) {
perror("mprotect");
exit(1);
}
printf("Protecting addresses: %p to %p for READ_ONLY\n", baseAddr, baseAddr + getDiskSize() - 1);
setupSignalHandler();
__asm__
(
"call %%eax;"
: "=a" (output)
: "a" (baseAddr)
);
printf("Will this ever print?");
//close fd, and unmap memory
cleanUp();
return EXIT_SUCCESS;
}
Here is the resulting output:
Received signal number: 11
Signal originates from process 0
SIGSEGV caused by this address: ? (nil)
//the above output repeatedly loops, since it fails to "re mprotect" that page.
Architecture: x86 32 bit OS: Ubuntu 11.04 - Linux version 2.6.38-12-generic (buildd@vernadsky) (gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4) )
Any ideas? The above logic works fine for simply read and writing into memory. Is there a better way to execute instructions at runtime as opposed to inline assembly?
Thanks in advance!
Upvotes: 2
Views: 1597
Reputation: 1
In that case, the faulting address is the instruction pointer. Cast your third argument ptr
(of your signal handler installed with SA_SIGINFO
) to a ucontext_t
, and retrieve the appropriate register, perhaps as (untested code!)
ucontext_t *uc = ptr;
void* faultyip = uc->uc_mcontext.gregs[REG_IP];
Read carefully /usr/include/sys/ucontext.h
for more.
I'm interested to know why you are asking!!
Upvotes: 2