首页
社区
课程
招聘
[原创]利用调试寄存器提升PCI扩展ROM代码的兼容性
发表于: 2013-11-21 17:05 11328

[原创]利用调试寄存器提升PCI扩展ROM代码的兼容性

2013-11-21 17:05
11328

Author:Sandman
Date:2013.11.20

前几天一个做硬件的朋友向我求助,他开发的一款磁盘数据加密卡在测试的过程中出现了兼容性问题。通过调试发现问题是由软件的问题造成的。通过一下午的调试,最终使用CPU提供的调试机制完美的解决了兼容性问题。希望本文可以对相关开发人员有所帮助。

PS:虽然本文标题含有“PCI、ROM”相关字样,但是本文不涉及复杂的数字电路理论,解决问题的过程全部是由软件代码完成的,请读者不必恐慌...

出现兼容性问题硬件的故障现象:部分机型插卡开机以后,扩展卡初始化界面没有出现,直接进入OS。但是在OS中可以看到安装的新硬件。

出现故障的机型:
1.以联想天开S4250为代表的联想OEM品牌机
2.HP DC5700等机型
3.部分H61、Q75主板的兼容机

出现兼容性问题硬件的基本原理:
问题硬件基于PCI扩展ROM机制,利用在开机引导过程中PCI扩展ROM中的代码会被BIOS执行的特性,挂钩INT19h拦截OS引导,并在INT19h的中断钩子中挂钩INT13h,INT13h钩子做OS读盘时的数据解密工作。PC的引导过程可以概括为如下6点:
1.开机,BIOS代码被加载至F000h:0000h处执行;BIOS POST自检
2.初始化显示设备
3.初始化外围硬件设备(PCI扩展ROM会在此时执行)
4.内存测试、设备测试等过程
5.调用INT19h加载OS
6.INT19h调用INT13h读取引导磁盘的0磁道0柱面1扇区(MBR)到0000h:7C00h处,并jmp far 0000h:7C00h跳转至MBR所在内存,最终MBR完成指定OS的加载。

在PCI规范中提供了一种机制,使PCI设备可以带一个扩展ROM。通过执行ROM中存放的代码来完成与设备有关的初始化的操作,同时也有可能辅助系统的引导功能,例如挂接中断等操作。问题硬件就利用了这个特性挂钩INT19H,抢在OS加载前获取执行权限。很多还原卡也是利用了这个特性抢在操作系统之前执行。
但是,执行扩展ROM的任务是由BIOS来完成的,不同的主板厂商使用的BIOS不同,极少数的主板甚至不会去执行扩展ROM中的代码。因此就要考虑一个问题:我们的扩展ROM代码会不会被BIOS执行?如果答案是否定的话,解决这个问题的办法只有一个:联系主板厂商升级BIOS。

如果ROM代码被部分执行了,只是在某个阶段出现了问题,这样就要通过调试的方式来解决。大厂商可以使用类似Arium ECM-XDP3这样的硬件调试器来进行实时调试。但是由于本人没有硬件调试器,所以只能使用黑箱排查的方式一步步寻找问题所在。

本文的调试过程基于WCH CH360S扩展ROM调试板,如图所示。

其中J1是外部输入引脚引出,可以通过读写I/O寄存器读出其状态。
根据CH360S的DataSheet,J1引脚为通用输入引脚,内置上拉电阻,默认为高电平,若将J1短接,则GPI1引脚接地,输入为低电平。可以利用这个特性在扩展ROM加载时作判断,如果被短接则不执行后面的代码。这样做的目的是为了方便调试,防止由于程序的错误导致无法进入系统。

在ROM的初始化代码段中,我加入了向屏幕打印输出INT19h中断地址的代码,同时HOOK INT19h中断,在HOOK以后再次打印INT19h中断地址的代码查看是否挂钩成功。HOOK处理例程中设置一个死循环,造成一个黑屏死机的假象,证明我们的INT19h被BIOS调用了。详细代码如下。

BITS 16

CONST_VID EQU 1A00H
CONST_DID EQU 8088H

ROM_START:

ROM_HEADER:
  DB 55H        ;; 00H: SIGNATURE 1
  DB 0AAH        ;; 01H: SIGNATURE 2
  DB (ROM_END-ROM_START) / 512  ;; 02H: IMAGE_SIZE
  JMP ROM_ENTRY      ;; 03H: JUMP INSTRUCTION
  times 12H DB 00H      ;; 06H: PADDING
  DW (PCI_DATA_STRUC-ROM_START)  ;; 18H: PCI_DATA_STRUC POINTER
  DW 0000H
  
