Vlad Vlad - 1 month ago 9
C Question

C preprocessor and "_asm _emit" directive

Im trying to achieve a boolean ariphmetics along with the

_asm _emit
in Visual Studio 2015, want to insert some x64 opcodes in x86 program, i must do a lot of
mov
to memory commands, so i tried to make some kind of macro, which take the address and emits in little-endian so :

#define EmitDword(x)\
{\
_asm _emit (x & 0x000000FF) \
_asm _emit ((x >> 8) & 0x000000FF) \
_asm _emit ((x >> 16) & 0x000000FF) \
_asm _emit ((x >> 24) & 0x000000FF) \
}


But i get an error
inline assembler syntax error in 'first operand';

The idea is, to pass an address of a variable to the macro, so it would be emmited directly into machine code, then i could do something like this :

#define EmitDword(x)\
{\
_asm _emit (x & 0x000000FF) \
_asm _emit ((x >> 8) & 0x000000FF) \
_asm _emit ((x >> 16) & 0x000000FF) \
_asm _emit ((x >> 24) & 0x000000FF) \
}

/* mov qword ptr [addr],reg */
#define X64_MovToMem(addr,reg)\
{\
_asm _emit 0x48\
_asm _emit 0x89\
_asm _emit reg\
_asm _emit 0x25\
EmitDword(addr)\
}

#define _rax 4
void test()
{
DWORD64 someData;
X64_MovToMem(&someData,_rax);
}


Is there any way to achieve the emitting of non-constant values using preprocessor inline assembly?

Answer

You don't need to mess around with 64-bit code in a 32-bit process to access the 64-bit Process Environment Block. You can obtain its address using 32-bit code, and it's located within the 32-bit address space. You'd only need to use 64-bit code if you need to access memory allocated outside the 32-bit address space, and I don't think Windows will ever do that in a 32-bit process.

If you really did need to have a 64-bit function in a 32-bit executable there's a better way of doing it than using the _asm _emit. The first thing to do is to write your entire 64-bit function in plain assembly and assemble it with an normal external assembler. For example, here's a function that reads from a 64-bit pointer in MASM syntax:

_TEXT   SEGMENT
__read64ptr:
    mov rax, [rsp + 8]
    mov eax, [rax]
    mov edx, [rax + 4]
    retf
_TEXT   ENDS
    END

This simple function takes a 64-bit pointer as an argument on the stack. A 64-bit value located at address pointed to is put into EAX and EDX. This function is meant to called with a 32-bit far call instruction.

Note that the return value takes up two 32-bit stack slots, one for the 32-bit offset of the return address and another for the selector. Despite the fact that the RETF instruction is executed in 64-bit mode, it uses a 32-bit stack size by default (unlike the 64-bit near RET instruction) and will work correctly with the 32-bit far return address saved on the stack.

Unfortunately we can't use this assembly file directly with the tools provided with Visual Studio. The 64-bit version of MASM only creates 64-bit object files, and the linker won't let us mix 32-bit and 64-bit object files. It should be possible to assemble 64-bit code into a 32-bit object using NASM and link with Microsoft's linker, but it's possible to use the code indirectly using only Microsoft's tools.

To do that, assemble the file and copy the machine code manually into a C array that lives in the .text section:

#pragma code_seg(push, ".text")
#pragma code_seg(pop)
char const __declspec(allocate(".text")) _read64ptr[] = {
    0x48, 0x8b, 0x44, 0x24, 0x08,   /* mov rax, [rsp + 8] */
    0x8b, 0x00,                     /* mov eax. [rax] */
    0x8b, 0x50, 0x04,               /* mov edx, [rax + 4] */
    0xcb                            /* retf */
};

To call it you just need to use code like this:

struct {
    void const *offset;
    unsigned short selector;
} const _read64ptr_ind = { _read64ptr, 0x33 };

unsigned long long
read64ptr(unsigned long long address) {
    unsigned long long value;
    _asm {
        push    DWORD PTR [address + 4]
        push    DWORD PTR [address]
        call    FWORD PTR [_read64ptr_ind]
        add     esp, 8
        mov     DWORD PTR [value], eax
        mov     DWORD PTR [value + 4], edx
    }
    return value;
}

The indirection with _read64ptr_ind is necessary because there's no way to write call 33h:_read64ptr in Microsoft inline assembly. Also note that the 64-bit code selector 0x33 is hard coded in this example, hopefully it won't change.

Here's an example that uses the above code to read the address of the 64-bit PEB from the 64-bit TEB (even though both are located in the 32-bit address space):

unsigned long long
readgsqword(unsigned long off) {
    unsigned long long value;
    _asm {
        mov edx, [off]
        mov eax, gs:[edx]
        mov edx, gs:[edx + 4]
        mov DWORD PTR [value], eax
        mov DWORD PTR [value + 4], edx
    }
    return value;
}

int
main() {
    printf("32-bit TEB address %08lx\n",
           __readfsdword(offsetof(NT_TIB, Self)));
    printf("32-bit PEB address %08lx\n", __readfsdword(0x30));
    unsigned long long teb64 = readgsqword(offsetof(NT_TIB64, Self));
    printf("64-bit TEB address %016llx\n", teb64);
    printf("64-bit PEB address %016llx\n", readgsqword(0x60));
    printf("64-bit PEB address %016llx\n", read64ptr(teb64 + 0x60));
}

Running it on my computer generates the following output:

32-bit TEB address 7efdd000
32-bit PEB address 7efde000
64-bit TEB address 000000007efdb000
64-bit PEB address 000000007efdf000
64-bit PEB address 000000007efdf000

As you can see all the structures can be accessed using 32-bit pointers and without any 64-bit code. In particular, the example shows how to obtain a 32-bit pointer to the 64-bit PEB using only 32-bit code.

A final note, there's no guarantee that Windows will handle 64-bit code running in a 32-bit process correctly. If an interrupt happens to occur at any time while executing the 64-bit code the process may end up crashing.