4

Currently i'm playing with the windows/WOW64 trick known as "the heaven's gate", which, as some of you will probably know, allows us to enter x64 mode even though in a x86 program (i was so amazed when i tested it and it worked!) But i know it is not supported on all Windows versions, so my code (because there is a code) uses seh, it looks like this:

start:
  use32
  ;; setup seh...
  call $33:.64bits_code ; specify 0x33 segment, it's that easy
  ;; success in x64 mode, quit seh...
  jmp .exit

.64bits_code:
  use64
  ;; ...
  use32
  retf

.seh_handler:
  use32
  ;; ...
  xor eax,eax ; EXCEPTION_CONTINUE_EXECUTION
  ret

.32bits_code: 
  ; we have been called by a far call (well, indirectly, routed by a seh handler)
  ; HERE IS THE PROBLEM => Should i use a retf since cs and eip are on the stack, 
  ;                        or the exception has been triggered before pushing them???
  ; "retf" or "jmp .exit"?

.exit:
  xor eax,eax
  push eax
  call [ExitProcess] 

I know a simple "jmp .exit" would do the trick, but i'm terribly curious about it

nts94
  • 41
  • 3
  • What is your question? If it's that comment in the code, please make it part of your question proper. – Robert Harvey Jun 11 '12 at 01:11
  • I'm so sorry, this is my first StackOverflow question. Well, yes, my question is: if an exception is triggered (for example, if the code runs in an x86 only system), will cs and eip have been already pushed on the stack? therefore, should i retf, or simply jmp? – nts94 Jun 11 '12 at 01:17

1 Answers1

1

When the OS gets an interrupt or a fault happens, it expects that no matter what the user code was up to, the CPU has saved the required state on the kernel stack so that an IRET will invisibly resume whatever it was doing.

Note that there is no "magic" involved in that state on the kernel stack. "Continuing execution" only means restoring saved values of rflags, cs:rip and ss:rsp and running the code that cs:rip ends up pointing to.

That means that without involving SEH specifically, just thinking about any kind of exception happening during a far call, there are really only two cases to consider:

  1. The exception happens "before" the jump: nothing has been pushed, the state on the kernel stack says we should resume by restarting the call instruction.
  2. The exception happens "after" the jump: cs:eip have been pushed, rip points somewhere at or after the .64bits_code label, and that saved state says that to resume we should jump to the 64-bit code.

If the CPU allowed a far call to be interrupted "in the middle", there would be no possible value of cs:rip that produces a consistent result when the OS continues execution. For example, if the far call's return address was pushed before the exception happened but the saved cs:rip points to the far call instruction, you'll end up with two copies of the return address on the stack and all hell breaks loose.

Now, to actually answer your question: it depends on the value of rIP that the OS tells you the exception happened on. If it points to the 64-bit code you must have cs:eip on the stack, if it points to the 32-bit code you can safely assume it has not been pushed yet.

olsner
  • 981
  • 7
  • 6