PCI_DATA_STRUC:
  DB 'PCIR'        ;; 00H: device flag
  DW CONST_VID        ;; 04H: vendor ID
  DW CONST_DID        ;; 06H: device ID
  DW 0000H        ;; 08H: VPD pointer
  DW 0018H        ;; 0AH: PCI_DATA_STRUC's length
  DB 00H          ;; 0CH: PCI_DATA_STRUC's version
  DB 00H,80H,01H        ;; 0DH: device type.Class Code
  DW (ROM_END-ROM_START) / 512    ;; 10H: Image length
  DW 0000H        ;; 12H: the version of the code data
  DB 00H          ;; 14H: the code type of ROM
  DB 80H          ;; 15H: the flag used to make sure if or not the last ROM image
  DW 0000H        ;; 16H: Reserved

Int19h_Hook_Hanlder:
    jmp $      ;卡在这里,证明INT19H确实被执行了
    int 89h
    iret

Install_Int19h_Hook:
    push ds
    xor ax,ax
    mov ds,ax
    mov eax,[ds:(19h*4)]
    mov [ds:(89h)*4],eax
    mov word [ds:(19h*4)],Int19h_Hook_Hanlder
    mov word [ds:(19h*4)+2],cs
    pop ds
    ret

ROM_ENTRY:
    push ax      ;AX=由BIOS传来的总线号/设备号/功能号
    pusha
    mov bx,ax
    mov ax,0b109h
    mov di,PC_BASE_ADDR  ;读取扩展ROM卡的IO地址
    int 1ah
    and cx,0FFF0h
    mov dx,cx
    mov dl,CH367_GPIR
    in al,dx      ;读取J1引脚的状态,如果被短接则退出
    and al,02h
    jz Exit_Init_Rom
  
    call PrintInt19h    ;打印初始的INT19H地址
    call Install_Int19h_Hook  ;安装INT19h钩子
    call PrintInt19h    ;打印修改后的INT19H地址
    
    

Exit_Init_Rom:
    popa
    pop ax    
    retf
%include "CH360DEF.ASM"
%include "PCIE_CFG.ASM"
%include "ShowIVT.asm"

times 32*1024-($-$$) db 0
ROM_END:
PULONG MapPhysicalMemory(ULONG StartAddress,ULONG RegionSize)
{
  PHYSICAL_ADDRESS PhyAddr;

  PhyAddr.HighPart = 0;
  PhyAddr.LowPart = StartAddress;

  return MmMapIoSpace(PhyAddr,RegionSize,FALSE);
}

VOID UnMapPhysicalMemory(PVOID StartAddress,ULONG RegionSize)
{
  MmUnmapIoSpace((ULONG)StartAddress,RegionSize);
}

VOID PrintInterruptVectorTable(PULONG  IVTBuffer)
{
  ULONG    i;
  USHORT    Segment;
  USHORT    Offset;

  for(i=0;i<0xFF;i++)
  {
    Segment = (USHORT)((IVTBuffer[i] & 0xFFFF0000) >> 16);
    Offset = (USHORT)(IVTBuffer[i] & 0xFFFF);
    DbgPrint("INT 0x%.2X:%.4X:%.4X\n",i,Segment,Offset);
  }
}

VOID DriverUnload(IN PDRIVER_OBJECT pDriverObj)
{  

}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObj, IN PUNICODE_STRING pRegistryString)
{
  PULONG    IVTBuffer;

  IVTBuffer = MapPhysicalMemory(0,0x100000);
  if(IVTBuffer)
  {
    DbgPrint("Mapped IVT:%.p\n",IVTBuffer);
    PrintInterruptVectorTable(IVTBuffer);
    UnMapPhysicalMemory(IVTBuffer,0x100000);
  }

  pDriverObj->DriverUnload = DriverUnload;

  return STATUS_SUCCESS;
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 6
支持
分享
最新回复 (6)
雪    币: 84
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
给力。。。。
2013-11-21 19:25
0
雪    币: 360
活跃值: (127)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
3
赶脚搞硬件的都是高富帅+牛人
2013-11-21 19:46
0
雪    币: 608
活跃值: (648)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
屌丝路过
2013-11-21 19:55
0
雪    币: 360
活跃值: (127)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
5
高富屌丝你好。。。。
2013-12-2 19:41
0
雪    币: 257
活跃值: (67)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
都玩硬件了
2013-12-2 20:20
0
雪    币: 5449
活跃值: (2501)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
大佬,您好,非常喜欢您的精彩分享,你代码中有3个引用文件,能不能分享一下啊。正好手头也有个PCI板卡,也想试试。
 "CH360DEF.ASM"、 "PCIE_CFG.ASM"、 "ShowIVT.asm"。
多谢!
2021-4-1 15:04
0
游客
登录 | 注册 方可回帖
返回
//