Это может быть точная копия Можно ли выполнить 32-битный код в 64-битном процессе, выполнив переключение режимов?, но этот вопрос задан год назад и имеет только один ответ, который не дает любой исходный код. Надеюсь на более подробные ответы.
Я использую 64-битный Linux (Ubuntu 12.04, если это важно). Вот код, который выделяет страницу, записывает в нее некоторый 64-битный код и выполняет этот код.
#include <assert.h>
#include <malloc.h>
#include <stdio.h>
#include <sys/mman.h> // mprotect
#include <unistd.h> // sysconf
unsigned char test_function[] = { 0xC3 }; // RET
int main()
{
int pagesize = sysconf(_SC_PAGE_SIZE);
unsigned char *buffer = memalign(pagesize, pagesize);
void (*func)() = (void (*)())buffer;
memcpy(buffer, test_function, sizeof test_function);
// func(); // will segfault
mprotect(buffer, pagesize, PROT_EXEC);
func(); // works fine
}
Теперь, чисто для развлечения, я хотел бы сделать то же самое, но с buffer
, содержащим произвольный 32-битный (ia32) код вместо 64-битного кода. На этой странице подразумевается, что вы можете выполнить 32 -битовый код на 64-битном процессоре путем входа в «подрежим долгой совместимости», путем установки битов дескриптора сегмента CS как LMA=1, L=0, D=1
. Я готов заключить свой 32-битный код в пролог / эпилог, который выполняет эту настройку.
Но можно выполнить эту настройку в Linux в пользовательском режиме? (Ответы BSD / Дарвина также будут приняты.) Вот здесь я начинаю очень расплываться в концепциях. Я думаю, что решение включает добавление нового дескриптора сегмента в GDT (или это LDT?), А затем переключение на этот сегмент с помощью инструкции lcall
. Но можно ли все это сделать в пользовательском режиме?
Вот пример функции, которая должна возвращать 4 при успешном запуске в подрежиме совместимости и 8 при запуске в длительном режиме. Моя цель - заставить указатель инструкции взять этот кодовый путь и выйти с другой стороны с %rax=4
, никогда не переходя в режим ядра (или делая это только с помощью задокументированных системных вызовов).
unsigned char behave_differently_depending_on_processor_mode[] = {
0x89, 0xE0, // movl %esp, %eax
0x56, // push %{e,r}si
0x29, 0xE0, // subl %esp, %eax
0x5E, // pop %{e,r}si
0xC3 // ret
};
mprotect
решает вопрос, на который дан ответ на stackoverflow.com/a/12712639/841108 (как получить новую исполняемую страницу ); мой главный вопрос касается подрежима совместимости. Я считаю, что x32 ABI совершенно неуместен - x32 - это просто дурацкий ABI, используемый дурацкими системами в обычном 64-битном длинном режиме, тогда как я хочу фактически переключить декодер в подрежим 32-битной совместимости. (Другими словами, мой вопрос вообще не имеет отношения к ABI; он связан с режимом процессора.) - person Quuxplusone   schedule 04.10.2012i386_set_ldt
, а у Linuxmodify_ldt
, но я не понимаю, что они делают. - person Quuxplusone   schedule 04.10.2012lcall
и установить дескрипторы данных (_2 _, _ 3_) - person ughoavgfhw   schedule 04.10.2012mmap
с флагамиMAP_ANONYMOUS|MAP_32BIT
для получения страницы в низкой (высокой) памяти. Что касается LDT, я все еще надеюсь, что кто-нибудь даст мне коды. Я думаю, что понимаю, что делают настоящие инструкцииlgdt
иlldt
, но я думаю, чтоmodify_ldt
- это что-то более высокоуровневое, которое не уничтожает всю таблицу; вы можете как-то добавить новые записи в существующий LDT. Возможно, мне стоит изучить этот пример кода. - person Quuxplusone   schedule 05.10.2012