4

目前我正在玩被称为“天堂之门”的 windows/WOW64 技巧,正如你们中的一些人可能知道的那样,即使在 x86 程序中,它也允许我们进入 x64 模式(当我测试它时我很惊讶它有效!)但我知道它不受所有 Windows 版本的支持,所以我的代码(因为有代码)使用 seh,它看起来像这样:

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] 

我知道一个简单的“jmp .exit”可以解决问题,但我对此非常好奇

4

1 回答 1

1

当操作系统收到中断或发生故障时,它期望无论用户代码在做什么,CPU 都已将所需的状态保存在内核堆栈上,以便IRET无形地恢复它正在执行的任何操作。

请注意,内核堆栈上的该状态不涉及“魔法”。“继续执行”仅意味着恢复保存的值rflagscs:ripss:rsp运行cs:rip最终指向的代码。

这意味着,在不专门涉及 SEH 的情况下,仅考虑在远调用期间发生的任何类型的异常,实际上只有两种情况需要考虑:

  1. 异常发生在跳转“之前”:没有推送任何内容,内核堆栈上的状态表明我们应该通过重新启动调用指令来恢复。
  2. 异常发生在跳转“之后”:cs:eip已被推送,rip指向.64bits_code标签处或之后的某个位置,并且保存的状态表示要恢复,我们应该跳转到 64 位代码。

如果 CPU 允许“在中间”中断远调用,那么当操作系统继续执行时,就不可能cs:rip产生一致的结果。例如,如果远调用的返回地址在异常发生之前被压入,但保存的cs:rip指向远调用指令,那么您最终会在堆栈上得到两个返回地址副本,并且一切都变得松散了。

现在,要实际回答您的问题:这取决于操作系统告诉您发生异常的 rIP 的值。如果它指向 64 位代码,则堆栈上必须有 cs:eip,如果它指向 32 位代码,则可以安全地假设它尚未被推送。

于 2012-07-22T18:06:05.700 回答