SYSCALL_INLINE в Android

Мне нужно использовать системный вызов внутри Android NDK, чтобы предотвратить перехват функций-оболочек. В Linux есть такие макросы, как SYSCALL_INLINE, которые позволяют использовать системный вызов без функции-оболочки. Таким образом, макрос встраивает ассемблерный код системного вызова непосредственно в проект.

Я не смог найти аналогичный макрос в Android NDK.

Может быть, я смогу написать свои собственные функции, подобные этой; https://git.busybox.net/uClibc/tree/libc/sysdeps/linux/arm/syscall.c

Но мне нужны версии одной и той же функции для рук, arm_64, x86 и x86_64.

Можете вы помочь мне? Как найти решение?


person Phillip    schedule 29.05.2018    source источник
comment
Я могу себе представить этот (frida.re) или этот (software.intel.com/en-us/articles/) или даже этот (linux.die.net/man/1/ltrace) @AlexCohn   -  person Alex Cohn    schedule 29.05.2018
comment
Вы легенда! Очень хороший ответ. Что такое макросы SYSCALL_LL_E и __SYSCALL_LL_O? Нужно ли мне заботиться о __LP64 или нет?   -  person solidak    schedule 20.05.2019


Ответы (1)


Ядро Linux для Android по-прежнему использует те же номера системных вызовов и ABI, что и обычный Linux, не так ли? (Итак, Как получить доступ к системному вызову из пользовательского пространства?) Таким образом, вы сможете использовать обычные методы с номерами вызовов из <asm/unistd.h>.

Вы можете использовать встроенные функции системного вызова MUSL libc в arch/x86_64/syscall_arch.h< /а>. У него разные для каждого разного количества аргументов вместо одного большого.


У MUSL есть версии syscall_arch.h для ARM, AArch64, i386 и x86-64, а также другие поддерживаемые им архитектуры. Он находится под лицензией разрешающей лицензии MIT, так что вы можете просто скопировать эти заголовки.

Например, их ARM-версия имеет

static inline long __syscall3(long n, long a, long b, long c)
{
    register long r7 __ASM____R7__ = n;  // macro trickery for not clobbering r7 in thumb mode (where it may be the frame pointer)
    register long r0 __asm__("r0") = a;
    register long r1 __asm__("r1") = b;
    register long r2 __asm__("r2") = c;
    __asm_syscall(R7_OPERAND, "0"(r0), "r"(r1), "r"(r2));
 // FIXME: add a "memory" clobber because pointed-to memory can be an input or output
}

К сожалению, это не совсем безопасно: это не сообщает компилятору, что операнды указателя разыменованы, поэтому он может рассматривать записи в буфер до write() как мертвые и оптимизировать их!

Это легко исправить: добавьте "memory" клобер.

IDK, если это было частью мотивации glibc для удаления аналогичных макросов системных вызовов и предоставления только не встроенной функции системного вызова. Или, может быть, они не хотели побуждать людей встраивать ABI системных вызовов в свои программы, чтобы теоретически он мог измениться и стать более эффективным в будущем.

Вы бы использовали его как

#include <asm/unistd.h>   // for __NR_write
#include <stdlib.h>       // for ssize_t
#include "syscall_arch.h"

// doesn't set errno or force all error returns to -1
// return values from -1 to -4095 are errors, e.g. -EBADF or -EFAULT

__attribte__((noinline))  // hack for inline asm unsafety
ssize_t my_write(int fd, const void *buf, size_t count) {
    return __syscall3(__NR_write, fd, (long)buf, count);
}

И, конечно же, эта функция может быть встроена в вызывающую программу, поэтому сохранение/восстановление r7 происходит один раз для всей функции.

my_write:
    str     r7, [sp, #-4]!
    mov     r7, #4
@ system-calling convention mostly matches function-calling convention
@ so args are in the right registers already
    svc 0
    ldr     r7, [sp], #4
    bx      lr

(редактировать): это было бы небезопасно, если бы было встроено в вызывающую программу, где мертвые хранилища могли бы оптимизироваться. Лучшим вариантом грубой силы будет стирание памяти во встроенном ассемблере, или дополнительная работа будет заключаться в добавлении фиктивного операнда памяти для системных вызовов, которые считывают или записывают память пользовательского пространства (см. at&t asm встроенная проблема С++). Или для munmap, чтобы убедиться, что не сохраняется в освобождаемые страницы проходят мимо него и происходят после того, как память не отображается.

От какой угрозы вы хотите защититься? Обычно на Android ваше приложение работает в отдельной песочнице, и ни один ненадежный агент не может перехватить функции оболочки системного вызова.

person Peter Cordes    schedule 29.05.2018
comment
@Phillip: Насколько я знаю, файлы заголовков Just Work. Я не смотрел на AArch64, полагаю, там вы нашли _1_? Это будет установлено для ABI, где _2_ и указатели являются 64-битными, или не определены для модели ILP32 (32-битные указатели в 64-битном режиме). Вам нужно беспокоиться об этом только в том случае, если Android NDK не определяет его, когда это необходимо. - person Phillip; 30.05.2018
comment
Спасибо большое, вы лучшие. Я вижу, что LP64 уже определен в заголовках Android NDK. - person Peter Cordes; 30.05.2018
comment
Я получаю эту ошибку для i386 _1_ Я установил директиву #if i386, чтобы отключить этот код для других процессоров У вас есть идеи? - person Phillip; 30.05.2018
comment
О, я не смотрел, что MUSL сделал для i386. Но да, на 32-битной платформе x86 лучший способ сделать системные вызовы — это function my_write(int, void const*, unsigned int): error: undefined reference to '__vsyscall' перейти на страницу кода, которую ядро ​​отображает в виртуальную память процессов пользовательского пространства (с метаданными разделяемой библиотеки ELF). См. blog.packagecloud. io/eng/2016/04/05/ для получения дополнительной информации о VDSO на случай, если вам нужно что-то сделать, чтобы связать его с i386. Но почему вы вообще включаете определения i386 для других архитектур? Вы хотите включить ровно одну версию заголовка, предназначенную для целевой архитектуры. - person Phillip; 30.05.2018
comment
Я включаю файлы заголовков в препроцессоры call, _2_, _3_, _4_, чтобы выбрать правильный для текущей архитектуры. Мое приложение теперь поддерживает 4 платформы (arm, arm_64, x86, x86_64). Но x86 генерирует эту ошибку. Я не хочу связывать какую-либо другую библиотеку, нельзя ли использовать ассемблерный код вместо _5_ или другого решения? - person Peter Cordes; 30.05.2018
comment
@Phillip: ты читал эту ссылку? Это не библиотека на диске, это код пользовательского пространства, предоставляемый непосредственно ядром. Но да, вы можете использовать более медленный #if __arm__ 32-битный ABI. - person Phillip; 30.05.2018
comment
Привет, @Peter, я все еще не мог понять проблему C ++, я заменил int 0x80 на переменную tmp, но должен ли я что-то делать с переменной tmp? Я добавил свой последний код макроса в чат. - person Peter Cordes; 30.05.2018
comment
Я положил это в обозревателе компиляторов Godbolt, скопировав достаточное количество ARM _8_ для компиляции. В некоторых установках Godbolt ARM gcc отсутствует _9_, а у gcc5.4 есть рабочий, результат в ARM режиме такой: - person Phillip; 04.06.2018