1
1
# -*- coding: UTF-8 -*-
2
2
import json
3
3
import os
4
- import pefile
5
4
import peid
6
5
import re
7
- from functools import cached_property
6
+ from functools import lru_cache
7
+ from pefile import PE
8
8
9
9
10
10
__all__ = ["PyPackerDetect" , "PACKERS" , "SECTIONS" ]
16
16
SECTIONS = json .load (f )['sections' ]
17
17
18
18
19
- # dirty fix for section names requiring to get their real names from the string table (as pefile does not seem to handle
20
- # this in every case)
21
- class PE (pefile .PE ):
22
- @cached_property
23
- def real_section_names (self ):
24
- from subprocess import check_output
25
- try :
26
- names , out = [], check_output (["objdump" , "-h" , self .path ]).decode ()
27
- except FileNotFoundError :
28
- return
29
- for l in out .split ("\n " ):
30
- m = re .match (r"\s+\d+\s(.*?)\s+" , l )
31
- if m :
32
- names .append (m .group (1 ))
33
- return names
19
+ @lru_cache
20
+ def _real_section_names (path , logger = None , timeout = 10 ):
21
+ from subprocess import Popen , PIPE , TimeoutExpired
22
+ p , names = Popen (["objdump" , "-h" , path ], stdout = PIPE , stderr = PIPE ), []
23
+ try :
24
+ out , err = p .communicate (timeout = timeout )
25
+ except TimeoutExpired :
26
+ if logger :
27
+ logger .debug (f"objdump: timeout ({ timeout } seconds)" )
28
+ return
29
+ if err :
30
+ if logger :
31
+ for l in err .decode ("latin-1" ).strip ().split ("\n " ):
32
+ logger .debug (l )
33
+ return
34
+ for l in out .decode ("latin-1" ).strip ().split ("\n " ):
35
+ m = re .match (r"\s+\d+\s(.*?)\s+" , l )
36
+ if m :
37
+ names .append (m .group (1 ))
38
+ return names
34
39
35
40
36
41
class PyPackerDetect :
@@ -56,7 +61,7 @@ def __add(i, msg):
56
61
if self .logger :
57
62
self .logger .debug ("%s:%s" % (['DETECTION' , 'SUSPICION' ][i ], msg ))
58
63
r [['detections' , 'suspicions' ][i ]].append (msg )
59
- if not isinstance (pe , pefile . PE ):
64
+ if not isinstance (pe , PE ):
60
65
path = pe
61
66
pe = PE (path )
62
67
pe .path = path
@@ -70,8 +75,8 @@ def __add(i, msg):
70
75
# the format "/[N]" where N is the offset in the string table ; in this case, we compute
71
76
# the real section names and map them to the shortened ones to compare the relevant names
72
77
# with the database of known section names
73
- if re .match (r"^\/\d+$" , n ) and pe .real_section_names :
74
- n = pe . real_section_names [i ]
78
+ if re .match (r"^\/\d+$" , n ) and _real_section_names ( pe .path , logger = self . logger ) :
79
+ n = _real_section_names [i ]
75
80
d ['all' ].append (n )
76
81
if ep >= s .VirtualAddress and ep <= (s .VirtualAddress + s .Misc_VirtualSize ):
77
82
d ['ep' ].append (n )
@@ -147,7 +152,7 @@ def __add(i, msg):
147
152
@staticmethod
148
153
def report (pe , findings ):
149
154
""" Report findings like the original project. """
150
- print ("Packer report for: %s" % (getattr (pe , "path" , "unknown path" ) if isinstance (pe , pefile . PE ) else pe ))
155
+ print ("Packer report for: %s" % (getattr (pe , "path" , "unknown path" ) if isinstance (pe , PE ) else pe ))
151
156
print ("\t Detections: %d" % len (findings ['detections' ]))
152
157
print ("\t Suspicions: %d" % len (findings ['suspicions' ]))
153
158
if len (findings ['detections' ]) > 0 or len (findings ['suspicions' ]) > 0 :
0 commit comments