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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
import math
import threading
from PySide6.QtWidgets import QWidget
from PySide6.QtGui import QImage, QColor, QPainter
from PySide6.QtCore import Qt, QSize, QTimer, Signal
import binaryninjaui
from binaryninjaui import ViewFrame, UIContext
from binaryninja.enums import ThemeColor
class EntropyThread(threading.Thread):
def __init__(self, data, image, block_size):
super(EntropyThread, self).__init__()
self.data = data
self.image = image
self.block_size = block_size
self.updated = False
self.average_entropy = 0.0
self.sample_count = 0
self.lock = threading.Lock()
def run(self):
width = self.image.width()
for i in range(0, width):
entropy_result = self.data.get_entropy(self.data.start + i * self.block_size, self.block_size)
entropy_value = entropy_result[0] if entropy_result else 0.0
v = int(entropy_value * 255)
# Update running average
with self.lock:
self.sample_count += 1
self.average_entropy += (entropy_value - self.average_entropy) / self.sample_count
if v >= 240:
color = binaryninjaui.getThemeColor(ThemeColor.YellowStandardHighlightColor)
self.image.setPixelColor(i, 0, color)
else:
baseColor = binaryninjaui.getThemeColor(ThemeColor.FeatureMapBaseColor)
entropyColor = binaryninjaui.getThemeColor(ThemeColor.BlueStandardHighlightColor)
color = binaryninjaui.mixColor(baseColor, entropyColor, v)
self.image.setPixelColor(i, 0, color)
self.updated = True
def get_average_entropy(self):
with self.lock:
return self.average_entropy
class EntropyWidget(QWidget):
entropyUpdated = Signal(float)
def __init__(self, parent, view, data):
super(EntropyWidget, self).__init__(parent)
self.view = view
self.data = data
self.raw_data = data.file.raw
self.block_size = (self.raw_data.length / 4096) + 1
if self.block_size < 1024:
self.block_size = 1024
self.width = int(self.raw_data.length / self.block_size)
self.image = QImage(self.width, 1, QImage.Format_ARGB32)
self.image.fill(QColor(0, 0, 0, 0))
self.thread = EntropyThread(self.raw_data, self.image, self.block_size)
self.started = False
self.timer = QTimer()
self.timer.timeout.connect(self.timerEvent)
self.timer.setInterval(100)
self.timer.setSingleShot(False)
self.timer.start()
self.setCursor(Qt.PointingHandCursor)
self.setMouseTracking(True)
self.setMinimumHeight(UIContext.getScaledWindowSize(32, 32).height())
def paintEvent(self, event):
p = QPainter(self)
p.drawImage(self.rect(), self.image)
p.drawRect(self.rect())
def sizeHint(self):
return QSize(640, 32)
def timerEvent(self):
if not self.started:
self.thread.start()
self.started = True
if self.thread.updated:
self.thread.updated = False
self.update()
self.entropyUpdated.emit(self.thread.get_average_entropy())
def mousePressEvent(self, event):
if event.button() != Qt.LeftButton:
return
frac = float(event.x()) / self.rect().width()
offset = int(frac * self.width * self.block_size)
self.view.navigateToFileOffset(offset)
def mouseMoveEvent(self, event):
frac = float(event.x()) / self.rect().width()
offset = int(frac * self.width * self.block_size)
addr = self.data.get_address_for_data_offset(offset)
if addr is not None:
self.setToolTip(f"0x{addr:x}")
else:
self.setToolTip(f"File offset: 0x{offset:x}")
def get_average_entropy(self):
return self.thread.get_average_entropy()
|