0%

PE文件中的数字签名信息(2)

前言

在上篇文章中,我们已经提取到了签名二进制数据,使用的是 ASN.1 语法 BER 编码格式,
那么如下为关于 ASN.1BER 的简单介绍,以及其对应的解析方法。

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>,如下图所示

TLV格式

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

TLV嵌套和组合

BER语法是基于八位组大端编码的,高位在左,底位在右。

Tag的格式

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结束

Tag大于0x30时

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
NTSTATUS CheckSign(IN PUCHAR Buffer, IN UINT32 Size)
{
UINT32 i = 0;
UCHAR TagPC = 0; // primitive(0) constructed(1)
UCHAR TagClass = 0; // TAG的类型
UINT32 TagValue = 0;
UINT32 TagLen = 0;
UINT32 LenValue = 0;
UINT32 LenLen = 0;
while (i < Size)
{
// 解析TAG信息
if (!Asn1BerTagDec(Buffer + i, Size - i,
&TagClass, &TagPC, &TagValue, &TagLen)) break;
i += TagLen;
if (i >= Size) break; // 遍历结束
// 解析LEN信息
if (!Asn1BerLenDec(Buffer + i, Size - i, &LenValue, &LenLen)) break;
i += LenLen;
if (i >= Size) break; // 遍历结束
// 检测是否匹配
if (TagPC != 0) continue; // 非原子节点
i += LenValue;
if (i >= Size) break; // 遍历结束
if (TagClass != 0) continue; // 非通用类型
if (TagLen != 1) continue; // TAG长度不是1
if (TagValue != 0x06) continue; // TAG内容不是OBJECT IDENTIFIER
if (LenLen != 1) continue; // LEN长度不是1
if (LenValue != 3) continue; // LEN内容不是3
if ((i - LenValue + 2) >= Size) break; // 55 04 03 commonName(2.5.4.3)
if (*(Buffer + i - LenValue) != 0x55) continue;
if (*(Buffer + i - LenValue + 1) != 0x04) continue;
if (*(Buffer + i - LenValue + 2) != 0x03) continue;
// 处理TAG信息
if (!Asn1BerTagDec(Buffer + i, Size - i,
&TagClass, &TagPC, &TagValue, &TagLen)) break;
i += TagLen;
if (i >= Size) break; // 遍历结束
// 处理LEN信息
if (!Asn1BerLenDec(Buffer + i, Size - i, &LenValue, &LenLen)) break;
i += LenLen;
if (i >= Size) break; // 遍历结束
// 检测是否匹配
if (TagPC != 0) continue; // 非原子节点
i += LenValue;
if (i >= Size) break; // 遍历结束
if (TagClass != 0) continue; // 非通用类型
if (TagLen != 1) continue; // TAG长度不是1
if ((TagValue != 0x0C) && // UTF8String(0x0C)
(TagValue != 0x13)) continue; // PrintableString(0x13)
// 与黑名单签名信息做对比
if (CompareSign(Buffer + i - LenValue, LenValue))
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
}

解析Tag的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
BOOLEAN Asn1BerTagDec(
IN PUCHAR Buffer, IN UINT32 Size,
OUT PUCHAR Class, // Tag的分类
OUT PUCHAR PC, // primitive(0) constructed(1)
OUT PUINT32 Value, // UNIVERSAL的类型
OUT PUINT32 Len) // Tag自身的长度
{
UINT32 i = 0;
*Class = (*Buffer) & 0xC0;
*PC = (*Buffer) & 0x20;
// 检测数据长度
if (((*Buffer) & 0x1F) == 0x1F) // 长编码格式
{
i++;
if (i >= Size) return FALSE;
*Value = 0;
while ((Buffer[i] & 0x80) == 0x80)
{
*Value += ((Buffer[i] & 0x7F) << 7);
i++;
if (i >= Size) return FALSE;
}
*Len = i + 1;
}
else // 短编码格式
{
if ((*Buffer) == 0) return FALSE;
*Value = (*Buffer) & 0x1F;
*Len = 1;
}
return TRUE;
}

解析Length的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
BOOLEAN Asn1BerLenDec(
IN PUCHAR Buffer, IN UINT32 Size,
OUT PUINT32 Value, // Content的长度
OUT PUINT32 Len) // Length自身的长度
{
UINT32 i = 0;
if ((*Buffer) > 0x7F) // 长编码格式
{
i++;
if (i >= Size) return FALSE;
*Value = 0;
*Len = (*Buffer) & 0x7F;
while (i <= *Len)
{
*Value = ((*Value) << 8) + Buffer[i];
i++;
if (i >= Size) return FALSE;
}
*Len += 1;
}
else // 短编码格式
{
*Value = *Buffer;
*Len = 1;
}
return TRUE;
}