-
-
[原创]windbg调试ACPI ASL Code 实例一则
-
发表于: 2019-8-31 15:22 6375
-
准备工作:搭建ACPI调试环境请先移步至:《搭建Win7调试ACPI的环境》里面有Win 7 Checked Build ACPI.sys
本文中OS为Win7 x86(镜像:en_windows_7_ultimate_x86_dvd_X15-65921.iso)
kd> vertarget ;记得OS一定要和ACPI.sys匹配,否则替换ACPI后OS可能无法启动。 ;我的OS版本信息如下 Windows 7 Kernel Version 7600 MP (1 procs) Free x86 compatible Built by: 7600.16385.x86fre.win7_rtm.090713-1255
这次调试对象为vmware中串口设备COM1,如下图,目标是在设备管理中执行Disable Com1时,跟踪COM1所执行的AML代码(即,Method(_DIS))。
kd> vertarget ;记得OS一定要和ACPI.sys匹配,否则替换ACPI后OS可能无法启动。 ;我的OS版本信息如下 Windows 7 Kernel Version 7600 MP (1 procs) Free x86 compatible Built by: 7600.16385.x86fre.win7_rtm.090713-1255
这次调试对象为vmware中串口设备COM1,如下图,目标是在设备管理中执行Disable Com1时,跟踪COM1所执行的AML代码(即,Method(_DIS))。

这是要跟踪的COM1 ACPI Method:
kd> !amli dns /s \_sb.pci0.isa.sio.COMA._DIS ;!amli dns /s查找ACPI命名空间中的命名对象 ACPI Name Space: \_SB.PCI0.ISA.SIO.COMA._DIS (ffffffff85172318) Method(_DIS:Flags=0x0,CodeBuff=85172375,Len=7)
我停用COM1前,设置设备管理器的列出设备的方式为"Devices by connection",这为了方便确认COM设备在设备树中的总线路径。根据上图显示,初步断定设备COM1在ACPI命名空间中的路径是:\_SB.PCI0.ISA.SIO.COMA。确认一下我的猜测是否正确:
kd> !amli find COMA \_SB.PCI0.ISA.SIO.COMA
!amli find将输出匹配的ACPI命名对象在ACPI命名空间中的完整路径。在不同域下会有同名ACPI对象,因此find的输出可能不唯一。比如,很多设备(毕竟Device(){}会在ACPI命名空间中创建一个新的域)都有Method(_DIS)这个函数,因此,执行!amli find _DIS可能会得到这样的输出:

如果不确定上述输出中哪个是调试目标,可以在windbg开启ACPI日志输出,然后执行对应的ACPI Method(如停用/启用设备)。在启停过程中,windbg将输出ACPI Object及相应Method执行的信息:
kd> !amli set spewon verboseon traceon ;开启ACPI Log输出 kd> g ;执行该命令后,在设备管理中停用COM1,得到如下输出 AMLI: 85409D48: EvalNameSpaceObject(\_SB.PCI0.ISA.SIO.COMA._STA) AMLI: 85409D48: \_SB.PCI0.ISA.SIO.COMA._STA() ffffffff851723e5: { ffffffff851723e5: | If(And(SIOC=0xf,0x2,)=0x2) ffffffff851723ef: | { ffffffff851723ef: | | If(FCMA=0x0) ffffffff851723f8: | | If(S1BL=0xfe) ffffffff851723fe: | | { ffffffff851723fe: | | | Return(0xd) ffffffff85172401: | | } ffffffff85172401: | }
我们在设备管理器中看到的COM设备名是COM1,为什么在ACPI输出中看到的设备名是\_SB.PCI0.ISA.SIO.COMA (RW Utility中在\_SB.PCI0.ISA下也找不到COM1这样的设备)?来看下Device(COMA)的ASL Code:
kd> !amli dns /s \_SB.PCI0.ISA.SIO.COMA ACPI Name Space: \_SB.PCI0.ISA.SIO.COMA (ffffffff85172020) Device(COMA) | Integer(_HID:Value=0x000000000105d041[17158209]) | Integer(_UID:Value=0x0000000000000001[1]) | String(_DDN:Str="COM1") | Method(_CRS:Flags=0x8,CodeBuff=851721c1,Len=18) | Method(_SRS:Flags=0x9,CodeBuff=8517223d,Len=89) | Method(_PRS:Flags=0x0,CodeBuff=85172301,Len=10) | Method(_DIS:Flags=0x0,CodeBuff=85172375,Len=7) | Method(_STA:Flags=0x0,CodeBuff=851723e5,Len=46)
ACPI Method是ACPI对象,ACPI Device也是ACPI对象,因此都可以用!amli dns /s命令查看对象信息。当然也可以RW Utility查看Device(COMA)的实现:

查看ACPI Spec,_DDN对象用于设置设备DOS Device Name(是不是可以认为是设备的别名?),可能这是我们在设备管理器中看到COM1的原因。
找到了COMA的Method对象,试试单步跟踪ACPI Method。根据前面的Log输出可知在停用过程中会调用Method(_STA)&Method(_DIS)方法,就在他们身上下断点吧:
kd> !amli bp \_SB.PCI0.ISA.SIO.COMA._STA kd> !amli bp \_SB.PCI0.ISA.SIO.COMA._DIS
停用设备后,windbg会中断到ACPI解释器,(此时Windbg的Command框前缀为"Input>",在Command框中直接输入!amli的command即可,如bp \_SB.PCI0.ISA.SIO.COMA_STA,不用带!amli),如图:

之前下了2个ASL Code断点,所以windbg中断时,我们并不清楚中断发生在哪个函数,可以用!amli lc命令查看后台正在执行的ACPI Method:
Input> AMLI(? for help)-> lc lc *Ctxt=ffffffff85f56000, ThID=ffffffff85157998, Flgs=A--CR----, pbOp=ffffffff851723e5, Obj=\_SB.PCI0.ISA.SIO.COMA._STA ;每一行输出代表正在执行的ACPI method;如果行首带"*"表示该函数在执行过程中触发了中断
执行完!amli lc后,可以顺带执行!amli r命令,查看该函数执行的上下文(参数/局部变量等信息):
Input> AMLI(? for help)-> r r Context=85f56000*, Queue=0, ResList=0 ThreadID=85f5600085157998, Flags=00000130, pbOp=ffffffff851723e5:[\_SB.PCI0.ISA.SIO.COMA._STA] StackTop=ffffffff85f57ee8, UsedStackSize=280 bytes, FreeStackSize=7664 bytes LocalHeap=85f560c4, CurrentHeap=ffffffff85f560c4, UsedHeapSize=52 bytes Object=\_SB.PCI0.ISA.SIO.COMA._STA, Scope=\_SB.PCI0.ISA.SIO.COMA._STA, ObjectOwner=ffffffff85f560e8, SyncLevel=0 AsyncCallBack=ffffffff87c4ac82, CallBackData=ffffffff8555fee0, CallBackContext=ffffffff8555feb0 ;ASL Method的局部变量 MethodObject=\_SB.PCI0.ISA.SIO.COMA._STA ffffffff85f57f38: Local0=Unknown() ffffffff85f57f50: Local1=Unknown() ffffffff85f57f68: Local2=Unknown() ffffffff85f57f80: Local3=Unknown() ffffffff85f57f98: Local4=Unknown() ffffffff85f57fb0: Local5=Unknown() ffffffff85f57fc8: Local6=Unknown() ffffffff85f57fe0: Local7=Unknown() 85f56040: RetObj=Unknown() Next AML Pointer: ffffffff851723e5:[\_SB.PCI0.ISA.SIO.COMA._STA] ;ASL Method的实现 ffffffff851723e5 : If(And(SIOC, 0x2, )) ffffffff851723ef : { ffffffff851723ef : | If(FCMA) ffffffff851723f5 : | { ffffffff851723f5 : | | Return(0xf) ffffffff851723f8 : | } ffffffff851723f8 : | If(S1BL)
最后,中断到ASL Method中了,就可以用!amli t或者!amli p单步跟踪Method执行:
Hit Breakpoint 1. AMLI(? for help)-> t ;单步执行\_SB.PCI0.ISA.SIO.COMA._STA函数的ASL Code t ;单步1 If(And(SIOC=0xf,0x2,)=0x2 AMLI(? for help)-> t t ;单步2 ) ffffffff851723ef: | { ffffffff851723ef: | | If(FCMA=0x0) ;!amli u反汇编即将执行的ASL Code AMLI(? for help)-> u u ffffffff851723f8:[\_SB.PCI0.ISA.SIO.COMA._STA+0x13] ffffffff851723f8 : If(S1BL) ffffffff851723fe : { ffffffff851723fe : | Return(0xd) ffffffff85172401 : } ffffffff85172401 : Store(Ones, S1BL) ffffffff85172407 : If(S1BL) ffffffff8517240d : { ffffffff8517240d : | Return(0xd) ffffffff85172410 : } ffffffff85172410 : Return(AMLI_DBGERR: UnAsmDataObj: input buffer is too small AMLI_DBGERR: Failed to unassemble scope at da317e2 (size=27)
最后的最后,一些ACPI事件发生时,可以从ACPI解释器退出到内核调试器(!amli q),查看触发该ACPI事件的DeviceObject及相应的IRP。以ACPI _PTS对象为例,当发生电源事件转换时,会执行该对象。
kd> !amli bp \_PTS kd> g ;执行后马上关机,windbg中断到ACPI解释器 AMLI(? for help)-> q

在AMLI解释器中,输入q后,ACPI解释器(Input>)退出到内核调试器(kd>)。执行kb,查看进入ACPI解释器的函数栈:
kd> kb # ChildEBP RetAddr Args to Child 00 8078fac8 87cad6f4 00000000 00000000 00000000 ACPI!DebugQuit+0x24 01 8078faf8 87cad875 87cc5128 8078fc1c 873c29e0 ACPI!DbgExecuteCmd+0xaf 02 8078fe34 87c9c1fa 87cc5128 87cb8854 8078fe64 ACPI!Debugger+0x104 03 8078fe44 87c9a145 00000000 873c29e0 873c29d8 ACPI!AMLIDebugger+0x33 .... 13 8a00fae0 82a5cd16 86390030 85181bf0 00000016 ACPI!ACPIDispatchIrp+0x1dc 14 8a00faf8 82a8d4ae 8746fb2c 86390030 8a00fb10 nt!IopPoHandleIrp+0x28 15 8a00fb08 82a5f049 8a00fb30 87c53b57 86390030 nt!IofCallDriver+0x55 16 8a00fb10 87c53b57 86390030 8746fa58 87c5c398 nt!IoCallDriver+0x10
windbg输出的frame# 15显示在关闭OS时,曾执行了IoCallDriver,并最终进入到ACPI解释器。IoCallDriver的参数有DeviceObject和IRP,通过这两个参数可以定位执行Method(_PTS)的设备:
kd> !object 86390030 Object: 86390030 Type: (8521ebc8) Device ObjectHeader: 86390018 (new version) HandleCount: 0 PointerCount: 7 Directory Object: 88e0f638 Name: 0000004a kd> !devstack 86390030 !DevObj !DrvObj !DevExt ObjectName 86667e10 \Driver\intelppm 866665d0 > 86390030 \Driver\ACPI 85181bf0 0000004a !DevNode 860dba18 : DeviceInst is "ACPI\GenuineIntel_-_x86_Family_6_Model_94_-_Intel(R)_Core(TM)_i5-6300HQ_CPU_@_2.30GHz\_0" ServiceName is "intelppm"
kd> !amli dns /s \_sb.pci0.isa.sio.COMA._DIS ;!amli dns /s查找ACPI命名空间中的命名对象 ACPI Name Space: \_SB.PCI0.ISA.SIO.COMA._DIS (ffffffff85172318) Method(_DIS:Flags=0x0,CodeBuff=85172375,Len=7)
我停用COM1前,设置设备管理器的列出设备的方式为"Devices by connection",这为了方便确认COM设备在设备树中的总线路径。根据上图显示,初步断定设备COM1在ACPI命名空间中的路径是:\_SB.PCI0.ISA.SIO.COMA。确认一下我的猜测是否正确:
kd> !amli find COMA \_SB.PCI0.ISA.SIO.COMA
kd> !amli find COMA \_SB.PCI0.ISA.SIO.COMA
!amli find将输出匹配的ACPI命名对象在ACPI命名空间中的完整路径。在不同域下会有同名ACPI对象,因此find的输出可能不唯一。比如,很多设备(毕竟Device(){}会在ACPI命名空间中创建一个新的域)都有Method(_DIS)这个函数,因此,执行!amli find _DIS可能会得到这样的输出:

!amli find将输出匹配的ACPI命名对象在ACPI命名空间中的完整路径。在不同域下会有同名ACPI对象,因此find的输出可能不唯一。比如,很多设备(毕竟Device(){}会在ACPI命名空间中创建一个新的域)都有Method(_DIS)这个函数,因此,执行!amli find _DIS可能会得到这样的输出:

如果不确定上述输出中哪个是调试目标,可以在windbg开启ACPI日志输出,然后执行对应的ACPI Method(如停用/启用设备)。在启停过程中,windbg将输出ACPI Object及相应Method执行的信息:
kd> !amli set spewon verboseon traceon ;开启ACPI Log输出 kd> g ;执行该命令后,在设备管理中停用COM1,得到如下输出 AMLI: 85409D48: EvalNameSpaceObject(\_SB.PCI0.ISA.SIO.COMA._STA) AMLI: 85409D48: \_SB.PCI0.ISA.SIO.COMA._STA() ffffffff851723e5: { ffffffff851723e5: | If(And(SIOC=0xf,0x2,)=0x2) ffffffff851723ef: | { ffffffff851723ef: | | If(FCMA=0x0) ffffffff851723f8: | | If(S1BL=0xfe) ffffffff851723fe: | | { ffffffff851723fe: | | | Return(0xd) ffffffff85172401: | | } ffffffff85172401: | }
我们在设备管理器中看到的COM设备名是COM1,为什么在ACPI输出中看到的设备名是\_SB.PCI0.ISA.SIO.COMA (RW Utility中在\_SB.PCI0.ISA下也找不到COM1这样的设备)?来看下Device(COMA)的ASL Code:
kd> !amli set spewon verboseon traceon ;开启ACPI Log输出 kd> g ;执行该命令后,在设备管理中停用COM1,得到如下输出 AMLI: 85409D48: EvalNameSpaceObject(\_SB.PCI0.ISA.SIO.COMA._STA) AMLI: 85409D48: \_SB.PCI0.ISA.SIO.COMA._STA() ffffffff851723e5: { ffffffff851723e5: | If(And(SIOC=0xf,0x2,)=0x2) ffffffff851723ef: | { ffffffff851723ef: | | If(FCMA=0x0) ffffffff851723f8: | | If(S1BL=0xfe) ffffffff851723fe: | | { ffffffff851723fe: | | | Return(0xd) ffffffff85172401: | | } ffffffff85172401: | }
我们在设备管理器中看到的COM设备名是COM1,为什么在ACPI输出中看到的设备名是\_SB.PCI0.ISA.SIO.COMA (RW Utility中在\_SB.PCI0.ISA下也找不到COM1这样的设备)?来看下Device(COMA)的ASL Code:
kd> !amli dns /s \_SB.PCI0.ISA.SIO.COMA ACPI Name Space: \_SB.PCI0.ISA.SIO.COMA (ffffffff85172020) Device(COMA) | Integer(_HID:Value=0x000000000105d041[17158209]) | Integer(_UID:Value=0x0000000000000001[1]) | String(_DDN:Str="COM1") | Method(_CRS:Flags=0x8,CodeBuff=851721c1,Len=18) | Method(_SRS:Flags=0x9,CodeBuff=8517223d,Len=89) | Method(_PRS:Flags=0x0,CodeBuff=85172301,Len=10) | Method(_DIS:Flags=0x0,CodeBuff=85172375,Len=7) | Method(_STA:Flags=0x0,CodeBuff=851723e5,Len=46)
ACPI Method是ACPI对象,ACPI Device也是ACPI对象,因此都可以用!amli dns /s命令查看对象信息。当然也可以RW Utility查看Device(COMA)的实现:
kd> !amli dns /s \_SB.PCI0.ISA.SIO.COMA ACPI Name Space: \_SB.PCI0.ISA.SIO.COMA (ffffffff85172020) Device(COMA) | Integer(_HID:Value=0x000000000105d041[17158209]) | Integer(_UID:Value=0x0000000000000001[1]) | String(_DDN:Str="COM1") | Method(_CRS:Flags=0x8,CodeBuff=851721c1,Len=18) | Method(_SRS:Flags=0x9,CodeBuff=8517223d,Len=89) | Method(_PRS:Flags=0x0,CodeBuff=85172301,Len=10) | Method(_DIS:Flags=0x0,CodeBuff=85172375,Len=7) | Method(_STA:Flags=0x0,CodeBuff=851723e5,Len=46)
ACPI Method是ACPI对象,ACPI Device也是ACPI对象,因此都可以用!amli dns /s命令查看对象信息。当然也可以RW Utility查看Device(COMA)的实现:

查看ACPI Spec,_DDN对象用于设置设备DOS Device Name(是不是可以认为是设备的别名?),可能这是我们在设备管理器中看到COM1的原因。
找到了COMA的Method对象,试试单步跟踪ACPI Method。根据前面的Log输出可知在停用过程中会调用Method(_STA)&Method(_DIS)方法,就在他们身上下断点吧:
kd> !amli bp \_SB.PCI0.ISA.SIO.COMA._STA kd> !amli bp \_SB.PCI0.ISA.SIO.COMA._DIS
停用设备后,windbg会中断到ACPI解释器,(此时Windbg的Command框前缀为"Input>",在Command框中直接输入!amli的command即可,如bp \_SB.PCI0.ISA.SIO.COMA_STA,不用带!amli),如图:
查看ACPI Spec,_DDN对象用于设置设备DOS Device Name(是不是可以认为是设备的别名?),可能这是我们在设备管理器中看到COM1的原因。
找到了COMA的Method对象,试试单步跟踪ACPI Method。根据前面的Log输出可知在停用过程中会调用Method(_STA)&Method(_DIS)方法,就在他们身上下断点吧:
kd> !amli bp \_SB.PCI0.ISA.SIO.COMA._STA kd> !amli bp \_SB.PCI0.ISA.SIO.COMA._DIS
停用设备后,windbg会中断到ACPI解释器,(此时Windbg的Command框前缀为"Input>",在Command框中直接输入!amli的command即可,如bp \_SB.PCI0.ISA.SIO.COMA_STA,不用带!amli),如图:
kd> !amli bp \_SB.PCI0.ISA.SIO.COMA._STA kd> !amli bp \_SB.PCI0.ISA.SIO.COMA._DIS
停用设备后,windbg会中断到ACPI解释器,(此时Windbg的Command框前缀为"Input>",在Command框中直接输入!amli的command即可,如bp \_SB.PCI0.ISA.SIO.COMA_STA,不用带!amli),如图:

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2019-8-31 21:10
被hyjxiaobia编辑
,原因: 追加内容
赞赏
他的文章
赞赏
雪币:
留言: