Reputation: 381
I was looking how NtDll
works for x86 processes, and i debugged the function NtCreateFile
using IDA PRO. The code for it is following:
mov eax, 55h ; NtCreateFile
mov edx, offset _Wow64SystemServiceCall@0 ;
call edx ; Wow64SystemServiceCall() ;
retn 2Ch
And the Wow64SystemServiceCall()
:
mov edx, large fs:30h
mov edx, [edx+464h]
test edx, 2
jz short loc_7738B5C8
int 2Eh ; DOS 2+ internal - EXECUTE COMMAND
; DS:SI -> counted CR-terminated command string
retn
loc_7738B5C8: ; CODE XREF:
jmp far ptr byte_7738B8FF
I looked up the command code for jmp far ptr byte_7738B8FF
and it was EA CF B5 38 77 33 00
which is a jump to another segment, 0x33 jmp 0x33:0x7738b5cf
. So from what i read in internet, this is the x64 segment base for processes on 64-bit systems, right? Unfortunately i cannot debug further, because, ida doesnt follow the jump. But i made another simple C application compiled for x64, and called CreateFile
, attached x64 IDA PRO Remote debugger, and looked up disassembly, and NtCreateFile
was looking like so:
x64_NtCreateFile proc near
mov r10, rcx
mov eax, 55h
test byte ptr ds:7FFE0308h, 1
jnz short loc_7FFED6695B85
syscall
retn
loc_7FFED6695B85:
int 2Eh ; DOS 2+ internal - EXECUTE COMMAND
; DS:SI -> counted CR-terminated command string
retn
So i have a few question, how does the jump from x86 process attached ntdll far jump jmp 0x33:0x7738b5cf
just lands into x64_NtCreateFile
first instruction? How does the switching from x86 to x64 happens in this case exactly? Basically i can just make x86 application, and switch segment with jump, and just execute the x64 code in there, which i can create by just doing something like db (0x00) ; x64 machine code commands
, is this right?
Upvotes: 1
Views: 4899
Reputation: 1548
If you look at the bytes at address 0x7738b5cf
, you would see something like
41 FF A7 F8 00 00 00
(at least if you're on Windows 8.1 or newer)
which corresponds to a single x86_64 instruction jmp QWORD PTR [r15+0xf8]
.
Right after switching from 32-bit to 64-bit code execution via the far jump, the R15
register will always point to a special jump table within wow64cpu.dll
(the R15
register is setup to point to this table from 64-bit code that executes before the 32-bit entry point of your application).
[r15+0xf8]
just happens to point to the CpupReturnFromSimulatedCode
method within wow64cpu.dll
, which will setup the right context and perform the actual system call (in your case for NtCreateFile
) using the syscall
instruction.
For some information that elaborates upon this, see:
Upvotes: 3
Reputation: 62096
Yes, I can confirm that one can execute 64-bit code in a 32-bit Windows app running on 64-bit windows.
Mixing x86 with x64 code provides an explanation and an example of how to do it.
This is what I tried (adapted for and compiled with my Smaller C compiler):
#define EM(a) asm("db " #a);
#define X64_Start_with_CS(_cs) \
{ \
EM(0x6A) EM(_cs) /* push _cs */ \
EM(0xE8) EM(0) EM(0) EM(0) EM(0) /* call $+5 */ \
EM(0x83) EM(4) EM(0x24) EM(5) /* add dword [esp], 5 */ \
EM(0xCB) /* retf */ \
}
#define X64_End_with_CS(_cs) \
{ \
EM(0xE8) EM(0) EM(0) EM(0) EM(0) /* call $+5 */ \
EM(0xC7) EM(0x44) EM(0x24) EM(4) /* */ \
EM(_cs) EM(0) EM(0) EM(0) /* mov dword [rsp + 4], _cs */ \
EM(0x83) EM(4) EM(0x24) EM(0xD) /* add dword [rsp], 0xD */ \
EM(0xCB) /* retf */ \
}
#define X64_Start() X64_Start_with_CS(0x33)
#define X64_End() X64_End_with_CS(0x23)
#define __break() asm("int3")
int main(void)
{
__break();
X64_Start();
EM(0x48) EM(0x8D) EM(0x05) EM(0xF9) EM(0xFF) EM(0xFF) EM(0xFF) // lea rax, [$] ; rip-relative
X64_End();
__break();
}
I then ran it under a debugger and noticed that eax contained the address of the 64-bit instruction "lea rax, [$]" when the second break point was hit.
Upvotes: 2