Skip to content

Latest commit

 

History

History
154 lines (85 loc) · 5.53 KB

[snowming]-2021-8-15-python 判断一个 PE 是否有数字签名.md

File metadata and controls

154 lines (85 loc) · 5.53 KB

0x01 问题描述

遇到了同如下的问题,在网上没有找到答案:

title

0x02 原理

PE 的头文件中有一处信息是关于证书的,就是 Certificate Table

title

这个 Certificate Table 位于哪里呢? 位于:NT Optional HeaderDATA_DIRECTORYCertificate Table

下图都出自 MSDN 文档:Optional Header Data Directories (Image Only)

title

title

title

让我们来看3个 .exe 文件,分别看他们的 Certificate Table

有证书的 exe:

title

没证书的 exe 之一:

title

没证书的 exe 之二:

title

应该可以看出来区别了:

没有证书的 exe,其 Certificate TableVirtualAddressSize 字段对应的值都为 0。很好理解吧,没有证书,那证书的大小和文件偏移肯定为0鸭。

0x03 代码实现

使用 Python 解析 PE 的库 pefile

先查看 NT 可选头中的 DATA_DIRECTORY 数组:

def hv_cer():
    PEfile_Path = "D:\A\\abexcm1.exe"
    pe = pefile.PE(PEfile_Path)
    print pe.OPTIONAL_HEADER.DATA_DIRECTORY

得到的结果是:

title

这对应着这些表:

title title

图出自:Optional Header Data Directories (Image Only), MSDN

可以看到我们要找的是此数组中的第5个结构体,数组下标4的元素 [IMAGE_DIRECTORY_ENTRY_SECURITY]。打印出来看看:

def hv_cer():
    PEfile_Path = "D:\A\\abexcm1.exe"
    pe = pefile.PE(PEfile_Path)
    print pe.OPTIONAL_HEADER.DATA_DIRECTORY[4].name
    print pe.OPTIONAL_HEADER.DATA_DIRECTORY[4]

title

到此就非常好说了,我使用了 python 的 re 模块的正则搜索来判断那两处是否为0:

def hv_cer(PEfile_Path):
    pe = pefile.PE(PEfile_Path)
    IMAGE_DIRECTORY_ENTRY_SECURITY = str(pe.OPTIONAL_HEADER.DATA_DIRECTORY[4])
    pattern1 = r'VirtualAddress:\s+0x0\b'
    pattern2 = r'Size:\s+0x0\b'
    if (re.search(pattern1, IMAGE_DIRECTORY_ENTRY_SECURITY) is None and re.search(pattern2, IMAGE_DIRECTORY_ENTRY_SECURITY) is None):
        print('{0} have certificate!'.format(PEfile_Path))

注意这里有一个坑:re 模块因为单行\多行模式的原因,影响了正则中 ^$ 的行为,用 \b 单词边界来匹配 pattern 的末尾比较好。 参考:Python正则表达式中的re.S,re.M,re.I的作用

0x04 测试结果

对于带数字签名的 exe strings64.exe

strings64.exe

title

对于不带数字签名的 exe wusa.exe

title

附上成品代码:

import os, string, re
import pefile


def hv_cer(PEfile_Path):
    pe = pefile.PE(PEfile_Path)
    IMAGE_DIRECTORY_ENTRY_SECURITY = str(pe.OPTIONAL_HEADER.DATA_DIRECTORY[4])
    pattern1 = r'VirtualAddress:\s+0x0\b'
    pattern2 = r'Size:\s+0x0\b'
    if (re.search(pattern1, IMAGE_DIRECTORY_ENTRY_SECURITY) is None and re.search(pattern2, IMAGE_DIRECTORY_ENTRY_SECURITY) is None):
        print('{0} have certificate!'.format(PEfile_Path))

参考文档:

  1. 恶意文件分析系统中的数字签名验证,绿盟技术博客,2015-07-29,参考 PE 查找证书的原理
  2. Optional Header Data Directories (Image Only), MSDN, 参考 PE 结构的权威资料
  3. Python读写PE文件模块pefile,Python 俱乐部,参考 pefile 库的用法
  4. erocarrera/pefile,Github pefile 库项目地址
  5. pefile/ReadingResourceStrings.md,Github,pefile 库的一个比较有用的用法示例
  6. Python正则表达式中的re.S,re.M,re.I的作用,博客园,傻白甜++,2019-3-30,参考 Python re 模块多行模式对于正则符号 $ ^ 的影响
  7. PE Format Manipulation with PEFile,BreakInSecurity,Alexandre CHERON,2017-12-12,参考 pefile 库的详细用法