[文档]classPackingDetector(Analysis):""" This analysis detects if a binary is likely packed or not. We may extend it to identify which packer is in use in the future. """PACKED_MIN_BYTES=256PACKED_ENTROPY_MIN_THRESHOLD=0.88
[文档]def__init__(self,cfg:CFGModel|None=None,region_size_threshold:int=0x20):self.packed:bool=Falseself.region_size_threshold:int=region_size_thresholdifcfgisNone:_l.warning("PackingDetector is using a most accurate CFG model in the knowledge base. We assume it is ""generated with force_smart_scan=False and force_complete_scan=False.")self._cfg=self.kb.cfgs.get_most_accurate()else:self._cfg=cfgself.analyze()
[文档]defanalyze(self):# assume we already have a CFG with complete scanning disabled# collect all regions that are not covered by the CFG in r+x sections, and then compute the entropy. we believe# the binary is packed if it is beyond a thresholdcovered_regions:list[tuple[int,int]]=[]last_known_section:Section|None=Nonefornodeinsorted(self._cfg.nodes(),key=lambdan:n.addr):section=Noneiflast_known_sectionisnotNoneandlast_known_section.contains_addr(node.addr):section=last_known_sectionifsectionisNone:section=self.project.loader.find_section_containing(node.addr)ifsectionisNone:# this node does not belong to any known section - ignore itcontinueifsection.is_readableandsection.is_executable:last_known_section=sectionifsectionisNone:# the node does not belong to any section. ignore itcontinueifnode.size==0:# ignore empty nodescontinueifnotcovered_regions:covered_regions.append((node.addr,node.addr+node.size))else:last_item=covered_regions[-1]iflast_item[0]<=node.addr<=last_item[1]<node.addr+node.size:# update the last itemcovered_regions[-1]=last_item[0],node.addr+node.sizeelse:# add a new itemcovered_regions.append((node.addr,node.addr+node.size))# now we get the uncovered regionsuncovered_regions:list[tuple[int,int]]=self._get_uncovered_regions(covered_regions)# compute entropytotal_bytes,entropy=self._compute_entropy(uncovered_regions)self.packed=total_bytes>=self.PACKED_MIN_BYTESandentropy>=self.PACKED_ENTROPY_MIN_THRESHOLD
def_get_uncovered_regions(self,covered_regions:list[tuple[int,int]])->list[tuple[int,int]]:# FIXME: We only support binaries with sections. Add support for segments in the futureall_executable_sections=[secforsecinself.project.loader.main_object.sectionsifsec.is_executableandsec.is_readableandnotsec.only_contains_uninitialized_data]all_executable_sections=sorted(all_executable_sections,key=lambdasec:sec.vaddr)idx=0uncovered_regions:list[tuple[int,int]]=[]forsectioninall_executable_sections:ifidx>=len(covered_regions):ifsection.memsize>self.region_size_threshold:uncovered_regions.append((section.vaddr,section.vaddr+section.memsize))else:i=idxlast_end=section.vaddrwhilei<len(covered_regions):region_start,region_end=covered_regions[i]ifregion_end>=section.vaddr+section.memsize:# move on to the next sectionbreakiflast_end<region_startandregion_start-last_end>self.region_size_threshold:uncovered_regions.append((last_end,region_start))i+=1last_end=max(last_end,region_end)idx=ireturnuncovered_regionsdef_compute_entropy(self,regions:list[tuple[int,int]])->tuple[int,float]:byte_counts=[0]*256forstart,endinregions:forbinself.project.loader.memory.load(start,end-start):byte_counts[b]+=1total=sum(byte_counts)iftotal==0:return0,0.0entropy=0.0forcountinbyte_counts:ifcount==0:continuep=1.0*count/totalentropy-=p*math.log(p,256)returntotal,entropy