__asm__ gcc вызов адреса памяти

0

У меня есть код, который выделяет память, копирует некоторый буфер в выделенную память, а затем перескакивает на этот адрес памяти.

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

Я хочу сделать что-то вроде:

address=VirtualAlloc(NULL,len+1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
dest=strncpy(address, buf, len);

И тогда я хочу сделать это в ASM:

MOV EAX, dest
CALL EAX.

Я пробовал что-то вроде:

  __asm__("movl %eax, dest\n\t"
 "call %eax\n\t");

Но это не работает. Как мне это сделать?

  • 1
    Привести указатель на указатель на функцию и вызвать его?
  • 0
    В синтаксисе AT & T movl %eax, dest - это хранилище, а не загрузка.
Теги:
gcc
assembly
memory
inline-assembly

1 ответ

4
Лучший ответ

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

Вам нужно использовать __builtin___clear_cache(buf, buf+len) после копирования машинного кода в буфер, прежде чем разыменовать указатель на функцию, иначе он может быть оптимизирован как мертвое хранилище. , В x86 имеется согласованный кэш инструкций, поэтому он не компилируется ни с какими дополнительными инструкциями, но он все еще нужен, чтобы оптимизатор знал, что происходит.

static inline
int func(char *dest, int len) {
    __builtin___clear_cache(dest, dest+len); // no instructions on x86 but still needed
    int ret = ((int (*)(void))dest)();   // cast to function pointer and deref
    return ret;
}

компилируется с GCC9.1 -O2 -m32 в

func(char*, int):
    jmp     [DWORD PTR [esp+4]]    # tailcall

Кроме того, вам на самом деле не нужно копировать строку, вы можете просто mprotect или VirtualProtect страницу, чтобы сделать ее исполняемой. Но если вы хотите убедиться, что он останавливается на первом 0 байте для проверки вашего шелл-кода, то обязательно скопируйте его.


Если вы все же настаиваете на inline asm, вы должны знать, что gcc inline asm - сложная вещь. Кроме того, если вы ожидаете, что функция вернется, вы должны убедиться, что она соответствует соглашению о вызовах, в частности, она сохраняет регистры, которые должны.

Синтаксис AT & T - это op src, dst поэтому ваш mov фактически был хранилищем глобального символа dest.

Тем не менее, вот ответ на вопрос как сформулировано:

int ret;
__asm__ __volatile__ ("call *%0" : "=a" (ret) : "0" (dest) : "ecx", "edx", "memory");

Объяснение: https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html.

call *%0= %0 относится к первому замещенному аргументу, * - стандартный синтаксис gas для косвенного вызова

"=a" (ret)= выходной аргумент в регистре eax должен быть назначен переменной ret после блока

"0" (dest)= входной аргумент в том же месте, что и выходной аргумент 0 (который является eax) должен быть загружен из dest перед блоком

"ecx", "edx"= сообщают компилятору, что эти регистры могут быть изменены блоком asm согласно обычному соглашению о вызовах.

"memory"= сообщить компилятору, что блок asm может внести неопределенные изменения в память, поэтому ничего не кэшируйте


Обратите внимание, что в x86-64 System V (Linux/OS X) небезопасно выполнять вызов функции из встроенного asm, как это. Там нет никакого способа объявить клоббер на красной зоне ниже RSP.

  • 0
    Это работает как магия! Не могли бы вы объяснить этот конкретный пример в ответе? Спасибо! ;)
  • 0
    Потрясающий ответ!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню