Обработчик SIGSEGV и эффект mprotect и looping при вводе инструкций во время выполнения.Обработчик не может получить информацию-> si_addr - PullRequest
1 голос
/ 21 ноября 2011

Я просмотрел различные темы, связанные с этим, но не смог найти эту конкретную проблему, с которой я столкнулся.

Вещи, на которые я смотрел: Внедрение кода в исполняемый файл во время выполнения C SIGSEGV Handler & Mprotect Можно ли защитить от записи каждую страницу в адресном пространстве процесса Linux? Как написать обработчик сигнала для перехвата SIGSEGV?

Я могу корректно обрабатывать SIGSEGV, когда в обработчике нужно установить либо PROT_READ, либо PROT_WRITE. Однако, когда я пытаюсь внедрить инструкции с помощью mmap, а затем использую mprotect, чтобы установить для него только PROT_READ, а затем я выполняю инструкции через встроенную сборку, это вызывает SIGSEGV, как и предполагалось, но обработчик не может получить исходный адрес, вызывающий сигнал, поэтому я не могу защитить его до PROT_READ | PROT_EXEC.

Пример:

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;
}

Вот результат:

Номер полученного сигнала: 11
Сигнал исходит из процесса 0
SIGSEGV вызван этим адресом: (Ноль)

// вышеприведенный вывод многократно повторяется, так как не удается «защитить» эту страницу.

Архитектура: x86 32 бит ОПЕРАЦИОННЫЕ СИСТЕМЫ: Ubuntu 11.04 - версия Linux 2.6.38-12-generic (buildd @ vernadsky) (gcc версия 4.5.2 (Ubuntu / Linaro 4.5.2-8ubuntu4)) *

Есть идеи? Вышеприведенная логика прекрасно работает для простого чтения и записи в память. Есть лучший способ выполнения инструкций во время выполнения, а не встроенная сборка?

Заранее спасибо!

1 Ответ

1 голос
/ 21 ноября 2011

В этом случае ошибочный адрес является указателем инструкции.Приведите третий аргумент ptr (вашего обработчика сигнала, установленного с SA_SIGINFO) к ucontext_t и получите соответствующий регистр, возможно, как ( непроверенный код! )

ucontext_t *uc = ptr;
void* faultyip = uc->uc_mcontext.gregs[REG_IP];

Внимательно прочитайте /usr/include/sys/ucontext.h.

Мне интересно узнать, почему вы спрашиваете !!

...