Skip to content
Advertisement

Running 32-bit code in 64-bit process on Linux – memory access

I’m experimenting with running 32-bit code inside a 64-bit Linux process. The 32-bit code is completely self-contained, it makes direct IA32 system calls on its own. If I were to load this code in a 32-bit process, it would run just fine.

Initially, I thought I could just allocate a stack for the 32-bit code, switch to it and everything would work fine, but that didn’t go so well. Mainly because stack-related instructions (POP/PUSH/…) were doing 8-byte shifts instead of 4 bytes.

By googling, I learned that I can transition to 32-bit mode by switching to segment selector 0x23. Unfortunately, segments are something I know very little about.

I am able to transition to 32-bit mode with something like this (inline AT&T assembly):

JavaScript

Where %0 contains a 32-bit address of where the code is mapped. The code starts running, I can see that PUSH/POP now works the way it should, but it crashes even earlier than when I ran the code in 64-bit mode on a seemingly innocuous instruction:

JavaScript

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

Yet, Linux sends a SIGSEGV to the process on this instruction and the faulting address is 0 (as reported by strace or p $_siginfo._sifields._sigfault.si_addr inside gdb). Somehow it seems 0x8feeca3c is not a valid address in the 32-bit realm.

Any ideas how to proceed?

UPDATE: I have written a minimal example that segfaults reading address 0, although address 0 is not really being referenced. It seems that reading any addresses in memory fails (even reading the address of the instruction that has just been executed!), although stack operations work OK.

JavaScript

Advertisement

Answer

Nice question 🙂

The problem is that ds is still set to zero, in 64 bit mode it’s not used. So, you need to reload that and it will work. Changing your initial test push/pop to push $0x2b; pop %ds will do the trick:

JavaScript

I extracted the 0x2b value from a 32 bit program. I just kept wondering why push worked. On closer look, ss is set in 64 bit mode too, so it may be safer to copy that to ds as well as to es.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement