详解二进制文件信息熵Entropy的计算

Posted ybdesire

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解二进制文件信息熵Entropy的计算相关的知识,希望对你有一定的参考价值。

1. 引入

Entropy(熵)能被用于衡量系统的混乱程度,熵值越大,说明混乱程度越高(参考1)。

熵也可以被用在特征工程领域,比如我们提取PE文件的熵值作为一个特征,这种情况下的熵值,一般就说的是“信息熵”。

信息熵也可以被用于衡量PE文件是否加壳(参考2)。PEiD中就有计算熵值的功能,当熵的值超过一定阈值时,则该PE或某段被加壳(Packed)。熵值大,就说明PE文件携带信息量多,就意味着PE文件可能被加壳了。当然,道高一尺魔高一丈,“低熵加壳”又是一个不同的研究方向。

信息熵也可以被用于检测PE病毒(参考3)。当然除了PE文件,其他的二进制文件(比如Mach-O,甚至于pythonscript)也都可以提取并使用信息熵作为特征。

“熵”的计算方式有很多种,包括“信息熵”,“交叉熵”,“相对熵”等等,具体计算过程详见参考4。本文主要讲解“信息熵”的计算,本文提到的“熵”也特指“信息熵”。

2. 信息熵的计算过程举例

信息熵的计算公式如下

其中,H用于表示信息熵的值,它一般是一个float小数。P(i)表示第i个事件发生的概率。

举个例子,假设天气事件的发生有4中可能性:阴、晴、雨、雪;每种事件的发生概率都为1/4,则P(1)=P(2)=P(3)=P(4)=1/4。

则熵值H=-14P(i)*log2(P(i))=2,所以熵值H等于2。这就是信息熵的计算过程。

在这个例子中,每种天气发生的概率都是相等的(都为1/4),此时能求得信息熵的最大值。即“概率均等时,信息熵值最大”,这也是被证明过的定理(证明过程见参考5)

3. 二进制文件信息熵的计算过程

对二进制文件计算信息熵,首先要将文件读入为byte数组,则数组中每个数据值的大小为0~255,具体代码如下:

with open('cfdbbd60c7dd63db797fb27e1c427077ce1915b8894ef7165d8715304756a7e2', 'rb') as fr:
	bytez = fr.read()

bytez中就存储了读入的byte数组,举例如下

bytez = b'MZ\\x90\\x00\\x03\\x00\\x00\\x00\\x04'

这个例子中,MZ是PE文件的标识,0x是十六进制数据。这个bytez数组可以转换为如下等效的int数组:

bytez = [77,90,144,0,3,0,0,0,4]

byte数组中有9个数据,字母’M’的ASCII码值为十进制整数77,'Z’的ASCII码值为90。

我们定义P(i)就是byte数组中各个数值为i的概率,则P(77)=1/9, P(90)=P(144)=P(3)=P(4)=1/9, P(0)=4/9。


H=-(P(77)*log2(P(77))+P(90)*log2(P(90))+P(144)*log2(P(144))+P(3)*log2(P(3))+P(4)log2(P(4))+P(0)log2(P(0)) ) = -(5(1/9)log2(1/9)+(4/9)log2(4/9)) = -(5(1/9)(-3.169925)+(4/9)(-1.169925)) = 2.28103611

所以,对这段9个数据的byte数组计算得到的熵值为 2.28103611。

4. python编程实现信二进制文件息熵计算

根据上一小节的说明,python计算二进制文件信息熵,要先读入二进制文件为byte数组,再对byte数组计算各个数值出现的概率,最终根据信息熵的计算公式将概率值log计算后乘法累加。完整代码如下:

import math

def H(data):
    entropy = 0
    for x in range(256):
        p_x = data.count(x)/len(data)
        if p_x > 0:
            entropy += -p_x*math.log(p_x, 2)
    return entropy


with open('cfdbbd60c7dd63db797fb27e1c427077ce1915b8894ef7165d8715304756a7e2', 'rb') as fr:
	bytez = fr.read()
	print('file H',H(bytez))# file H 4.459983287684002

这个例子最终计算得到的结果为4.459983287684002,这说该二进制文件信息熵值为4.459983287684002。

因为二进制文件读入的数据为byte数组,byte数组中每一个元素值的大小为0255,所以上面计算信息熵的函数H()中直接固定了range(256)来计算0255每个数据值出现的概率,这也简化了程序的编程实现。

