Я просмотрел различные темы, связанные с этим, но не смог найти эту конкретную проблему, с которой я столкнулся.
Вещи, на которые я смотрел:
Внедрение кода в исполняемый файл во время выполнения
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)) *
Есть идеи? Вышеприведенная логика прекрасно работает для простого чтения и записи в память. Есть
лучший способ выполнения инструкций во время выполнения, а не встроенная сборка?
Заранее спасибо!