SIGSEGV при выполнении машинного кода из массива на ARMv6

Я пытаюсь выполнить машинный код, хранящийся в массиве на первом Raspberry Pi под ArchLinux. Я сделал это под x86, но я не могу понять, что я делаю неправильно под ARMv6. Проблема в том, что не имеет значения, какой код находится в массиве, он всегда будет аварийно завершать работу после выполнения первой инструкции. Код скомпилирован с отключенной поддержкой Thumb в gcc 5.2.0.

Вот код, который я использовал для тестирования:

#include <stdio.h>

char shellcode[] = {
 0x04, 0xb0, 0x2d, 0xe5, // push {r11}
 0x00, 0xb0, 0x8d, 0xe2, // add r11, sp, #0
 0x00, 0x00, 0xa0, 0xe1, // nop
 0x00, 0x00, 0xa0, 0xe1, // nop
 0x00, 0x00, 0xa0, 0xe1, // nop
 0x00, 0xd0, 0x4b, 0xe2, // sub sp, r11, #0
 0x04, 0xb0, 0x9d, 0xe4, // pop {r11}
 0x1e, 0xff, 0x2f, 0xe1  // bx lr
};

void shellcode2() {
 asm("mov r0, r0");
 asm("mov r0, r0");
}

typedef void (*entry_t)();

int main() {
 entry_t entry = (entry_t)(shellcode);
 entry();

 return 0;
}

Машинный код был взят из shellcode2 дизассемблирование функции, я не знаю, является ли это правильным способом сделать это, но проблема в том, что даже если первая инструкция в шеллкоде не nop - она ​​вылетит.

Program received signal SIGSEGV, Segmentation fault.
0x00020704 in shellcode ()
(gdb) disas /r
Dump of assembler code for function shellcode:
=> 0x00020704 <+0>:     04 b0 2d e5     push    {r11}           ; (str r11, [sp, #-4]!)
   0x00020708 <+4>:     00 b0 8d e2     add     r11, sp, #0
   0x0002070c <+8>:     00 00 a0 e1     nop                     ; (mov r0, r0)
   0x00020710 <+12>:    00 00 a0 e1     nop                     ; (mov r0, r0)
   0x00020714 <+16>:    00 00 a0 e1     nop                     ; (mov r0, r0)
   0x00020718 <+20>:    00 d0 4b e2     sub     sp, r11, #0
   0x0002071c <+24>:    04 b0 9d e4     pop     {r11}           ; (ldr r11, [sp], #4)
   0x00020720 <+28>:    1e ff 2f e1     bx      lr
End of assembler dump.

Я что-то упускаю или просто делаю это неправильно на ARMv6? Если кто-нибудь может указать мне правильное направление, я был бы благодарен.

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

2 ответа

Решение

Помещение комментария в ответ, чтобы будущие пользователи могли его найти.

ARMv6 поддерживает регионы "Никогда не выполнять", которые можно использовать для предотвращения выполнения страниц, содержащих данные, как код.

Это сделано в качестве меры безопасности, поскольку защищает от множества хакерских подходов. Но это может быть проблемой для программ, пытающихся сделать самоизменяющийся код (что эффективно делает OP).

Проверьте ARMv6 документы для деталей.

Вам нужно указать секцию памяти в вашем скрипте компоновщика, чтобы код выполнялся из. Например:

.umem : {
    . = ALIGN(4);
    _umem = .;
    . = . + 48k;    
} > RAM  

Где ram - это раздел, помеченный как минимум rx _umem должен быть символом по адресу, с которого начинается этот 48-килобайтный буфер, и может быть добавлен в ваш заголовочный файл с помощью оператора extern.

Другие вопросы по тегам