WDTF的安装

如果需要自动测试 Modern Standby 那么需要安装 WDTF (Windows Device Testing Framework)【参考1】, 这个事情在之前的 ZVIRTUALBATTERY工具上有提到过【参考1】,当时我将一个 WDTF 安装包集成到了这个工具中,在安装工具的时候会自动安装。

实际上微软的推荐是需要安装和当前版本相匹配的 WDTF。因此这里就介绍一下如何单独安装。

1.检查当前 Windows 版本,命令  WinVer

2.根据这个版本,找对应的 WDK,通常是 ISO格式

3.可以选择直接挂接,比如下面直接把ISO 虚拟成一个光驱

4.使用命令  E:\Installers>msiexec /i “Windows Driver Testing Framework (WDTF) Runtime Libraries-x64_en-us.msi” 即可安装

5.保险起见,可以到已经安装程序中查看是否有如下程序

参考:

1. https://docs.microsoft.com/en-us/windows-hardware/drivers/wdtf/

2. http://www.lab-z.com/zvb/

Step to UEFI (212)WinPE 格式:RDATA 节的作用

前面的文章中提到了 .rdata 是用于保存常量数据的节,这里对这个位置进行研究。

首先从  \Build\AppPkg\DEBUG_VS2015x86\X64\AppPkg\Applications\Hello\Hello\OUTPUT\Hello.map 看到 Section 分布:

Start         Length     Name                   Class
 0001:00000000 000012f7H .text$mn                CODE
 0002:00000000 000006f4H .rdata                  DATA
 0002:000006f4 00000110H .rdata$zzzdbg           DATA
 0003:00000000 00000020H .data                   DATA
 0003:00000020 00000020H .bss                    DATA
 0004:00000000 000000a8H .pdata                  DATA
 0005:00000000 00000084H .xdata                  DATA

对应的,在下面有这个段对应偏移的内容:

比如,其中有下面这样一条:

0002:00000690       mHexStr                    0000000000001c50     BasePrintLib:PrintLibInternal.ob

我们查看 \MdePkg\Library\BasePrintLib\PrintLibInternal.c有如下定义:

GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 mHexStr[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

对应代码:

/**
  Internal function that convert a number to a string in Buffer.

  Print worker function that converts a decimal or hexadecimal number to an ASCII string in Buffer.

  @param  Buffer    Location to place the ASCII string of Value.
  @param  Value     The value to convert to a Decimal or Hexadecimal string in Buffer.
  @param  Radix     Radix of the value

  @return A pointer to the end of buffer filled with ASCII string.

**/
CHAR8 *
BasePrintLibValueToString (
  IN OUT CHAR8  *Buffer,
  IN INT64      Value,
  IN UINTN      Radix
  )
{
  UINT32  Remainder;

  //
  // Loop to convert one digit at a time in reverse order
  //
  *Buffer = 0;
  do {
    Value = (INT64)DivU64x32Remainder ((UINT64)Value, (UINT32)Radix, &Remainder);
    *(++Buffer) = mHexStr[Remainder];
  } while (Value != 0);

  //
  // Return pointer of the end of filled buffer.
  //
  return Buffer;
}

结合代码可以出这个是用来将十进制数值转化为十六进制字符串的,对于一般的打印处理是会用到的。

使用 CFF 打开 EFI 文件,查看 .rdata Section(红色框中),可以在其中看到定义的字符串:

如果把上面的 0x34修改为0x46,再次运行 Hello.efi 就会用不同的值显示出来。修改之前运行结果如下:

修改之后运行的结果:

因此,可以确定在 .rdata 中存放了mHexStr[]用于十进制对十六进制显示的转换。这就是 .rdata存放的常量的作用。

Step to UEFI (211)调用 ConOut 的条件(下)

前面的研究中提到了调用 ConOut 必须满足的2个条件,这次就研究一下其中的另外条件:

“代码起始处的 push rdi , 不一定是RDI,任何8Bytes的寄存器都可以,但是如果没有这语句在调用ConOut的时候这个函数内部会发生错误”

针对这个问题,使用单步跟踪的方式,尝试定位出现问题的位置:

1.CoreStartImage() 函数中的下列代码, 准备跳入 Hello.EFI 代码执行

Image->Status = Image->EntryPoint (ImageHandle, Image->Info.SystemTable);

0000000075829870 48 8B 53 38          mov         rdx,qword ptr [rbx+38h]  
0000000075829874 48 8B CE             mov         rcx,rsi  
0000000075829877 40 88 7B 18          mov         byte ptr [rbx+18h],dil  
000000007582987B FF 53 20             call        qword ptr [rbx+20h]  
000000007582987E 48 89 83 A8 00 00 00 mov         qword ptr [rbx+0A8h],rax  

2.下面是 Hello.EFI 的代码,这次使用的代码我们已经加入了对于 rsp 的调整

Hello.asm

    ; reserve space for 2 arguments
    sub rsp, 2 * 8

    ; rdx points to the EFI_SYSTEM_TABLE structure
    ; which is the 2nd argument passed to us by the UEFI firmware
    ; adding 64 causes rcx to point to EFI_SYSTEM_TABLE.ConOut
    mov rcx, [rdx + 64]

    ; load the address of our string into rdx
    lea rdx, [rel strHello]

    ; EFI_SYSTEM_TABLE.ConOut points to EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
    ; call OutputString on the value in rdx
call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString]

3. 上面的 call 会转入下面的函数中继续

ConsoleLoggerOutputString()

  } else {
    return (ConsoleLoggerOutputStringSplit(WString, ConsoleInfo));
000000007404CE18 E8 A3 FB FF FF       call        ConsoleLoggerOutputStringSplit (07404C9C0h)  
  }

4.跳转到下面的函数继续

ConsoleLoggerOutputStringSplit()

  //
  // Forward the request to the original ConOut
  //
  Status = ConsoleInfo->OldConOut->OutputString (ConsoleInfo->OldConOut, (CHAR16*)String);
000000007404C9CA 48 8B 42 58          mov         rax,qword ptr [rdx+58h]  
000000007404C9CE 48 8B DA             mov         rbx,rdx  
000000007404C9D1 48 8B F9             mov         rdi,rcx  
000000007404C9D4 48 8B D1             mov         rdx,rcx  
000000007404C9D7 48 8B C8             mov         rcx,rax  
000000007404C9DA FF 50 08             call        qword ptr [rax+8]  

5.上述代码会继续调用下面这个函数

ConSplitterTextOutOutputString()

Status = Private->TextOutList[Index].TextOut->OutputString (
0000000075514940 48 8B 83 E0 00 00 00 mov         rax,qword ptr [rbx+0E0h]  
0000000075514947 48 8B D7             mov         rdx,rdi  
000000007551494A 4E 8B 44 30 10       mov         r8,qword ptr [rax+r14+10h]  
000000007551494F 49 8B C8             mov         rcx,r8  
0000000075514952 41 FF 50 08          call        qword ptr [r8+8]  
                                                    Private->TextOutList[Index].TextOut,
                                                    WString
                                                    );

6.上述代码会继续调用下面这个函数

GraphicsConsoleConOutOutputString ()

  //
  // Forward the request to the original ConOut
  //
  Status = ConsoleInfo->OldConOut->OutputString (ConsoleInfo->OldConOut, (CHAR16*)String);
0000000073F4C9CA 48 8B 42 58          mov         rax,qword ptr [rdx+58h]  
0000000073F4C9CE 48 8B DA             mov         rbx,rdx  
0000000073F4C9D1 48 8B F9             mov         rdi,rcx  
0000000073F4C9D4 48 8B D1             mov         rdx,rcx  
0000000073F4C9D7 48 8B C8             mov         rcx,rax  
0000000073F4C9DA FF 50 08             call        qword ptr [rax+8]  

7.继续执行

ConSplitterTextOutOutputString

    Status = Private->TextOutList[Index].TextOut->OutputString (
0000000075394940 48 8B 83 E0 00 00 00 mov         rax,qword ptr [rbx+0E0h]  
0000000075394947 48 8B D7             mov         rdx,rdi  
000000007539494A 4E 8B 44 30 10       mov         r8,qword ptr [rax+r14+10h]  
000000007539494F 49 8B C8             mov         rcx,r8  
0000000075394952 41 FF 50 08          call        qword ptr [r8+8]  
                                                    Private->TextOutList[Index].TextOut,
                                                    WString
                                                    );

8. 继续执行

WinNtGopBlt ( )

      //LABZ Mart
      CopyMem (Blt, VScreen, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * Width);
