33from functools import cached_property
44from statistics import mean
55
6+ from ..utils import *
7+
68
79CACHE_DIR = os .path .expanduser ("~/.exeplot" )
810# https://matplotlib.org/2.0.2/examples/color/named_colors.html
4850SHADOW = {'shade' : .3 , 'ox' : .005 , 'oy' : - .005 , 'linewidth' : 0. }
4951SUBLABELS = {
5052 'ep' : lambda d : "EP at 0x%.8x in %s" % d ['ep' ][1 :],
51- 'size' : lambda d : "Size = %s" % _human_readable_size (d ['size' ], 1 ),
53+ 'size' : lambda d : "Size = %s" % human_readable_size (d ['size' ], 1 ),
5254 'size-ep' : lambda d : "Size = %s\n EP at 0x%.8x in %s" % \
53- (_human_readable_size (d ['size' ], 1 ), d ['ep' ][1 ], d ['ep' ][2 ]),
55+ (human_readable_size (d ['size' ], 1 ), d ['ep' ][1 ], d ['ep' ][2 ]),
5456 'size-ent' : lambda d : "Size = %s\n Average entropy: %.2f\n Overall entropy: %.2f" % \
55- (_human_readable_size (d ['size' ], 1 ), mean (d ['entropy' ]) * 8 , d ['entropy*' ]),
57+ (human_readable_size (d ['size' ], 1 ), mean (d ['entropy' ]) * 8 , d ['entropy*' ]),
5658 'size-ep-ent' : lambda d : "Size = %s\n EP at 0x%.8x in %s\n Average entropy: %.2f\n Overall entropy: %.2f" % \
57- (_human_readable_size (d ['size' ], 1 ), d ['ep' ][1 ], d ['ep' ][2 ], mean (d ['entropy' ]) * 8 ,
59+ (human_readable_size (d ['size' ], 1 ), d ['ep' ][1 ], d ['ep' ][2 ], mean (d ['entropy' ]) * 8 ,
5860 d ['entropy*' ]),
5961}
6062
6163
62- def _ensure_str (s , encoding = 'utf-8' , errors = 'strict' ):
63- if isinstance (s , bytes ):
64- try :
65- return s .decode (encoding , errors )
66- except :
67- return s .decode ("latin-1" )
68- elif not isinstance (s , (str , bytes )):
69- raise TypeError ("not expecting type '%s'" % type (s ))
70- return s
71-
72-
73- def _human_readable_size (size , precision = 0 ):
74- i , units = 0 , ["B" , "KB" , "MB" , "GB" , "TB" , "PB" , "EB" , "ZB" , "YB" ]
75- while size >= 1024 and i < len (units )- 1 :
76- i += 1
77- size /= 1024.0
78- return "%.*f%s" % (precision , size , units [i ])
79-
80-
8164class Binary :
8265 def __init__ (self , path , ** kwargs ):
8366 from lief import logging , parse
@@ -132,7 +115,7 @@ def __sections_data(self):
132115 h_len = b .header .header_size + b .header .program_header_size * b .header .numberof_segments
133116 elif self .type == "MachO" :
134117 h_len = [28 , 32 ][str (b .header .magic )[- 3 :] == "_64" ] + b .header .sizeof_cmds
135- yield 0 , f"[0] Header ({ _human_readable_size (h_len )} )" , 0 , h_len , "black"
118+ yield 0 , f"[0] Header ({ human_readable_size (h_len )} )" , 0 , h_len , "black"
136119 # then handle binary's sections
137120 color_cursor , i = 0 , 1
138121 for section in sorted (b .sections , key = lambda s : s .offset ):
@@ -145,30 +128,30 @@ def __sections_data(self):
145128 c = co [color_cursor % len (co )]
146129 color_cursor += 1
147130 start , end = section .offset , section .offset + section .size
148- yield i , f"[{ i } ] { self .section_names [section .name ]} ({ _human_readable_size (end - start )} )" , start , end , c
131+ yield i , f"[{ i } ] { self .section_names [section .name ]} ({ human_readable_size (end - start )} )" , start , end , c
149132 i += 1
150133 # sections header at the end for ELF files
151134 if self .type == "ELF" :
152135 start , end = end , end + b .header .section_header_size * b .header .numberof_sections
153- yield i , f"[{ i } ] Section Header ({ _human_readable_size (end - start )} )" , start , end , "black"
136+ yield i , f"[{ i } ] Section Header ({ human_readable_size (end - start )} )" , start , end , "black"
154137 i += 1
155138 # finally, handle the overlay
156139 start , end = self .size - b .overlay .nbytes , self .size
157- yield i , f"[{ i } ] Overlay ({ _human_readable_size (end - start )} )" , start , self .size , "lightgray"
140+ yield i , f"[{ i } ] Overlay ({ human_readable_size (end - start )} )" , start , self .size , "lightgray"
158141 i += 1
159- yield i , f"TOTAL: { _human_readable_size (self .size )} " , None , None , "white"
142+ yield i , f"TOTAL: { human_readable_size (self .size )} " , None , None , "white"
160143
161144 def __segments_data (self ):
162145 b = self .__binary
163146 if self .type == "PE" :
164147 return # segments only apply to ELF and MachO
165148 elif self .type == "ELF" :
166149 for i , s in enumerate (sorted (b .segments , key = lambda x : (x .file_offset , x .physical_size ))):
167- yield i , f"[{ i } ] { str (s .type ).split ('.' )[1 ]} ({ _human_readable_size (s .physical_size )} )" , \
150+ yield i , f"[{ i } ] { str (s .type ).split ('.' )[1 ]} ({ human_readable_size (s .physical_size )} )" , \
168151 s .file_offset , s .file_offset + s .physical_size , "lightgray"
169152 elif self .type == "MachO" :
170153 for i , s in enumerate (sorted (b .segments , key = lambda x : (x .file_offset , x .file_size ))):
171- yield i , f"[{ i } ] { s .name } ({ _human_readable_size (s .file_size )} )" , \
154+ yield i , f"[{ i } ] { s .name } ({ human_readable_size (s .file_size )} )" , \
172155 s .file_offset , s .file_offset + s .file_size , "lightgray"
173156
174157 def _data (self , segments = False , overlap = False ):
@@ -255,7 +238,7 @@ def rawbytes(self):
255238
256239 @cached_property
257240 def section_names (self ):
258- names = {s .name : _ensure_str (s .name ).strip ("\x00 " ) or "<empty>" for s in self .__binary .sections }
241+ names = {s .name : ensure_str (s .name ).strip ("\x00 " ) or "<empty>" for s in self .__binary .sections }
259242 # names from string table only applies to PE
260243 if self .type != "PE" :
261244 return names
0 commit comments