This might be an exact duplicate of Is it possible to execute 32-bit code in 64-bit process by doing mode-switching?, but that question is from a year ago and only has one answer that doesn't give any source code. I'm hoping for more detailed answers.
I'm running 64-bit Linux (Ubuntu 12.04, if it matters). Here's some code that allocates a page, writes some 64-bit code into it, and executes that code.
#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
}
Now, purely for entertainment value, I'd like to do the same thing but with buffer
containing arbitrary 32-bit (ia32) code, instead of 64-bit code. This page implies that you can execute 32-bit code on a 64-bit processor by entering "long compatibility sub-mode", by setting the bits of the CS segment descriptor as LMA=1, L=0, D=1
. I am willing to wrap my 32-bit code in a prologue/epilogue that performs this setup.
But can I do this setup, in Linux, in usermode? (BSD/Darwin answers will also be accepted.) This is where I start to get really hazy on the concepts. I think the solution involves adding a new segment descriptor to the GDT (or is it the LDT?), and then switching to that segment via an lcall
instruction. But can all that be done in usermode?
Here's a sample function that should return 4 when successfully run in compatibility sub-mode, and 8 when run in long mode. My goal is to get the instruction pointer to take this codepath and come out the other side with %rax=4
, without ever dropping into kernel mode (or doing so only via documented system calls).
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
solves the question answered in https://mcmap.net/q/19861/-allocating-a-data-page-in-linux-with-nx-bit-turned-off (how to get a new executable page); my main question is about compatibility sub-mode. I believe the x32 ABI is completely irrelevant — x32 is just a wacky ABI used by wacky systems in the regular 64-bit long mode, whereas what I want to do is actually switch the decoder into 32-bit compatibility sub-mode. (In other words, my question is not related to the ABI at all; it's related to the processor mode.) – Poulini386_set_ldt
and Linux hasmodify_ldt
but I don't understand what they do. – Poulinlcall
and set your data descriptors (ds
,ss
) – Suribachimmap
with the flagsMAP_ANONYMOUS|MAP_32BIT
to acquire a page in low(high) memory. Re the LDT, I'm still holding out hope that someone will give me teh codes. I think I sort of understand what the actuallgdt
andlldt
instructions do, but I thinkmodify_ldt
is something higher-level that doesn't wipe out the entire table; you can actually add new entries to the existing LDT somehow. Perhaps I should study this sample code. – Poulin