Step to UEFI (243)Call 的简单研究

前面代码中调用了 AsmCurrentRIP ,这次我们使用 WinDBG查看 X64 Mode 下 Call的具体实现。 为了更好的进行查看,我们需要对代码进行一些修改。在 SecStartupPhase2() 中加入下面的代码(SecCoreStartupWithStack()函数太早,尚且运行在 ROM 中的,另外此时的Debug环境还没有配置好无法halt ):

  //
  // Find PEI Core entry point. It will report SEC and Pei Core debug information if remote debug
  // is enabled.
  //
  BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase;
  FindAndReportEntryPoints (&BootFv, &PeiCoreEntryPoint);
  SecCoreData->BootFirmwareVolumeBase = BootFv;
  SecCoreData->BootFirmwareVolumeSize = (UINTN) BootFv->FvLength;
  DEBUG ((DEBUG_INFO,
    "Check [BootFirmwareVolumeBase]-0x%X [PeiCoreEntryPoint]=0x%X BootFirmwareVolumeBase=0x%X \n",
	(UINT64)BootFv,
    (UINT64)(PeiCoreEntryPoint),
	(UINT64)(*PeiCoreEntryPoint)
    ));
  //LabzDebug_Start
  CpuBreakpoint();
  AsmCurrentRIP();
  //LabzDebug_End	
  //
  // Transfer the control to the PEI core
  //
  (*PeiCoreEntryPoint) (SecCoreData, (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTable);

编译完成后,运行NewDbg.bat 批处理,会自动调用 QEMU 还有 WinDBG 然后会自动 halt住。第一次 Halt并非我们需要的位置,因此,我们用g 让它继续运行。再次 Halt 之后是停在 int3 中,使用u反编译可以看一下。接下来连续运行两次t 命令。再使用 u 查看:

0: kd> t
00000000`fffcedd7 c3              ret
0: kd> t
00000000`fffce7a7 e8c8ddffff      call    00000000`fffcc574
0: kd> u
00000000`fffce7a7 e8c8ddffff      call    00000000`fffcc574
00000000`fffce7ac 488d15098e0100  lea     rdx,[00000000`fffe75bc]
00000000`fffce7b3 488b4c2438      mov     rcx,qword ptr [rsp+38h]
00000000`fffce7b8 ff542440        call    qword ptr [rsp+40h]
00000000`fffce7bc e8d30b0000      call    00000000`fffcf394
00000000`fffce7c1 0fb6c0          movzx   eax,al
00000000`fffce7c4 85c0            test    eax,eax
00000000`fffce7c6 741f            je      00000000`fffce7e7

这里的信息可以和 SecMain.cod 对起来:

; 1016 :   DEBUG ((DEBUG_INFO,
  00098	33 c0		 xor	 eax, eax
  0009a	85 c0		 test	 eax, eax
  0009c	75 b2		 jne	 SHORT $LN4@SecStartup

; 1022 :   //LabzDebug_Start
; 1023 :   CpuBreakpoint();

  0009e	e8 00 00 00 00	 call	 CpuBreakpoint

; 1024 :   AsmCurrentRIP();

  000a3	e8 00 00 00 00	 call	 AsmCurrentRIP

; 1025 :   
; 1026 :   //
; 1027 :   // Transfer the control to the PEI core
; 1028 :   //
; 1029 :   (*PeiCoreEntryPoint) (SecCoreData, (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTable);

  000a8	48 8d 15 00 00
	00 00		 lea	 rdx, OFFSET FLAT:mPrivateDispatchTable
  000af	48 8b 4c 24 38	 mov	 rcx, QWORD PTR SecCoreData$[rsp]
  000b4	ff 54 24 40	 call	 QWORD PTR PeiCoreEntryPoint$[rsp]
$LN10@SecStartup:

下面着重分析 上面调用 AsmCurrentRIP() 这个函数:

00000000`fffce7a7 e8c8ddffff      call    00000000`fffcc574

指令是: e8c8dd ffff  , 从手册可以看到 E8 是 “Call near”的意思:

Call Near

对于 near call 动作如下:

最重要的动作是: push(RIP)和 RIP=tempRIP。特别注意,这里的 RIP 不是当前 call 所在的 RIP(不是 00000000`fffce7a7),而是这条指令的下一条(手册中有描述Near Call. When executing a near call, the processor pushes the value of the EIP register (which contains the offset of the instruction following the CALL instruction) on the stack (for use later as a return-instruction pointer). The processor then branches to the address in the current code segment specified by the target operand. The target operand specifies either an absolute offset in the code segment (an offset from the base of the code segment) or a relative offset (a signed displacement relative to the current value of the instruction pointer in the EIP register; this value points to the instruction following the CALL instruction). The CS register is not changed on near calls.)就是说执行e8 c8dd ffff操作的时候,RIP=00000000 fffce7ac, 于是 Push RIP到堆栈中,然后修改 RIP。 其中的 0xFFFF DDC8(-0x2238)是相对位置,于是跳转到的位置是 00000000 FFFC E7AC+(-0x2238)= 00000000 FFFC C574。也就是右侧 call 00000000`fffcc574的由来。

因此,我们修改代码,直接输出 ESP 指向的8个字节即可实现输出调用位置下一条指令的IP。

接下来,我们再查看call调用前后堆栈的情况。下面是执行 call 之前,使用r rsp 和 dd rsp 查看堆栈寄存器和对应的内存位置:

0: kd> r rsp
rsp=000000000081fb10
0: kd> dd rsp
00000000`0081fb10  00000040 00000000 fffe4424 00000000
00000000`0081fb20  00820000 00000000 008207a0 00000000
00000000`0081fb30  008207a0 00000000 0000001f 00000000
00000000`0081fb40  00820000 00000000 0081fd50 00000000
00000000`0081fb50  008207a0 00000000 00000000 00000000
00000000`0081fb60  0081fb98 00000000 fffd23ab 00000000
00000000`0081fb70  0081fd50 00000000 00000001 00000000
00000000`0081fb80  00000000 00000000 fffdf860 00000000

接下来使用 t 这里就跳入了 AsmCurrentRIP 函数中。再查看 rsp 和对应的内存位置:

0: kd> t
00000000`fffcc574 488d05f9ffffff  lea     rax,[00000000`fffcc574]
0: kd> r rsp
rsp=000000000081fb08
0: kd> dd rsp
00000000`0081fb08  fffce7ac 00000000 00000040 00000000
00000000`0081fb18  fffe4424 00000000 00820000 00000000
00000000`0081fb28  008207a0 00000000 008207a0 00000000
00000000`0081fb38  0000001f 00000000 00820000 00000000
00000000`0081fb48  0081fd50 00000000 008207a0 00000000
00000000`0081fb58  00000000 00000000 0081fb98 00000000
00000000`0081fb68  fffd23ab 00000000 0081fd50 00000000
00000000`0081fb78  00000001 00000000 00000000 00000000
0: kd> r rip
rip=00000000fffcc574
0: kd> u rip
00000000`fffcc574 488d05f9ffffff  lea     rax,[00000000`fffcc574]
00000000`fffcc57b 66ba0204        mov     dx,402h
00000000`fffcc57f ee              out     dx,al
00000000`fffcc580 48c1e808        shr     rax,8
00000000`fffcc584 ee              out     dx,al
00000000`fffcc585 48c1e808        shr     rax,8
00000000`fffcc589 ee              out     dx,al
00000000`fffcc58a 48c1e808        shr     rax,8

可以看到堆栈存放了call 指令下一条的 RIP,同时 RSP 减8。

参考:

1. https://stackoverflow.com/questions/10376787/need-help-understanding-e8-asm-call-instruction-x86

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注