Windows下打开HIPM和DIPM 的方法

Microsoft在设计 ModernStandy 功能的时候没有想过在普通的机械硬盘(HDD)上使用这个功能。于是,正常情况下,如果当前系统有HDD的话是无法进入MS的。这时候需要手工打开 DIPM 和 HIPM 。具体方法如下:

1.打开 Power Options ->Edit Plan Settings

2.编辑注册表Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\PowerSettings\0012ee47-9041-4b5d-9b77-535fba8b1442\0b2d69d7-a2a1-449c-9680-f91c70521c60将这个位置的 Attributes 设置为 2

3.再次打开Power Options ->Edit Plan Settings 可以看到目前出现选项可以选择当前的模式。

与之相反,如果你想提高当前硬盘的性能,可以考虑关闭 DIPM和 HIPM。

本文参考 https://mywindowshub.com/add-ahci-link-power-management-hipmdipm-power-options-windows-10/

CP2102 USB转TTL 串口分析

CP210x是Silicon Labs 公司出品的一系列USB转串口芯片。目前这一系列有:CP2101, CP2102, CP2103, CP2104, CP2105, 和CP2108。其中不同版本差别在下图可以看到【参考1】:

最近我拿到一个 CP2102 的USB转串口卡,发现Windows10可以通过Windows Update直接安装驱动无需额外下载。另外,它的官方网站提供了大量的资料和参考给人感觉很好。我的目标是使用 Arduino 来直接驱动,因此还需要进行一些额外的研究。非常遗憾,之前介绍的USBlyser抓包结果让人看起来一头雾水,最后只能使用USB逻辑分析仪抓包。一端通过 Arduino 按照 9600,8,None,1,None 模式发送。另外一端使用串口工具读取。数据是单独的 A  2秒后发送“string from www.lab-z.com” 字符串。对照网站提供的AN571可以解读全部数据。

1.打开/关闭串口

2.查询CTS/RTS等等线的状态

设备返回状态信息

3.查询 Serial Status

返回一个19Bytes的结构体

4.设置通讯参数

0x0800 : 1 Stop bit, None Parity, Data bits=8

5.设置流控制

发送一个 20Bytes 的数值,上面发送多次的原因是前两次被NAK 了,第三次才成功。

6.设置特殊控制字符(实际上我们不需要)

7.设置波特率

这个 0x01C200 是 115200 的波特率。紧接着又设置了一次,这次是 9600

8.设置 Modem 握手方式(我们没有用),这里用了 0x0200, 设置了 RTS

随后又设置用 DTS

参考:

1. https://www.silabs.com/documents/public/application-notes/an976-cp2102-3-4-9-to-cp2102n-porting-guide.pdf

Step to UEFI (213)EFI 的WinPE 格式研究

这一系列文章并不是按照“由浅入深”的原则排列的,更多的是按照“任务驱动”的方式编写的。就是按照:提出问题,解决问题,为什么能解决问题,是否还有其他解决问题的方法,原理探究的方式进行排列的。这种方式可以帮助你快速掌握编程技术,当然要想实现这个目标更重要的是要亲手操作研读代码。

前面的文章中介绍过 EFI 文件格式的一些简单知识,这里会对一只EFI 做完整的分析,标明每一个字节的含义。

首先,我们选择实验的目标是 \AppPkg\Applications\Hello

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