调用这个H()函数,来对上小节例子中给定的byte数组,代码和结果如下:

bytez1 = b'MZ\\x90\\x00\\x03\\x00\\x00\\x00\\x04'
print('H1',H(bytez1))# 2.2810361125534233
bytez2 = [77,90,144,0,3,0,0,0,4]
print('H2',H(bytez2))# 2.2810361125534233

可以看到,无论是对byte数组bytez1,还是对int数组bytez2,他们的信息熵值都是一样的,这也和上一小节的手工计算结果是完全相等的。

此处使用的信息熵计算H()函数引用自参考6。

5. python第三方库计算信息熵

参考7中有一个pip包pyEntropy可以直接用于计算信息熵,它提供了多种熵值的计算方式

  • Shannon Entropy: shannon_entropy
  • Sample Entropy: sample_entropy
  • Multiscale Entropy: multiscale_entropy
  • Composite Multiscale: Entropy composite_multiscale_entropy
  • Permutation Entropy: permutation_entropy
  • Multiscale Permutation Entropy: multiscale_permutation_entropy
  • Weighted Permutation Entropy: weighted_permutation_entropy

调用它提供的shannon_entropy()就能计算信息熵,示例代码如下:

from pyentrp import entropy as ent

# example1: binary file entropy
with open('cfdbbd60c7dd63db797fb27e1c427077ce1915b8894ef7165d8715304756a7e2', 'rb') as fr:
	bytez = fr.read()
	print('pyentrp H', ent.shannon_entropy(bytez))# 4.459983287684001

# example2: byte array entropy
bytez1 = b'MZ\\x90\\x00\\x03\\x00\\x00\\x00\\x04'
print('pyentrp1', ent.shannon_entropy(bytez1))# 2.2810361125534233
# example3: int list entropy
bytez2 = [77,90,144,0,3,0,0,0,4]
print('pyentrp2', ent.shannon_entropy(bytez2))# 2.2810361125534233

这与第3节手动计算,第4节的python代码计算得到的结果是完全一致的。这也验证了手动计算,python源码计算,第三方库的计算这三种信息熵计算方式结果是一致的。

这个库中计算信息熵的源码见参考8。核心部分的源码解释如下,这和信息熵的计算公式是一致的:

def shannon_entropy(time_series):
    data_set = list(set(time_series))#去重
    freq_list = []#计算每个数值出现的概率
    for entry in data_set:
        counter = 0.#每个数值出现的次数
        for i in time_series:#统计每个数值出现的次数
            if i == entry:
                counter += 1
        freq_list.append(float(counter) / len(time_series))#每个数值出现的概率

    # Shannon entropy,信息熵计算过程
    ent = 0.0
    for freq in freq_list:#信息熵计算公式
        ent += freq * np.log2(freq)
    ent = -ent
    return ent

6. 总结

本文给出了二进制文件信息熵计算的细节过程示例,细节过程的python源码,以及第三方库的计算方式(核心源码解释)。

参考

  1. https://zh.wikipedia.org/wiki/%E7%86%B5
  2. https://www.pediy.com/kssd/pediy11/115515.html
  3. 基于信息熵的模糊模式识别病毒检测方法研究
  4. https://charlesliuyx.github.io/2017/09/11/%E4%BB%80%E4%B9%88%E6%98%AF%E4%BF%A1%E6%81%AF%E7%86%B5%E3%80%81%E4%BA%A4%E5%8F%89%E7%86%B5%E5%92%8C%E7%9B%B8%E5%AF%B9%E7%86%B5/
  5. https://blog.csdn.net/feixi7358/article/details/83861858
  6. https://github.com/gcmartinelli/entroPy/blob/master/entropy.py#L8
  7. https://github.com/nikdon/pyEntropy
  8. https://github.com/nikdon/pyEntropy/blob/master/pyentrp/entropy.py#L80

以上是关于详解二进制文件信息熵Entropy的计算的主要内容,如果未能解决你的问题,请参考以下文章

信息量的度量——熵(entropy)

为什么交叉熵可以用于计算代价函数

一个python的计算熵(entropy)的函数

x264代码剖析(十七):核心算法之熵编码(Entropy Encoding)

Tensorflow四种交叉熵函数计算公式:tf.nn.cross_entropy

图解最大熵原理(The Maximum Entropy Principle)