000000007542217A 48 85 ED             test        rbp,rbp  
000000007542217D 74 1E                je          WinNtGopBlt+175h (07542219Dh)  
      Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) ((UINT8 *) BltBuffer + (DstY * Delta) + DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
000000007542217F 48 8B 96 28 02 00 00 mov         rdx,qword ptr [rsi+228h]  
0000000075422186 4A 8D 0C BB          lea         rcx,[rbx+r15*4]  
000000007542218A 48 03 8C 24 98 00 00 00 add         rcx,qword ptr [BltBuffer]  
      VScreen = &Private->VirtualScreen[(VerticalResolution - SrcY - 1) * HorizontalResolution + SourceX];
0000000075422192 49 03 D6             add         rdx,r14  
0000000075422195 4C 8B C5             mov         r8,rbp  
      VScreen = &Private->VirtualScreen[(VerticalResolution - SrcY - 1) * HorizontalResolution + SourceX];
0000000075422198 E8 5F 22 00 00       call        CopyMem (0754243FCh)  

9. 出乎意料的是问题发生在 CopyMem 的函数中

CopyMem()

  return InternalMemCopyMem (DestinationBuffer, SourceBuffer, Length);
0000000075424471 4D 8B C6             mov         r8,r14  
0000000075424474 48 8B D6             mov         rdx,rsi  
0000000075424477 48 8B CB             mov         rcx,rbx  
000000007542447A E8 81 CB FF FF       call        InternalMemCopyMem (075421000h)  

10.这个函数体结构如下,出现问题的是 movdqa xmmword ptr [rsp+18h],xmm0

InternalMemCopyMem()

InternalMemCopyMem:
00000000749B1000 56                   push        rsi  
00000000749B1001 57                   push        rdi  
00000000749B1002 48 89 D6             mov         rsi,rdx  
00000000749B1005 48 89 CF             mov         rdi,rcx  
00000000749B1008 4E 8D 4C 06 FF       lea         r9,[rsi+r8-1]  
00000000749B100D 48 39 FE             cmp         rsi,rdi  
00000000749B1010 48 89 F8             mov         rax,rdi  
00000000749B1013 73 05                jae         InternalMemCopyMem+1Ah (0749B101Ah)  
00000000749B1015 49 39 F9             cmp         r9,rdi  
00000000749B1018 73 48                jae         InternalMemCopyMem+62h (0749B1062h)  
InternalMemCopyMem.0:
00000000749B101A 48 31 C9             xor         rcx,rcx  
00000000749B101D 48 29 F9             sub         rcx,rdi  
00000000749B1020 48 83 E1 0F          and         rcx,0Fh  
00000000749B1024 74 0C                je          InternalMemCopyMem+32h (0749B1032h)  
00000000749B1026 4C 39 C1             cmp         rcx,r8  
00000000749B1029 49 0F 47 C8          cmova       rcx,r8  
00000000749B102D 49 29 C8             sub         r8,rcx  
00000000749B1030 F3 A4                rep movs    byte ptr [rdi],byte ptr [rsi]  
InternalMemCopyMem.1:
00000000749B1032 4C 89 C1             mov         rcx,r8  
00000000749B1035 49 83 E0 0F          and         r8,0Fh  
00000000749B1039 48 C1 E9 04          shr         rcx,4  
00000000749B103D 74 2C                je          InternalMemCopyMem+6Bh (0749B106Bh)  
00000000749B103F 66 0F 7F 44 24 18    movdqa      xmmword ptr [rsp+18h],xmm0  
InternalMemCopyMem.2:
00000000749B1045 F3 0F 6F 06          movdqu      xmm0,xmmword ptr [rsi]  
00000000749B1049 66 0F E7 07          movntdq     xmmword ptr [rdi],xmm0  
00000000749B104D 48 83 C6 10          add         rsi,10h  
00000000749B1051 48 83 C7 10          add         rdi,10h  
00000000749B1055 E2 EE                loop        InternalMemCopyMem+45h (0749B1045h)  
00000000749B1057 0F AE F0             mfence  
00000000749B105A 66 0F 6F 44 24 18    movdqa      xmm0,xmmword ptr [rsp+18h]  
00000000749B1060 EB 09                jmp         InternalMemCopyMem+6Bh (0749B106Bh)  

经过上面的分析,出现问题是是 movdqa      xmmword ptr [rsp+18h],xmm0  或者 movdqu      xmm0,xmmword ptr [rsi]   语句,对于这个指令,资料上【参考1】有如下解释:

MOVDQA – 移动对齐的双四字

将双四字从源操作数(第二个操作数)移到目标操作数(第一个操作数)。此指令可以用于在 XMM 寄存器与 128 位内存位置之间移入/移出双四字,或是在两个 XMM 寄存器之间移动。源操作数或目标操作数是内存操作数时,操作数必须对齐 16 字节边界,否则将生成一般保护性异常 (#GP)。

实际跟踪发现出现问题的时候要么是 rsp 没有16位对齐,要么是 rsi 没有对齐。因此,再回头来看这个问题应该是堆栈没有对齐导致的问题。

“3.栈按照16字节对齐

现在我们应该明白了.在调用一个函数的时候. 使用 *sub rsp,xxx**进行抬栈,函数内部则进行参数赋值.其实也是相当于push了参数.只不过它不像x86一样.在里面进行平栈了.而是外面进行平栈了.那么有个疑问.比如说我们就4个参数. 通过上面来说.我们应该申请 sub rsp,0x20个字节才对.在CALL的时候x86 x64都是一样的会将返回地址入栈. 那为什么要rsp,0x28.这样的话会多申请一个参数的值哪.

原因是这样的.栈要按照16字节对齐进行申请.

那么还有人会说.按照16字节对齐,那么我们的参数已经是16字节对齐了.比如为我们4个寄存器申请预留空间. rsp,0x20. (4 * 8 = 32 = 16j进制的 0x20) 那为什么还是会申请 rsp,0x28个字节,并且不对齐.其实是这样的.当我们在 Call函数的时候.返回地址会入栈.如果按照我们之前申请的rsp,0x20个字节的话.那么当

返回地址入栈之后,现在总共抬栈大小是 0x28个字节.并不是16进制对齐. 但是当我们一开始就申请0x28个字节.当返回地址入栈.那么就是0x28+8 = 0x30个字节. 0x30个字节不是正好跟16字节对齐吗.所以我们的疑问也就没有了.

所以申请了0x28个字节,其实多出了的8字节是要跟返回地址一样.进行栈对齐使用.

那么申请的这个8字节空间,是没有用的.只是为了对齐使用.“【参考2】

为了验证上面的说法对此,跟踪前面提到的每一次调用进行检查:

函数 对齐
Hello.asm RSP = 00000184E41CF5C8
ConsoleLoggerOutputString() RSP = 00000184E41CF578
ConsoleLoggerOutputStringSplit() RSP = 00000184E41CF548
ConSplitterTextOutOutputString() RSP = 00000184E41CF4F8
GraphicsConsoleConOutOutputString () RSP = 00000184E41CF438
FlushCursor() RSP = 00000184E41CF158
GraphicsConsoleConOutOutputString () RSP = 00000184E41CF0C8
CopyMem() RSP = 00000184E41CF098
InternalMemCopyMem() RSP = 00000184E41CF080RDX = 00000184E84EE86C

上面提到堆栈并不是调用到每个函数之后的,而是经过每个函数调整的堆栈,因为调用参数不同,因此弹出的参数数量会有差别。例如下面这个函数,进入之后会通过三条语句对堆栈进行调整,我们检查的位置就在 sub rsp,20h之后。

EFI_STATUS
ConsoleLoggerOutputStringSplit(
  IN CONST CHAR16   *String,
  IN CONSOLE_LOGGER_PRIVATE_DATA *ConsoleInfo
  )
{
0000000073F2C9C0 48 89 5C 24 08       mov         qword ptr [rsp+8],rbx  
0000000073F2C9C5 57                   push        rdi  
0000000073F2C9C6 48 83 EC 20          sub         rsp,20h  
  EFI_STATUS    Status;

  //
  // Forward the request to the original ConOut
  //
  Status = ConsoleInfo->OldConOut->OutputString (ConsoleInfo->OldConOut, (CHAR16*)String);

所以可以使用之前提到的方法多PUSH 一个 RXX寄存器,或者直接 SUB rsp,x8h 同样可以解决。

参考:

  1. https://blog.csdn.net/u011019337/article/details/9260257
  2. https://www.cnblogs.com/iBinary/p/10959444.html

Step to UEFI (210)调用 ConOut 的条件(上)

前面的研究中提到了调用 ConOut 必须满足的2个条件,这次就研究一下其中的一个条件:

“实践发现在调用 SystemTable->ConOut 的时候会损坏堆栈,比如下面的调用方式(按道理2个参数的情况下是通过寄存器来进行参数传递的)

push rdx 
Call ConOut
pop rdx 

运行之后会发现 rdx 值发生了变化”

首先需要明确的是:堆栈生长方向是从高向低。比如:PUSH RAX执行之后,会将RSP=RSP-8, RAX值存放在 RSP 给出的位置,然后;POP 动作与之相反。

开始跟踪代码,首先在调用处停下来:

执行Call 命令之后,RSP=RSP-8, 然后将 call 后面的下一条指令地址存放在 [RSP ] 之中。此时堆栈如下(简单起见,认为下面是高地址,上面是低地址。当前的IP和堆栈IP 都记作2Bytes方便查看):

跳转到了我们的 ASM 代码中

继续运行 call qword ptr [rcx+8] 之后堆栈情况:

特别注意,这里有一个 mov qword ptr [rsp+8],rbx, 运行之后会将前面一个的返回地址覆盖掉(就是 call qword ptr [rbx+20h] 指令(RIP=989B)的下一条地址)。因此,无论后面如何,这里都不可能正常返回。再进一步,观察到每个函数进入之后都有 sub esp,xxx 这样的操作,咨询天杀他回复“64位的函数调用中,参数的传递,也是要通过栈传递的,call子函数前将数据放到栈中再调用。小于四个参数的情况,一般使用RCX、RDX、R8、R9传递,但是也需要在栈中保留相应的空间。”换句话说,小于4个参数的情况会直接使用寄存器传递参数,但是同样要在堆栈上预留出这几个参数的空间。经过研究在【参考1】有如下描述:

1.在调用函数之前,会申请参数预留空间.(rcx,rdx,r8,r9)

2.函数内部,会将寄存器传参的值(rcx,rdx,r8,r9)保存到我们申请的预留空间中.

上面这两步其实就相当于x86下的 push r9 push r8 push rdx,push rcx

3.调用约定是 __fastcall.传参有rcx rdx,平栈是按照c调用约定平栈. 也就是调用者平栈.

因此,我们之前编写调用  ConOut 的方法是错误的,必须按照规则在堆栈上预留2个参数的空间。 从前面可以看到,堆栈的生长方向是由高到低,因此用  sub rsp,16 即可预留2个8Bytes。

上面就是发生堆栈损坏的原因,本质是我们违反了编译器的约定导致的。

参考:

1. https://www.cnblogs.com/iBinary/p/10959444.html  x64汇编第三讲,64位调用约定与函数传参.

WinDBG 查看 ACPI Table 的操作

最近根据【参考1】研究了一下通过 WinDBG 查看 ACPI Table 的方法,需要特别注意的是,必须按照顺序执行才能取得需要查看的信息。

1.运行!rsdt命令,感觉是让 WinDBG 进行查找的动作

||0:6: kd> !rsdt
Sorry: Unable to get ACPI!AcpiInformation.
Searching for RSDP.*************************************************************************
***                                                                   ***
***                                                                   ***
***    Either you specified an unqualified symbol, or your debugger   ***
***    doesn't have full symbol information.  Unqualified symbol      ***
***    resolution is turned off by default. Please either specify a   ***
***    fully qualified symbol module!symbolname, or enable resolution ***
***    of unqualified symbols by typing ".symopt- 100". Note that     ***
***    enabling unqualified symbol resolution with network symbol     ***
***    server shares in the symbol path may cause the debugger to     ***
***    appear to hang for long periods of time when an incorrect      ***
***    symbol name is typed or the network symbol server is down.     ***
***                                                                   ***
***    For some commands to work properly, your symbol path           ***
***    must point to .pdb files that have full type information.      ***
***                                                                   ***
***    Certain .pdb files (such as the public OS symbols) do not      ***
***    contain the required information.  Contact the group that      ***
***    provided you with these symbols if you need this command to    ***
***    work.                                                          ***
***                                                                   ***
***    Type referenced: hal!_RSDT_32                                  ***
***                                                                   ***
*************************************************************************
.........................................0x0effe0: Read 0x000020 of 0x000024 bytes
Could not locate the RSDT pointer

2. !acpicache 命令,显示当前系统 Cache 的ACPI Table

||0:6: kd> !acpicache
Dumping cached ACPI tables...
  XSDT @(fffff7e200004018) Rev: 0x1 Len: 0x000104 TableID: CFL-ULT
  DBGP @(fffff7e200005018) Rev: 0x1 Len: 0x000034 TableID: CFL-ULT
  MCFG @(fffff7e200006018) Rev: 0x1 Len: 0x00003c TableID: CFL-ULT
  FACP @(fffff7e200050018) Rev: 0x6 Len: 0x000114 TableID: CFL-ULT
  APIC @(fffff7e200051018) Rev: 0x3 Len: 0x00012c TableID: CFL-ULT
  BOOT @(fffff7e200054018) Rev: 0x1 Len: 0x000028 TableID: TIANO   
  DMAR @(fffff7e200055018) Rev: 0x1 Len: 0x0000a8 TableID: CFL     
  HPET @(fffff7e20007e018) Rev: 0x1 Len: 0x000038 TableID: CFL-ULT
  FPDT @(ffffe3020d0fe9e8) Rev: 0x1 Len: 0x000044 TableID: CFL-ULT
  DSDT @(ffffe3020d400018) Rev: 0x2 Len: 0x045806 TableID: CFL-ULT
  SSDT @(ffffe3020d0a7018) Rev: 0x2 Len: 0x001b1c TableID: CpuSsdt
  SSDT @(ffffe3020f31f018) Rev: 0x2 Len: 0x0031c6 TableID: SaSsdt 
  SSDT @(ffffe3020f322208) Rev: 0x2 Len: 0x00045a TableID: Tpm2Tabl
  SSDT @(ffffe3020f322698) Rev: 0x2 Len: 0x000046 TableID: MeSsdt 
  TPM2 @(ffffe3020f322708) Rev: 0x3 Len: 0x000034 TableID: CFL     
  UEFI @(ffffe3020f322768) Rev: 0x1 Len: 0x000042 TableID: CFL-ULT
  SSDM @(ffffe3020f3227d8) Rev: 0x1 Len: 0x000055 TableID: 
  SSDT @(ffffe3020f322858) Rev: 0x2 Len: 0x000538 TableID: PerfTune
  ECDT @(ffffe3020f322db8) Rev: 0x1 Len: 0x000069 TableID: CFL-ULT
  SSDT @(ffffe3020d05e018) Rev: 0x2 Len: 0x002f5c TableID: CnlU_Rvp
  SSDT @(ffffe3020e7fa978) Rev: 0x2 Len: 0x000fae TableID: Ther_Rvp
  SSDT @(ffffe3020d05b018) Rev: 0x2 Len: 0x0029c5 TableID: xh_whld4
  SSDT @(ffffe3020d0a5018) Rev: 0x2 Len: 0x001b67 TableID: UsbCTabl
  LPIT @(ffffe3020d0a6ba8) Rev: 0x1 Len: 0x00005c TableID: CFL-ULT
  WSMT @(ffffe3020d0a6c38) Rev: 0x1 Len: 0x000028 TableID: CFL-ULT
  SSDT @(ffffe3020d058018) Rev: 0x2 Len: 0x0027de TableID: PtidDevc
  SSDT @(ffffe3020d0a3018) Rev: 0x2 Len: 0x00149f TableID: TbtTypeC
  DBG2 @(ffffe3020d0a4548) Rev: 000 Len: 0x000054 TableID: CFL-ULT
  NHLT @(ffffe3020d0a1018) Rev: 000 Len: 0x001783 TableID: CFL     
  BGRT @(ffffe3020d0a2908) Rev: 0x1 Len: 0x000038 TableID: CFL-ULT

3. !fadt 命令,显示 FADT 的信息

||0:6: kd> !fadt 
FADT -- fffff8036da7aee0
FADT revision is 6, which is not understood by this debugger
HEADER - fffff8036da7aee0
  Signature:               FACP
  Length:                  0x00000114
  Revision:                0x06
  Checksum:                0xa1
  OEMID:                   INTEL 
  OEMTableID:              CFL-ULT
  OEMRevision:             0x20170001
  CreatorID:               INTL
  CreatorRev:              0x20160422
FADT - BODY - fffff8036da7af04
  FACS:                    0x8ca12000
  DSDT:                    0x8cbb6000
  Int Model:               Dual PIC
  SCI Vector:              0x009
  SMI Port:                0x000000b2
  ACPI On Value:           0x0f0
  ACPI Off Value:          0x0f1
  SMI CMD For S4 State:    0x0f2
  PM1A Event Block:        0x00001800
  PM1B Event Block:        0x00000000
  PM1 Event Length:        0x004
  PM1A Control Block:      0x00001804
  PM1B Control Block:      0x00000000
  PM1 Control Length:      0x002
  PM2 Control Block:       0x00001850
  PM2 Control Length:      0x001
  PM Timer Block:          0x00001808
  PM Timer Length:         0x004
  GP0 Block:               0x00001860
  GP0 Length:              0x020
  GP1 Block:               0x00000000
  GP1 Length:              0x00000000
  GP1 Base:                0x00000010
  C2 Latency:              0x00065
  C3 Latency:              0x003e9
  Memory Flush Size:       0x00000
  Memory Flush Stride:     0x00000
  Duty Cycle Index:        0x001
  Duty Cycle Index Width:  0x003
  Day Alarm Index:         0x00d
  Month Alarm Index:       0x000
  Century byte (CMOS):     0x032
  Boot Architecture:       0x0001
    The machine does not contain a legacy i8042
  Flags:                   0x0020c4b5
    WRITEBACKINVALIDATE_WORKS .................. SET
    WRITEBACKINVALIDATE_DOESNT_INVALIDATE ...... CLEAR
    SYSTEM_SUPPORTS_C1 ......................... SET
    P_LVL2_UP_ONLY ............................. CLEAR
    PWR_BUTTON_GENERIC ......................... SET
    SLEEP_BUTTON_GENERIC ....................... SET
    RTC_WAKE_GENERIC ........................... CLEAR
    RTC_WAKE_FROM_S4 ........................... SET
    TMR_VAL_EXT ................................ CLEAR
    DCK_CAP .................................... CLEAR
    RESET_CAP .................................. SET
      RESET_VALUE: 6
      RESET_REG: System I/O - 0000000000000cf9
    SEALED_CASE_CAP ............................ CLEAR
    HEADLESS_CAP ............................... CLEAR
    CPU_SW_SLP_CAP ............................. CLEAR
    PCI_EXP_WAK ................................ SET
    USE_PLATFORM_CLOCK ......................... SET
    RTC_WAKE_VALID_FROM_S4 ..................... CLEAR
    REMOTE_POWER_ON_CAPABLE .................... CLEAR
    FADT_FORCE_CLUSTERED_APIC_MODE ............. CLEAR
    FADT_FORCE_APIC_PHYSICAL_DESTINATION_MODE .. CLEAR
    ACPI_HARDWARE_NOT_PRESENT .................. CLEAR
    AOAC_CAPABLE_PLATFORM ...................... SET

上述实验平台为 WHL HDK,有兴趣的朋友可以试试。

参考:

1. https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/other-acpi-debugging-extensions 

WinDBG访问硬件

前面对于 RU.EFI 的研究告诉我们对于 X86 来说,访问硬件信息需要的基本操作有:

1. PCI 信息的访问

2. 访问IO Port 直接访问

3. IO Port Index/Data 方式的访问

4. Memory 的访问

5. Memory Index/Data 方式的访问

6. MSR 的访问

掌握了上述的访问方法就可以访问到 X86 上的全部空间和寄存器。下面逐项介绍在 WinDBG 中的访问方法。因为大多数情况下,WinDBG 是用于调试 Windows 软件本身而不是硬件,因此很多操作都是来自个人总结如果有错误或者遗漏,恳请及时指出。

1.PCI的访问

a. !pcitree 命令可以用来查看当前系统中的PCI总线和设备信息【参考1】

实例如下

0: kd> !pcitree
SYMSRV:  BYINDEX: 0x5
         C:\ProgramData\Dbg\sym
         pci.pdb
         96732E11A7284081C982C9A015D949A81
SYMSRV:  UNC: C:\ProgramData\Dbg\sym\pci.pdb\96732E11A7284081C982C9A015D949A81\pci.pdb - path not found
SYMSRV:  UNC: C:\ProgramData\Dbg\sym\pci.pdb\96732E11A7284081C982C9A015D949A81\pci.pd_ - path not found
SYMSRV:  UNC: C:\ProgramData\Dbg\sym\pci.pdb\96732E11A7284081C982C9A015D949A81\file.ptr - path not found
SYMSRV:  RESULT: 0x80070003
SYMSRV:  BYINDEX: 0x6
         C:\ProgramData\Dbg\sym*https://msdl.microsoft.com/download/symbols
         pci.pdb
         96732E11A7284081C982C9A015D949A81
SYMSRV:  UNC: C:\ProgramData\Dbg\sym\pci.pdb\96732E11A7284081C982C9A015D949A81\pci.pdb - path not found
SYMSRV:  UNC: C:\ProgramData\Dbg\sym\pci.pdb\96732E11A7284081C982C9A015D949A81\pci.pd_ - path not found
SYMSRV:  UNC: C:\ProgramData\Dbg\sym\pci.pdb\96732E11A7284081C982C9A015D949A81\file.ptr - path not found
SYMSRV:  HTTPGET: /download/symbols/pci.pdb/96732E11A7284081C982C9A015D949A81/pci.pdb
SYMSRV:  HttpQueryInfo: 801900c8 - HTTP_STATUS_OK
SYMSRV:  pci.pdb from https://msdl.microsoft.com/download/symbols:copied      
SYMSRV:  PATH: C:\ProgramData\Dbg\sym\pci.pdb\96732E11A7284081C982C9A015D949A81\pci.pdb
SYMSRV:  RESULT: 0x00000000
DBGHELP: C:\ProgramData\Dbg\sym\pci.pdb\96732E11A7284081C982C9A015D949A81\pci.pdb cached to C:\ProgramData\Dbg\sym\pci.pdb\96732E11A7284081C982C9A015D949A81\pci.pdb
DBGHELP: pci - public symbols  
        C:\ProgramData\Dbg\sym\pci.pdb\96732E11A7284081C982C9A015D949A81\pci.pdb
Bus 0x0 (FDO Ext ffffb106414d5a20)
  (d=0,  f=0) 80863e34 devext 0xffffb1063bfef1b0 devstack 0xffffb1063bfef060 0600 Bridge/HOST to PCI
  (d=2,  f=0) 80863ea0 devext 0xffffb1063bfee1b0 devstack 0xffffb1063bfee060 0300 Display Controller/VGA
  (d=8,  f=0) 80861911 devext 0xffffb1063bfed1b0 devstack 0xffffb1063bfed060 0880 Base System Device/'Other' base system device
  (d=12, f=0) 80869df9 devext 0xffffb1063bfec1b0 devstack 0xffffb1063bfec060 1180 Unknown Base Class/Unknown Sub Class
  (d=13, f=0) 80869dfc devext 0xffffb1063bfeb1b0 devstack 0xffffb1063bfeb060 0700 Simple Serial Communications Controller/Serial Port
  (d=14, f=0) 80869ded devext 0xffffb1063bfea1b0 devstack 0xffffb1063bfea060 0c03 Serial Bus Controller/USB
  (d=14, f=2) 80869def devext 0xffffb1063bfe91b0 devstack 0xffffb1063bfe9060 0500 Memory Controller/RAM
  (d=15, f=0) 80869de8 devext 0xffffb1063b4751b0 devstack 0xffffb1063b475060 0c80 Serial Bus Controller/Unknown Sub Class
  (d=16, f=0) 80869de0 devext 0xffffb1063b4671b0 devstack 0xffffb1063b467060 0780 Simple Serial Communications Controller/'Other'
  (d=1d, f=0) 80869db0 devext 0xffffb1063b4691b0 devstack 0xffffb1063b469060 0604 Bridge/PCI to PCI
  Bus 0x1 (FDO Ext ffffb106414e1a20)
    (d=0,  f=0) 144da802 devext 0xffffb106415d81b0 devstack 0xffffb106415d8060 0108 Mass Storage Controller/Unknown Sub Class
  (d=1f, f=0) 80869d84 devext 0xffffb1063b47b740 devstack 0xffffb1063b47b5f0 0601 Bridge/PCI to ISA
  (d=1f, f=3) 80869dc8 devext 0xffffb106414d01b0 devstack 0xffffb106414d0060 0401 Multimedia Device/Audio
  (d=1f, f=4) 80869da3 devext 0xffffb106415c21b0 devstack 0xffffb106415c2060 0c05 Serial Bus Controller/Unknown Sub Class
  (d=1f, f=5) 80869da4 devext 0xffffb106415c91b0 devstack 0xffffb106415c9060 0c80 Serial Bus Controller/Unknown Sub Class
Total PCI Root busses processed = 1
Total PCI Segments processed = 1
如果运行时出现类似下面的提示,需要使用.reload pci.sys 加载一下对应的 symbol
0: kd> !pcitree
Error retrieving address of PciFdoExtensionListHead

b. !pci 命令,参数比较多,这里直接给出常用的命令【参考2】

!pci 2 ff  //列出当前系统中全部 PCI 设备

0: kd> !pci 2 ff  
PCI Segment 0 Bus 0
00:0  8086:3e34.0b  Cmd[0006:.mb...]  Sts[0090:c....]  Intel Host Bridge  SubID:8086:7270
02:0  8086:3ea0.00  Cmd[0400:......]  Sts[0010:c....]  Intel VGA Compatible Controller  SubID:8086:2212
08:0  8086:1911.00  Cmd[0000:......]  Sts[0010:c....]  Intel Other System Peripheral  SubID:8086:7270
12:0  8086:9df9.30  Cmd[0002:.m....]  Sts[0010:c....]  Intel Other Signal Processing Controller  SubID:8086:7270
13:0  8086:9dfc.30  Cmd[0000:......]  Sts[0010:c....]  Intel Serial Controller  SubID:8086:7270
14:0  8086:9ded.30  Cmd[0406:.mb...]  Sts[0290:c....]  Intel Class:c:3:30  SubID:8086:7270
14:2  8086:9def.30  Cmd[0006:.mb...]  Sts[0010:c....]  Intel RAM Controller
15:0  8086:9de8.30  Cmd[0400:......]  Sts[0010:c....]  Intel Class:c:80:0  SubID:8086:7270
16:0  8086:9de0.30  Cmd[0406:.mb...]  Sts[0010:c....]  Intel Other Communications Controller  SubID:8086:7270
1d:0  8086:9db0.f0  Cmd[0407:imb...]  Sts[0010:c....]  Intel PCI-PCI Bridge 0->0x1-0x1
1f:0  8086:9d84.30  Cmd[0407:imb...]  Sts[0000:.....]  Intel ISA Bridge  SubID:8086:7270
1f:3  8086:9dc8.30  Cmd[0406:.mb...]  Sts[0010:c....]  Intel Audio Device  SubID:8086:7270
1f:4  8086:9da3.30  Cmd[0000:......]  Sts[0280:.....]  Intel SMBus Controller  SubID:8086:7270
1f:5  8086:9da4.30  Cmd[0402:.m....]  Sts[0000:.....]  Intel Class:c:80:0  SubID:8086:7270
PCI Segment 0 Bus 0x1
00:0  144d:a802.01  Cmd[0406:.mb...]  Sts[0010:c....]  Class:1:8:2  SubID:144d:a801

!pci f 0 2 0 0 0x200 //查看Bus 0, Device 2,function 0 上从 0 到 0x200的寄存器

0: kd> !pci f 0 2 0 0 0x200
PCI Segment 0 Bus 0
02:0  8086:3ea0.00  Cmd[0400:......]  Sts[0010:c....]  Intel VGA Compatible Controller  SubID:8086:2212
      cf8:80001000  IntPin:1  IntLine:0  Rom:0  cis:0  cap:40
      MEM[0]:cf000004  MPF[2]:d000000c  IO[4]:ffc1       
      00000000:  3ea08086 00100400 03000000 00000000
      00000010:  cf000004 00000000 d000000c 00000000
      00000020:  0000ffc1 00000000 00000000 22128086
      00000030:  00000000 00000040 00000000 00000100
      00000040:  010c7009 7a6160b1 9615808c 00000000
      00000050:  000001c1 00008031 00000000 8e000001
      00000060:  00010000 00000000 00000000 00000000
      00000070:  0092ac10 10008000 00000000 00000000
      00000080:  00000000 00000000 00000000 00000000
      00000090:  00000000 00000000 00000000 00000000
      000000a0:  00000000 00000000 00000000 0000d005
      000000b0:  fee00358 00000000 00000000 00000000
      000000c0:  00000000 00000000 00000000 00000000
      000000d0:  00220001 00000003 00000000 00000000
      000000e0:  00000000 00000000 00008000 00000000
      000000f0:  00000000 00000000 00000000 8cb33018
      00000100:  2001001b 00001400 00000000 00000000
      00000110:  00000000 00000000 00000000 00000000
      00000120:  00000000 00000000 00000000 00000000
      00000130:  00000000 00000000 00000000 00000000
      00000140:  00000000 00000000 00000000 00000000
      00000150:  00000000 00000000 00000000 00000000
      00000160:  00000000 00000000 00000000 00000000
      00000170:  00000000 00000000 00000000 00000000
      00000180:  00000000 00000000 00000000 00000000
      00000190:  00000000 00000000 00000000 00000000
      000001a0:  00000000 00000000 00000000 00000000
      000001b0:  00000000 00000000 00000000 00000000
      000001c0:  00000000 00000000 00000000 00000000
      000001d0:  00000000 00000000 00000000 00000000
      000001e0:  00000000 00000000 00000000 00000000
      000001f0:  00000000 00000000 00000000 00000000
      00000200:  0001000f

2. IO Port 的访问

IO port to read and write【参考3】

Function Command Description / mnemonic
Read IO port ib, iw, id Input from port (byte, word, double word)
Write IO port ob, ow, od Output to port (byte, word, double word)

实例如下 :访问70/71 上面的 CMOS,这个位置是当前的Second 所以过了一段时间会有变化

0: kd> !ob 70 0
0: kd> !ib 71
00000071: 000000000059
0: kd> !ob 70 0
0: kd> !ib 71
00000071: 000000000004

3. Memory 的访问

对于我们来说关注点通常只是物理内存,可以通过 !d* 命令来访问到【参考1】

实例如下 :!db 0xFFFFFF00 l 0x100   //按照Byte访问0xFFFFFF00,长度为 0x100 字节

目标机上同时使用 RW Everything 查看,可以看到结果相同:

4. MSR 的访问

可以通过 rdmsr 和  wrmsr 来实现,例如:

0: kd> rdmsr 0x2ff
msr[2ff] = 00000000`00000c06

此外,CPUID也可以被看作是一种MSR 可以用 CPUID 指令进行访问【参考4】:

0: kd> !cpuid
CP  F/M/S  Manufacturer     MHz
 0  6,142,11 GenuineIntel    1992
 1  6,142,11 GenuineIntel    1992
 2  6,142,11 GenuineIntel    1992
 3  6,142,11 GenuineIntel    1992
 4  6,142,11 GenuineIntel    1992
 5  6,142,11 GenuineIntel    1992
 6  6,142,11 GenuineIntel    1992
 7  6,142,11 GenuineIntel    1992

不过看起来能够提供的信息还是比较有限的。

参考:

  1. https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-pcitree
  2. https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-pci
  3. https://www.cnblogs.com/jiangxueqiao/p/7418195.html 
  4. https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/-cpuid

Step to UEFI (209)subst 命令

Subst 命令是一个很老的 DOS命令,通过它可以将一个目录映射为一个盘符。

例如,我本机当前只有一个盘符c:

通过 subst d: 201903, 即可将 buildbs\201903 这个目录映射为 D:

再次查看会多出一个 d: 盘符

对于我们BIOS编译来说,有时候会发生BUILD Debug版本的时候 Code 爆掉了,超过容量大小的限制。其中很大原因是目录过长。代码中很多DEBUG宏使用了 __FILE__ 定义,在编译中会展开为当前文件的完整路径,凭空要多出十几个字节。累积下来数量可观。为此,可以将你的编路径映射为一个盘符然后进入盘符编译。这样编译过程中路径是 D: 这样可以减少尺寸:

使用完毕后,可以使用 subst 盘符 /d 来取消映射。

台湾宝工(Pro’sKit)真的是一家玩具公司了

前一段时间参加创客嘉年华,惊奇的发现他们在这个活动上有一个摊位,更惊奇的是他们的摊位主打竟然是玩具。然后和他们聊了两句发现他们这些年转型向玩具市场进军。

转过天,我修理东西,拿出了他们家生产的螺丝刀,发现竟然生锈了。

基本上每一个刀头都没有幸免…….
这套工具用的很少
外包装还是完好的

除了这一套螺丝刀,我还有一个游标卡尺也是他们家的

检查发现也是生锈的

工具一直存放在家里,看起他们他们用的材质是很有问题的。如果非要加一个条件的话那就是:宝工产品不适合南方地区使用(作为东北人,过了山海关都算是南方)。

Step to UEFI (208)谁改动了我的文件头?

前面介绍了如何使用汇编语言直接编写UEFI Application,这里我偶然发现一个问题:用汇编生成的EFI 文件中,DOS 头部看起来多了一些东西。比如十六进制查看之前的 NasmUEFI.EFI 文件:

对比 Hello.EFI:

可以看到有些内容被清空为0x00. 首先怀疑的是编译过程中有工具来完成这个动作。于是,重新编译 Hello.EFI 观察到在末期有下面的操作:

Generating code
Finished generating code
        "GenFw" -e UEFI_APPLICATION -o c:\buildbs\201903\Build\AppPkg\DEBUG_VS2015x86\X64\AppPkg\Applications\Hello\Hello\DEBUG\Hello.efi c:\buildbs\201903\Build\AppPkg\DEBUG_VS2015x86\X64\AppPkg\Applications\Hello\Hello\DEBUG\Hello.dll

这个操作输入的是hello.dll 输出为 hello.efi,观察发现DLL 中还存在一些字符串,到了EFI 中会消失。接下来在窗口中直接运行 GenFw 来从 DLL生成EFI文件,最终确认是这个工具来完成移除多余的字符工作的,准确的说只是用0xFF覆盖,并没有引起文件长度的变化。

接下来查看 GenFW 的代码, \BaseTools\Source\C\GenFw\GenFw.c 其中下面就是我们需要找到的位置:

//
  // Zero all unused fields of the DOS header
  //
  if (DosHdr != NULL) {
    memcpy (&BackupDosHdr, DosHdr, sizeof (EFI_IMAGE_DOS_HEADER));
    memset (DosHdr, 0, sizeof (EFI_IMAGE_DOS_HEADER));
    DosHdr->e_magic  = BackupDosHdr.e_magic;
    DosHdr->e_lfanew = BackupDosHdr.e_lfanew;
    for (Index = sizeof (EFI_IMAGE_DOS_HEADER); Index < (UINT32 ) DosHdr->e_lfanew; Index++) {
      FileBuffer[Index] = (UINT8) DosHdr->e_cp;
    }
  }

特别提醒:目前提供的编译辅助工具都是 X86 版本的,因此需要在 x86 Native Tools Command Prompt 窗口下编译,如果使用X64会出现编译报错。

本文只是为了简单试验,并没有考虑一些特殊情况。针对上面的代码进行修改,插入一句话即可:

strcpy(&FileBuffer[sizeof (EFI_IMAGE_DOS_HEADER)],"www.lab-z.com");

这样,再次使用 GenFw 来进行DLL到EFI的转换,可以看到 EFI 文件中DOS Header后面被插入了我们期望的字符串。

通过上述的方法可以在 EFI 文件中自动加入自定义的字符串,通过这样的方法可以制作一些特殊的 EFI 文件,比如只能在特别BIOS上运行的 EFI文件。