/***
  Print a welcoming message.

  Establishes the main structure of the application.

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
  )
{
  Print(L"Hello there fellow Programmer.\n");
  Print(L"Welcome to the world of EDK II.\n");

  return(0);
}

这个代码功能很简单,就是显示两行字符串。生成的代码为 8,160 (0x1FE0)bytes大小。

特别的,要在对应的INF文件中加入下面的语句保证生成 COD文件。

[BuildOptions]
  MSFT:*_*_X64_CC_FLAGS  = /FAsc /Od

接下来使用 SFF 工具直接分析 EFI:

1.从 0 到 0x3C 是一个 Dos Header。这个只是作为兼容性的结构存在并没有任何功能。

这个结构可以在\MdePkg\Include\IndustryStandard\PeImage.h 看到定义

///
/// PE images can start with an optional DOS header, so if an image is run
/// under DOS it can print an error message.
///
typedef struct {
  UINT16  e_magic;    ///< Magic number.
  UINT16  e_cblp;     ///< Bytes on last page of file.
  UINT16  e_cp;       ///< Pages in file.
  UINT16  e_crlc;     ///< Relocations.
  UINT16  e_cparhdr;  ///< Size of header in paragraphs.
  UINT16  e_minalloc; ///< Minimum extra paragraphs needed.
  UINT16  e_maxalloc; ///< Maximum extra paragraphs needed.
  UINT16  e_ss;       ///< Initial (relative) SS value.
  UINT16  e_sp;       ///< Initial SP value.
  UINT16  e_csum;     ///< Checksum.
  UINT16  e_ip;       ///< Initial IP value.
  UINT16  e_cs;       ///< Initial (relative) CS value.
  UINT16  e_lfarlc;   ///< File address of relocation table.
  UINT16  e_ovno;     ///< Overlay number.
  UINT16  e_res[4];   ///< Reserved words.
  UINT16  e_oemid;    ///< OEM identifier (for e_oeminfo).
  UINT16  e_oeminfo;  ///< OEM information; e_oemid specific.
  UINT16  e_res2[10]; ///< Reserved words.
  UINT32  e_lfanew;   ///< File address of new exe header.
} EFI_IMAGE_DOS_HEADER;

使用 SFF 工具可以方便的看到每个项目释义:

可以看到大多数信息都是 00,从前面的实验也可以知道,这是编译过程中被我们的工具擦掉的。

2.接下来是 NT Header, 分为两个部分,一个是 File Header 另一个是 Optional Header.

这个结构同样在  \MdePkg\Include\IndustryStandard\PeImage.h 有定义,可以看到其中是有2部分的。

///
/// @attention
/// EFI_IMAGE_HEADERS64 is for use ONLY by tools.
///
typedef struct {
  UINT32                      Signature;
  EFI_IMAGE_FILE_HEADER       FileHeader;
  EFI_IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} EFI_IMAGE_NT_HEADERS64;

2.1 先看一下 File Header,从 0xBC到 0xCE.

///
/// COFF File Header (Object and Image).
///
typedef struct {
  UINT16  Machine;
  UINT16  NumberOfSections;
  UINT32  TimeDateStamp;
  UINT32  PointerToSymbolTable;
  UINT32  NumberOfSymbols;
  UINT16  SizeOfOptionalHeader;
  UINT16  Characteristics;
} EFI_IMAGE_FILE_HEADER;

其中的 Characteristics 提供了一些基本信息:

2.2 Optional Header 从 0xD0到0x1B4,其中有一些比较重要的信息

比如,这里给出了AddressOfEntryPoint 就是EFI文件的代码入口。

下面是其中的 DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES] 定义:

///
/// Optional Header Standard Fields for PE32+.
///
typedef struct {
  ///
  /// Standard fields.
  ///
  UINT16                    Magic;
  UINT8                     MajorLinkerVersion;
  UINT8                     MinorLinkerVersion;
  UINT32                    SizeOfCode;
  UINT32                    SizeOfInitializedData;
  UINT32                    SizeOfUninitializedData;
  UINT32                    AddressOfEntryPoint;
  UINT32                    BaseOfCode;
  ///
  /// Optional Header Windows-Specific Fields.
  ///
  UINT64                    ImageBase;
  UINT32                    SectionAlignment;
  UINT32                    FileAlignment;
  UINT16                    MajorOperatingSystemVersion;
  UINT16                    MinorOperatingSystemVersion;
  UINT16                    MajorImageVersion;
  UINT16                    MinorImageVersion;
  UINT16                    MajorSubsystemVersion;
  UINT16                    MinorSubsystemVersion;
  UINT32                    Win32VersionValue;
  UINT32                    SizeOfImage;
  UINT32                    SizeOfHeaders;
  UINT32                    CheckSum;
  UINT16                    Subsystem;
  UINT16                    DllCharacteristics;
  UINT64                    SizeOfStackReserve;
  UINT64                    SizeOfStackCommit;
  UINT64                    SizeOfHeapReserve;
  UINT64                    SizeOfHeapCommit;
  UINT32                    LoaderFlags;
  UINT32                    NumberOfRvaAndSizes;
  EFI_IMAGE_DATA_DIRECTORY  DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES];
} EFI_IMAGE_OPTIONAL_HEADER64;

3.紧接着是Section Headers[x]

定义在 \MdePkg\Include\IndustryStandard\PeImage.h

///
/// Section Table. This table immediately follows the optional header.
///
typedef struct {
  UINT8 Name[EFI_IMAGE_SIZEOF_SHORT_NAME];
  union {
    UINT32  PhysicalAddress;
    UINT32  VirtualSize;
  } Misc;
  UINT32  VirtualAddress;
  UINT32  SizeOfRawData;
  UINT32  PointerToRawData;
  UINT32  PointerToRelocations;
  UINT32  PointerToLinenumbers;
  UINT16  NumberOfRelocations;
  UINT16  NumberOfLinenumbers;
  UINT32  Characteristics;
} EFI_IMAGE_SECTION_HEADER;

我们这次分析的EFI 文件中各个节如下:

.text  执行代码的节;

.rdata 保存常量数据的节;

.data 保存数据的节,这个对应C语言中以初始化的全局变量数据;

XXXX 一个名称全部为空的节;

.pdata和 .xdata都存放的是异常处理相关的内容。

4.随后是Relocation Directory

对应在  \MdePkg\Include\IndustryStandard\PeImage.h 有如下定义:

///
/// Relocation format.
///
typedef struct {
  UINT32  VirtualAddress;
  UINT32  SymbolTableIndex;
  UINT16  Type;
} EFI_IMAGE_RELOCATION;

5.下面是 Debug  Directory 在  \MdePkg\Include\IndustryStandard\PeImage.h 有如下定义

///
/// Debug Directory Format.
///
typedef struct {
  UINT32  Characteristics;
  UINT32  TimeDateStamp;
  UINT16  MajorVersion;
  UINT16  MinorVersion;
  UINT32  Type;
  UINT32  SizeOfData;
  UINT32  RVA;           ///< The address of the debug data when loaded, relative to the image base.
  UINT32  FileOffset;    ///< The file pointer to the debug data.
} EFI_IMAGE_DEBUG_DIRECTORY_ENTRY;

Section Headers 后面就是紧密排列着的每个 Section 的内容了。

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/

=========================================================

2025年4月22日 更新的离线 WDTF 安装包

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 = &amp;Private->VirtualScreen[(VerticalResolution - SrcY - 1) * HorizontalResolution + SourceX];
0000000075422192 49 03 D6             add         rdx,r14  
0000000075422195 4C 8B C5             mov         r8,rbp  
      VScreen = &amp;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