Running 32-bit code on a 64-bit Linux process - memory access

I am experimenting with running 32-bit code inside a 64-bit Linux process. The 32-bit code is completely autonomous, it makes direct IA32 system calls on its own. If I loaded this code into a 32-bit process, it would work fine.

At first I thought that I could just allocate a stack for 32-bit code, switch to it, and everything will work fine, but this is not so good. Mostly because the instructions associated with the stack (POP / PUSH / ...) did 8-byte shifts instead of 4 bytes.

From googling, I found out that I can switch to 32-bit mode by switching to the 0x23 segment selector. Unfortunately, segments are something that I know very little about.

I can go into 32-bit mode with something like this (AT & T built-in assembly):

movl $0x23, 4(%%rsp) // segment selector 0x23
movq %0, %%rax
movl %%eax, (%%rsp) // target 32-bit address to jump to
lret

Where% 0 contains the 32-bit address where the code is displayed. The code starts to work, I see that PUSH / POP now works as it should, but it crashes even earlier than when I ran the code in 64-bit mode on an apparently harmless instruction:

0x8fe48201      mov    0xa483c(%rbx),%ecx

Where %rbx(or more like %ebx, since this code is already 32-bit, GDB just doesn't know that) contains 0x8fe48200. The address that he is trying to read from ( 0x8feeca3c) is valid and readable (according to /proc/XXX/maps), and when I read it from inside GDB, it contains a wait value.

, Linux SIGSEGV , - 0 ( strace p $_siginfo._sifields._sigfault.si_addr gdb). - , 0x8feeca3c 32- .

, ?

UPDATE: , segfaults 0, 0 . , ( !), .

#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>

// 32-bit code we're executing
const unsigned char instructions[] = {
        0x6a, 0, // push 0
        0x58, // popl %eax
        0xe8, 0, 0, 0, 0, // call the next line to get our location in memory
        0x5b, // pop %ebx
        // THE FOLLOWING mov SEGFAULTS, but it is well within the mapped area (which has size 0x3000)
        // A simpler "mov (%ebx), %eax" (0x8b, 0x03) would fail as well
        0x8b, 0x83, 0, 0x20, 0, 0, // mov 0x2000(%ebx), %eax
        0xf4 // hlt, not reached
};

int main()
{
        void* area;
        void* stack;

        area = mmap(NULL, 3*4096, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0);
        memcpy(area, instructions, sizeof(instructions));

        stack = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1, 0);
        stack = (void*) (((uint64_t) stack) + 4096 - 4);

        memset(((char*)area) + 2*4096, 0xab, 100); // Place 0xAB in the area we mov from in 32-bit instructions

        // Switch to 32-bit mode and jump into the code
        __asm__ volatile("movq %1, %%rsp;" \
                         "subq $8, %%rsp;" \
                         "movl $0x23, 4(%%rsp);" \
                         "movq %0, %%rax;" \
                         "movl %%eax, (%%rsp);" \
                         "lret" :: "m"(area), "r"(stack) :);
}
+4
1

:)

, ds - ​​ , 64- . , , . push/pop push $0x2b; pop %ds :

const unsigned char instructions[] = {
        0x6a, 0x2b, // push $0x2b
        0x1f, // pop %ds

0x2b 32- . , push . ss 64 , ds, es.

+3

Source: https://habr.com/ru/post/1668188/


All Articles