头条的_signature这个如何_如何自制一个UEFI实用小工具:RomHover

9732283b88b7f18ef9686edc9700508e.png

有网友在微信公众号私信我,想要这篇文章里面介绍的RomHover:

https://zhuanlan.zhihu.com/p/30106517​zhuanlan.zhihu.com

为了授人以鱼不如授人以渔,今天详细介绍一下它的原理。首先,最新的RomHover可以在这里下载到:链接:https://pan.baidu.com/s/1XVHUtkQNDwTnyuYpweHPug 提取码:yo0a。

RomHover如何使用

要使用RomHover,需要注册它(因为它是个COM组件),方法是用管理员方式打开命令行窗口,用regsvr32来注册:

2b591cef91689a2177ef1c197ac3594c.png

注意必须是管理员方式,否则没有权限运行regsvr32。RomHover不在当前目录,请自动补全目录。

注册好了,最好重启一下,或者杀掉Explore进程,用来重载所有的Explore扩展组件Shell Extension。好了,我们开启一个文件资源管理器,会发现所有的.efi文件图标都换了:

0bbcf5e9966631f555a21aff72d1726b.png

把鼠标悬停在这些UEFI驱动上面,过一会,会弹出一个提示窗口:

95a8b127050abe224d5f553086343db5.png

这是标准的文件管理器的infotip窗口,里面有很多有用的信息,如支持CPU的架构是啥,对齐方式,是哪种驱动,PDB文件在哪里等等。这里看到这个是X86的64位驱动,是个Boot service驱动。我们换一个看看:

dc326418a710eac1ad17812d1d79e084.png

这是个标准的Shell,可以看到它的类型是UEFI应用,而且只能被用于32位的x86架构。

f39adb765ebb76904b4c30d437b835e2.png

而同样名字的另一个Shell,却是仅支持ARM 64bit。是不是很方便?再也不会用错版本了!我们甚至可以从pdb文件路径看出它当初是在哪个目录下被build的。

534cad6609f7950aebd2019c220e20a4.png

看,这个支持NTFS的UEFI驱动,也是ARM 64bit的,不要用错了哦。

RomHover的原理

RomHover程序,它是一个标准的COM组件,关于它的原理,可以写一本书,这里就不展开了,希望详细了解的同学,强烈推荐这本潘爱民的《com原理与应用》:

【二手旧书9成新】COM原理与应用 潘爱民 清华大学出版社【图片 价格 品牌 报价】-京东​item.jd.com
c4ebd0fb1275cd0e1806cc1c713db614.png

com技术和com+、dcom曾经十分火热,在Windows中得到广泛应用。很多应用扩展都是通过com技术实现。但COM技术已经被淘汰,还没入坑的同学就别跟了。

这个DLL格式的COM组件,内部包含了几个注册文件,其中比较重要的是这个

HKCR
{
	RomHover.RomProperty.1 = s 'RomProperty Class'
	{
		CLSID = s '{44109032-A5CA-11D4-8755-0080AD121757}'
	}
	RomHover.RomProperty = s 'RomProperty Class'
	{
		CLSID = s '{44109032-A5CA-11D4-8755-0080AD121757}'
		CurVer = s 'RomHover.RomProperty.1'
	}
	NoRemove CLSID
	{
		ForceRemove {44109032-A5CA-11D4-8755-0080AD121757} = s 'RomProperty Class'
		{
			ProgID = s 'RomHover.RomProperty.1'
			VersionIndependentProgID = s 'RomHover.RomProperty'
			ForceRemove 'Programmable'
			InprocServer32 = s '%MODULE%'
			{
				val ThreadingModel = s 'Apartment'
			}
			'TypeLib' = s '{44109024-A5CA-11D4-8755-0080AD121757}'
		}
	}
    NoRemove .efi = s 'UEFIDriverTitle'
    {
        NoRemove shellex
        {
            NoRemove {00021500-0000-0000-C000-000000000046} = s '{44109032-A5CA-11D4-8755-0080AD121757}'
        }
    }
	
    NoRemove UEFIDriverTitle
    {
   	DefaultIcon = s '%MODULE%,-205'
    }
    

e5253f3357d7c5d43ce69091f88ecb9a.png

标黄的部分更改了.efi文件的icon,为RomHover文件的205号图标。也就是:

#define IDI_ICON1                       205

微软为文件管理器制定的标准COM接口可以在MSDN上查到[1]。悬停的接口叫做IQueryInfo[2],我们的关键类CRomProperty就从它上派生:

BEGIN_COM_MAP(CRomProperty)
	COM_INTERFACE_ENTRY(IRomProperty)
	COM_INTERFACE_ENTRY(IDispatch)
	COM_INTERFACE_ENTRY(IPersistFile)
	COM_INTERFACE_ENTRY(IQueryInfo)
END_COM_MAP()

protected:
    // IRomInfoShlExt
    CString m_sFilename;

// IRomProperty
public:
    // IPersistFile
    STDMETHOD(GetClassID)(LPCLSID)      { return E_NOTIMPL; }
    STDMETHOD(IsDirty)()                { return E_NOTIMPL; }
    STDMETHOD(Load)(LPCOLESTR, DWORD);
    STDMETHOD(Save)(LPCOLESTR, BOOL)    { return E_NOTIMPL; }
    STDMETHOD(SaveCompleted)(LPCOLESTR) { return E_NOTIMPL; }
    STDMETHOD(GetCurFile)(LPOLESTR*)    { return E_NOTIMPL; }

    // IQueryInfo
    STDMETHOD(GetInfoFlags)(DWORD*)     { return E_NOTIMPL; }
    STDMETHOD(GetInfoTip)(DWORD, LPWSTR*);

其它的method都不用实现,返回E_NOTIMPL就行。GetInfoTip是主程序,把鼠标悬停在文件上,就会调用这个方法,它的代码是:

HRESULT CRomProperty::GetInfoTip ( DWORD dwFlags, LPWSTR* ppwszTip )
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());    // init MFC

	LPMALLOC   pMalloc;
	CFile		file;
	CString    sFirstLine;
	CString    sTooltip;

    UNREFERENCED_PARAMETER(dwFlags);
    USES_CONVERSION;

	EFI_IMAGE_DOS_HEADER                DosHdr;
	EFI_IMAGE_OPTIONAL_HEADER_UNION		Hdr;
	UINT16                              Magic;

    // Try to open the file.
    if ( !file.Open ( m_sFilename , CFile::modeRead | CFile::shareDenyWrite ))
        return E_FAIL;

	file.Read(&DosHdr,sizeof(EFI_IMAGE_DOS_HEADER));

	if (DosHdr.e_magic == EFI_IMAGE_DOS_SIGNATURE) {
		//
		// DOS image header is present, so read the PE header after the DOS image header.
		//
		file.Seek((DosHdr.e_lfanew) & 0x0ffff,CFile::begin);
		file.Read(&Hdr,sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION));

	} else {
		//
		// DOS image header is not present, so PE header is at the image base.no in parked support for now
		//
        return E_FAIL;
	}
	CString tempstr;

	if (Hdr.Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
		sTooltip = _T("不能识别该驱动!n");
		goto SHOWRESULT;
	}


    sTooltip = _T("文件名:");
	sTooltip += m_sFilename;
    sTooltip += _T("n");

	sTooltip += _T("nPE文件头内容:n");

	sTooltip += _T("t支持CPU架构:t");
    switch (Hdr.Pe32.FileHeader.Machine) {
    case EFI_IMAGE_MACHINE_IA32:
      //
      // Assume PE32 image with IA32 Machine field.
      //
      Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
	  tempstr = _T("IA32(32bit)");
      break;
    case EFI_IMAGE_MACHINE_X64:
      //
      // Assume PE32+ image with X64 or IPF Machine field
      //
      Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
	  tempstr = _T("X64(64bit)");
      break;
    case EFI_IMAGE_MACHINE_EBC:
      //
      // Assume PE32+ image with X64 or IPF Machine field
      //
      Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
	  tempstr = _T("EBC");
      break;

	case IMAGE_FILE_MACHINE_ARMT:
		Magic = Hdr.Pe32.OptionalHeader.Magic;
		tempstr = _T("ARM 32 mixed");
	break;

	case IMAGE_FILE_MACHINE_ARM64:
		Magic = Hdr.Pe32.OptionalHeader.Magic;
		tempstr = _T("ARM 64bit");
		break;

    case EFI_IMAGE_MACHINE_IA64:
      //
      // Assume PE32+ image with X64 or IPF Machine field
      //
      Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
	  tempstr = _T("IA64(IPF 64bit)");
      break;
    default:
      //
      // For unknow Machine field, use Magic in optional Header
      //
      Magic = Hdr.Pe32.OptionalHeader.Magic;
	  tempstr = _T("Unknown");

    }
	sTooltip += tempstr;
    sTooltip += _T("n");

	if (Hdr.Pe32.FileHeader.TimeDateStamp != 0) {
		time_t TimeX = (time_t)Hdr.Pe32.FileHeader.TimeDateStamp;
		tm* pGMT = gmtime(&TimeX);
		char* pTime = asctime(pGMT);
		tempstr.Format(_T("t驱动生成时间:t%s"), pTime);
	}
	else {
		tempstr.Format(_T("t驱动生成时间:t已清除n"));
	}
	sTooltip += tempstr;

	sTooltip += _T("tSection数目:t");
	tempstr.Format(_T("%d"),Hdr.Pe32.FileHeader.NumberOfSections);
	sTooltip += tempstr;
    sTooltip += _T("n");

	sTooltip += _T("nOPTIONAL文件头内容:n");
	sTooltip += _T("Magic:ttt");
	PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;

	if(Hdr.Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) 
	{
		tempstr.Format(_T("%X (PE32) "),EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC);
		ImageContext.ImageType		= Hdr.Pe32.OptionalHeader.Subsystem;
	    ImageContext.ImageSize		= (UINT64)Hdr.Pe32.OptionalHeader.SizeOfImage;
		ImageContext.SectionAlignment  = Hdr.Pe32.OptionalHeader.SectionAlignment;
		ImageContext.SizeOfHeaders     = Hdr.Pe32.OptionalHeader.SizeOfHeaders;
		ImageContext.NumberOfRvaAndSizes = Hdr.Pe32.OptionalHeader.NumberOfRvaAndSizes;
		memcpy(ImageContext.DataDirectory,Hdr.Pe32.OptionalHeader.DataDirectory,sizeof(EFI_IMAGE_DATA_DIRECTORY)* EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES);

	}else 
	{
		tempstr.Format(_T("%X (PE32+) "),EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC);
		ImageContext.ImageType		   = Hdr.Pe32Plus.OptionalHeader.Subsystem;
		ImageContext.ImageSize         = (UINT64) Hdr.Pe32Plus.OptionalHeader.SizeOfImage;
		ImageContext.SectionAlignment  = Hdr.Pe32Plus.OptionalHeader.SectionAlignment;
		ImageContext.SizeOfHeaders     = Hdr.Pe32Plus.OptionalHeader.SizeOfHeaders;
		ImageContext.NumberOfRvaAndSizes = Hdr.Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
		memcpy(ImageContext.DataDirectory,Hdr.Pe32Plus.OptionalHeader.DataDirectory,sizeof(EFI_IMAGE_DATA_DIRECTORY)* EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES);
 
	}
	sTooltip += tempstr;
    sTooltip += _T("n");

	sTooltip += _T("驱动类型:tt");
    switch (ImageContext.ImageType) {
    case EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
	  tempstr = _T("UEFI Runtime驱动");
      break;
    case EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION:
	  tempstr = _T("UEFI Application");
      break;
    case EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
	  tempstr = _T("UEFI Boot Service驱动");
      break;
    case EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER:
	  tempstr = _T("SAL Runtime Driver");
      break;
	}
	sTooltip += tempstr;
    sTooltip += _T("n");

	tempstr.Format(_T("t入口函数地址:t0x%xn"), Hdr.Pe32Plus.OptionalHeader.AddressOfEntryPoint);
	sTooltip += tempstr;

	tempstr.Format(_T("tSection对齐:t0x%xn"), Hdr.Pe32Plus.OptionalHeader.SectionAlignment);
	sTooltip += tempstr;

	tempstr.Format(_T("t文件对齐:t0x%xn"), Hdr.Pe32Plus.OptionalHeader.FileAlignment);
	sTooltip += tempstr;

	tempstr.Format(_T("t文件大小:t%d(Byte)n"), Hdr.Pe32Plus.OptionalHeader.SizeOfImage);
	sTooltip += tempstr;

	sTooltip += _T("tRva目录 数目:t");
	tempstr.Format(_T("%d"), ImageContext.NumberOfRvaAndSizes);
	sTooltip += tempstr;
	sTooltip += _T("n");

	for (int i = 0; i < 10; i++) {
		switch (i) {
		case 0:
			sTooltip += _T("ttExport Directory		RVA [size]:");
			break;
		case 1:
			sTooltip += _T("ttImport Directory	RVA [size]:");
			break;
		case 2:
			sTooltip += _T("ttResource Directory	RVA [size]:");
			break;
		case 3:
			sTooltip += _T("ttException Directory	RVA [size]:");
			break;
		case 4:
			sTooltip += _T("ttCert Directory		RVA [size]:");
			break;
		case 5:
			sTooltip += _T("ttBaseReloc Directory	RVA [size]:");
			break;
		case 6:
			sTooltip += _T("ttDebug Directory	RVA [size]:");
			break;
		case 7:
			sTooltip += _T("ttArch Directory		RVA [size]:");
			break;
		case 8:
			sTooltip += _T("ttGlobal Ptr Directory	RVA [size]:");
			break;
		case 9:
			sTooltip += _T("ttTLS Directory		RVA [size]:");
			break;
		case 10:
			sTooltip += _T("ttLoad Conf Directory	RVA [size]:");
			break;
		case 11:
			sTooltip += _T("ttBoundImport Directory	RVA [size]:");
			break;
		case 12:
			sTooltip += _T("ttAddrTable Directory	RVA [size]:");
			break;
		case 13:
			sTooltip += _T("ttDelayImport Directory	RVA [size]:");
			break;
		case 14:
			sTooltip += _T("ttCOM Desc Directory	RVA [size]:");
			break;
		case 15:
			sTooltip += _T("ttReserved Directory	RVA [size]:");
			break;
		}
		tempstr.Format(_T("%x [%x]"), ImageContext.DataDirectory[i].VirtualAddress, ImageContext.DataDirectory[i].Size);
		sTooltip += tempstr;
		sTooltip += _T("n");
	}

	if (Hdr.Pe32Plus.OptionalHeader.SectionAlignment != Hdr.Pe32Plus.OptionalHeader.FileAlignment) {
		// Can't handle mixed alignment by now, skip
		sTooltip += _T("Section和文件对齐不同,部分信息已忽略!n");
		goto SHOWRESULT;
	}

	sTooltip += _T("nDebug目录n");
	sTooltip += _T("        Time  Type        Size        RVA    Pointern");
	sTooltip += _T("    --------  ------   --------     --------      --------n");

	EFI_IMAGE_DEBUG_DIRECTORY_ENTRY DebugEntry;
	file.Seek(ImageContext.DataDirectory[6].VirtualAddress, CFile::begin);
	file.Read(&DebugEntry, sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY));

	tempstr.Format(_T("   %08x cv      %08x %08x %xn"), DebugEntry.TimeDateStamp, DebugEntry.SizeOfData, DebugEntry.RVA, DebugEntry.FileOffset);
	sTooltip += tempstr;

	sTooltip += _T("npdb文件位置: ");
	file.Seek(DebugEntry.FileOffset, CFile::begin);
	UINT8 buff[800];
	memset(buff, 0, 800);
	file.Read(buff, DebugEntry.SizeOfData);
	void* CodeViewEntryPointer = buff;
	char* lpStr;

	switch (*(UINT32*)CodeViewEntryPointer) {
	case CODEVIEW_SIGNATURE_NB10:
		lpStr = (char*)((char*)CodeViewEntryPointer + sizeof(EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY));
		break;
	case CODEVIEW_SIGNATURE_RSDS:
		lpStr = (char*)((char*)CodeViewEntryPointer + sizeof(EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY));
		break;
	case CODEVIEW_SIGNATURE_MTOC:
		lpStr = (char*)((char*)CodeViewEntryPointer + sizeof(EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY));
		break;
	default:
		lpStr = "Unknow";
		break;

	}
	tempstr.Format(_T("%sn"), lpStr);
	sTooltip += tempstr;

SHOWRESULT:
	// Get an IMalloc interface from the shell.
    if ( FAILED( SHGetMalloc ( &pMalloc )))
        return E_FAIL;

    // Allocate a buffer for Explorer.  Note that the must pass the string 
    // back as a Unicode string, so the string length is multiplied by the 
    // size of a Unicode character.
    *ppwszTip = (LPWSTR) pMalloc->Alloc ( (10 + lstrlen(sTooltip)) * sizeof(wchar_t) );

    // Release the IMalloc interface now that we're done using it.
    pMalloc->Release();

    if ( NULL == *ppwszTip )
    {
	    return E_OUTOFMEMORY;
    }

    // Use the Unicode string copy function to put the tooltip text in the buffer.
    wcscpy ( *ppwszTip, T2COLE((LPCTSTR) sTooltip) );

    return S_OK;
}

代码比较丑,见谅啊。本质上它就是按照pe格式解析了uefi驱动(uefi驱动是pe格式,和我们Windows的EXE和dll一样),并把想要输出的内容放到一个unicode的字串中,传出去拉倒,逻辑十分简单。

结语

RomHover的代码我再clean up一下,就开源到github上。权当抛砖引玉。com技术个人认为设计十分优美,充分利用了 c++的STL和ATL库,可惜现在几乎没人懂了。

欢迎大家关注我的专栏和用微信扫描下方二维码加入微信公众号"UEFIBlog",在那里有最新的文章。

d32356ef4acac843ddf218351454d6dd.png
用微信扫描二维码加入UEFIBlog公众号

参考

  1. ^ShellEx介绍 https://docs.microsoft.com/en-us/windows/win32/shell/handlers
  2. ^IQueryInfo https://docs.microsoft.com/zh-cn/windows/win32/api/shlobj_core/nn-shlobj_core-iqueryinfo?redirectedfrom=MSDN
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值