探索Windows S3唤醒函数 (一)

  • Post author:
  • Post category:其他


最近看UEFI中关于S3 Resume的实现,在Resume的最后阶段,UEFI通过SwitchStack执行OS的S3代码:

UefiCpuPkg\Universal\Acpi\S3Resume2Pei\S3Resume.c:
VOID
EFIAPI
S3ResumeBootOs (
  IN ACPI_S3_CONTEXT                *AcpiS3Context,
  IN PEI_S3_RESUME_STATE            *PeiS3ResumeState
  )
{
...
    DEBUG ((DEBUG_INFO, "Transfer to 32bit OS waking vector - %x\r\n", (UINTN)Facs->XFirmwareWakingVector));
      SwitchStack (
        (SWITCH_STACK_ENTRY_POINT) (UINTN) Facs->XFirmwareWakingVector,
        NULL,NULL,(VOID *)(UINTN)TempStackTop);
...
}

根据ACPI Spec,Facs->XFirmwareWakingVector是OS执行S3唤醒的函数:

X Firmware Waking

Vector
8 24 64-bit physical address of OSPM’s Waking Vector.

Before transitioning the system into a global sleeping state, OSPM

fills in this field and the OSPM Flags field to describe the waking

vector. OSPM populates this field with the physical memory

address of an OS-specific wake function. During POST, the

platform firmware checks if the value of this field is non-zero and if

so transfers control to OSPM by jumping to this address after

creating the appropriate execution environment

表格摘自ACPI 6.2 “5.2.10 Firmware ACPI Control Structure (FACS)”,FACS表用于firmware和OS传输数据,表格中提到的OSPM是指OS的ACPI.sys模块。如表格所述,Facs->XFirmwareWakingVector的值由Windows 负责填写。在Comet lake platform上Facs->XFirmwareWakingVector的物理地址值为:0x2000,所以,我决定在windows上查找具体设置的代码(

既然OS能设置,驱动模块也能设置,这是挂Hook的绝佳选择

)。

查看ACPI table当然少不了rw工具,不过很可惜,不知出于什么原因,在xp\win7\win10上Facs->XFirmwareWakingVector都不约而同的不可见(目前,我猜想:如ACPI spec如说,该值只有在进入S3 Sleep时才会被设置):

虽然不清楚Facs->XFirmwareWakingVector的值,但也不能阻挡我搜索的步伐。查找ReactOS 3.15源码后,我选择用windbg搜索WakeVector相关的符号:

drivers\bus\acpi\acpica\hardware\hwsleep.c

ACPI_STATUS
AcpiSetFirmwareWakingVector64 (
    UINT64                  PhysicalAddress)
{
    ACPI_FUNCTION_TRACE (AcpiSetFirmwareWakingVector64);


    /* Determine if the 64-bit vector actually exists */

    if ((AcpiGbl_FACS->Length <= 32) || (AcpiGbl_FACS->Version < 1))
    {
        return_ACPI_STATUS (AE_NOT_EXIST);
    }

    /* Clear 32-bit vector, set the 64-bit X_ vector */

    AcpiGbl_FACS->FirmwareWakingVector = 0;
    AcpiGbl_FACS->XFirmwareWakingVector = PhysicalAddress;
    return_ACPI_STATUS (AE_OK);
}

(跟各位得瑟一把,由于工作缘故,我有部分Checked Build OS,所以我逆向的工作量会减轻不少),并最终在hal模块中找到对应的(变量)符号,然而该变量的值依然是空:

kd> x *!*WakeVector*
82a21c98          hal!HalpWakeVector = <no type information>

kd> dd hal!HalpWakeVector
82a21c98  00000000 00000000 00000000 00000000
82a21ca8  00000000 00000000 00000000 00000000
82a21cb8  00000000 00000000 00000000 00000000
82a21cc8  00000000 00000000 00000000 00000000

很遗憾,Windows内核原理\Windows情景分析等经典书都不曾关注S3\S4的实现,没有直接的参考来源;而本文OS S3相关代码是在虚拟机上做的测试,虚拟机进入S3后系统被挂起,windbg也失去作用,因此,我对hal模块进行简单的逆向分析:

1.确定hal模块:

kd> lm m hal
Browse full module list
start    end        module name
82a04000 82a3d000   hal        (pdb symbols)          e:\symbols\win7x86sp1chk\halmacpi.pdb\0B89B9AEB8FB4822A25882B9A877C1031\halmacpi.pdb

kd> lmvm hal
Browse full module list
start    end        module name
82a04000 82a3d000   hal        (pdb symbols)          e:\symbols\win7x86sp1chk\halmacpi.pdb\0B89B9AEB8FB4822A25882B9A877C1031\halmacpi.pdb
    Loaded symbol image file: halmacpi.dll
    Image path: halmacpi.dll
    Image name: halmacpi.dll
    Browse all global symbols  functions  data

虚拟机上使用的hal模块对应c:\windows\system32\halmacpi.dll,用IDA加载该dll(附注,根据调试发现,下文将提到的内容在win7\win8.1\win10差别不大,因此对于这部分OS还是具有参考价值)。本文借助win10x64 TH2 Checked build的halmacpi.dll为参考,对win10 RS5 free build的halmacpi.dll进行分析(附注,微软提供的win10x64 TH2 Checked build无法正常安装使用,如果看官手头有该OS,需要用dism命令,从镜像中提取halmacpi.dll。此处要感谢我的同事lyly,告诉我dism提取镜像中模块的方法)

2.搜索对hal!HalpWakeVector的引用:

呵呵,很可惜,还真没找到。不过运气好,在IDA “Strings window”中看到一串重要的字符串以及函数HalpAcpiGetFacsMapping对它的引用:

01C003D8C0 aHalpacpigetfac db '**** HalpAcpiGetFacsMapping: No FADT found.',0Ah,0
.text:00000001C003D8C0                                         ; DATA XREF: HalpAcpiGetFacsMapping+54↑o

2.1.在HalpAcpiGetFacsMapping内部,有两段代码片:

01C0001E39                 mov     edx, 'PCAF'
.text:00000001C0001E3E                 call    HalpAcpiGetTableWork
.text:00000001C0001E43                 test    rax, rax
.text:00000001C0001E46                 jnz     short loc_1C0001E83 ; Physical memory address of the FACS

这段代码是根据Acpi table的签名来查找Acpi table。Long字符数组’PCAF’其实就是FACP表的签名。HalpAcpiGetTableWork将返回FACP表的物理地址。然后根据FACP->FIRMWARE_CTRL和签名”SCAF”再次获得Facs地址:

01C0001E83 loc_1C0001E83:                          ; CODE XREF: HalpAcpiGetFacsMapping+52↑j
.text:00000001C0001E83                 mov     edx, [rax+FACP_20.FIRMWARE_CTRL] ; facs
.text:00000001C0001E86                 mov     r9d, 'SCAF'     ; Physical memory address of the FACS
...
.text:00000001C0001EB2                 call    HalpAcpiCheckAndMapTable
.text:00000001C0001EB7                 mov     cs:HalpAcpiFacsMapping, rax ; cs:HalpAcpiFacsMapping: hold facs phyical address
...
.text:00000001C0001ED3                 mov     rax, cs:HalpAcpiFacsMapping
FIRMWARE_CTRL 4 36 Physical memory address of the FACS, where OSPM and

Firmware exchange control information. See Section 5.2.6,

“Root System Description Table,” for a description of the FACS. If

the X_FIRMWARE_CTRL field contains a non zero value which

can be used by the OSPM, then this field must be ignored by the

OSPM. If the HARDWARE_REDUCED_ACPI flag is set, and both

this field and the X_FIRMWARE_CTRL field are zero, there is no

FACS available.

根据acpi spec,FIRMWARE_CTRL存放Facs表物理地址。各位看官需要从EDKII code中提取FACP和FACS表结构并创建头文件,然后在IDA-File-Load-“parse C header file”加载该头文件,并在IDA “Structures”窗口中引用头文件中定义的FACP/FACS结构,这样能提到代码的可阅读性:

2.2.HalpAcpiGetFacsMapping返回值

HalpAcpiGetFacsMapping返回Facs表的物理地址。

3.定位HalpWakeVector

获得Facs表的目的,八九不离十就是获得WakeVector的地址,所以,还要查找对HalpAcpiGetFacsMapping的引用:

.text:00000001C0001DF4 HalpAcpiGetFacsMapping proc near        ; CODE XREF: HalAcpiGetFacsMappingDispatch+2↑j
.text:00000001C0001DF4                                         ; HaliInitPowerManagement+BA↓p
.text:00000001C0001DF4                                         ; DATA XREF: ...

果然,在HaliInitPowerManagement函数中找到对hal!HalpWakeVector的赋值:

PAGE:00000001C005F53A                 call    HalpAcpiGetFacsMapping
PAGE:00000001C005F53F                 test    rax, rax
PAGE:00000001C005F542                 jz      short loc_1C005F54F
PAGE:00000001C005F544                 add     rax, FACS_20.FwWakingVector
PAGE:00000001C005F548                 mov     cs:HalpWakeVector, rax

HaliInitPowerManagement首先调用HalpAcpiGetFacsMapping函数,如果Facs表非空,则从Facs->FwWakingVector取值赋给HalpWakeVector。现在终于定位到关键变量,它是开启windows中ACPI S3 Resume大门的钥匙,下一篇来看下对OS对hal!HalpWakeVector的存取。



版权声明:本文为lixiangminghate原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。