前言
在上篇文章中,我们已经提取到了签名二进制数据,使用的是 ASN.1 语法 BER 编码格式,
那么如下为关于 ASN.1 和 BER 的简单介绍,以及其对应的解析方法。
ASN.1介绍
ASN.1 是一种 抽象语法标记 语言,本身只定义表示信息的抽象句法,并没有对编码的方法做出限定。
其可以使用的编码规则有很多,这里使用的是基本编码规则 BER,即 Basic Encoding Rules 。
在上篇文章给出的微软链接 Authenticode_PE.docx 文档中,有关于签名的具体 ASN.1 结构体定义。
另外微软关于 BER 的一些知识也做了说明,相关链接如下:
https://docs.microsoft.com/en-us/windows/win32/seccertenroll/distinguished-encoding-rules
BER编码规则介绍
BER语法的格式是 TLV 三元组 <Type, Length, Value>,如下图所示

该三元组整体可以作为 Value 进行嵌套或者组合,如下图所示

BER语法是基于八位组大端编码的,高位在左,底位在右。
Tag的格式
Tag的格式按位数划分,如下图所示

第7,6位指明Tag的类型。第5位指明该类型以 primitive 方式编码还是 constructed 方式编码
| 第7,6位 | 类型 | 说明 |
|---|---|---|
| 00 | UNIVERSAL | 标准类型,规定的固定类型 |
| 01 | APPLICATION | 唯一标志应用内的类型,不推荐使用 |
| 10 | context-specific | 由上下文指定类型,在SEQUENCE、SET和CHOICE类型中使用 |
| 11 | PRIVATE | 在小范围内唯一标志一个类型,不推荐使用 |
其中 UNIVERSAL 类型规定的固定类型如下所示
| 第4-0位 | 说明 |
|---|---|
| 0 | 保留 |
| 1 | BOOLEAN |
| 2 | INTEGER |
| 3 | BIT STRING |
| 4 | OCTET STRING |
| 5 | NULL |
| 6 | OBJECT IDENTIFIER |
| 7 | ObjectDescripion |
| 8 | EXTERNAL,INSTANCE OF |
| 9 | REAL |
| 10 | ENUMERATED |
| 11 | EMBEDDED PDV |
| 12 | UFT8String |
| 13 | RELATIVE-OID |
| 14 | 保留 |
| 15 | 保留 |
| 16 | SEQUENCE,SEQUENCE OF |
| 17 | SET,SET OF |
| 18 | NumericString |
| 19 | PrintableString |
| 20 | TeletexString,T61String |
| 21 | VideotexString |
| 22 | IA5String |
| 23 | UTCTime |
| 24 | GeneralizedTime |
| 25 | GraphicString |
| 26 | VisibleString,ISO646String |
| 27 | GeneralString |
| 28 | UniversalString |
| 29 | CHARACTER STRING |
| 30 | BMPString |
| 31 | 保留 |
如果Tag大于0x30,当第1个字节低5位全部为1时,后续的字节最高位为1表示还有,为0表示Tag结束

Length的格式
Length 表示 Content 的字节长度,如果 Content 超过127个字节,还需要增加表示 Length 的长度,
方法为在长度数据的前边添加 0x80,比如 0x30 82 01 10 这里的 0x82 表示后边2个字节都是长度数据。
对象标识符
对象标识符(OBJECT IDENTIFIER, OID)类型,用层次的形式来表示标准规范,标识符树通过一个点分的
十进制符号来定义,这个符号以组织、子部分、标准的类型和各自的子标识符开始。
例如:MD5的OID是 1.2.840.113549.2.5 表示为 iso(1),member-body(2),US(840),rsadsi(113549),
digestAlgorithm(2),md5(5)。
OID的编码规则:将前两部分定义为x.y,合成为一个字节40*x+y,其余每个部分按照big-endian格式,
按照7个bit位进行分割,每个分割出来的片段第8标为1,组成一个字节,最后一个片段第8位标为0。
例如:MD5的计算方式为,原始数据为(42,840,113549,2,5),按照7位分割后((0x2A),(0x86,0x48),
(0x86,0xF7,0x0D),(0x02),(0x05)),最终 TLV 编码为 0x06 08 2A 86 48 86 F7 0D 02 05 。
如下为微软给出的OID信息如何计算的链接:
https://docs.microsoft.com/en-us/windows/win32/seccertenroll/about-object-identifier
查找签名的代码
查找签名的过程,就是循环解析 TLV 三元组信息,找到签名对应的 OID 信息
1 | NTSTATUS CheckSign(IN PUCHAR Buffer, IN UINT32 Size) |
解析Tag的函数
1 | BOOLEAN Asn1BerTagDec( |
解析Length的函数
1 | BOOLEAN Asn1BerLenDec( |