-
Notifications
You must be signed in to change notification settings - Fork 17
/
find_device_name.py
118 lines (102 loc) · 3.84 KB
/
find_device_name.py
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
112
113
114
115
116
117
118
import idautils
import idc
import mmap
import re
import sys
from collections import namedtuple
#
ASCII_BYTE = " !\"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}\\\~\t"
UNICODE_RE_4 = re.compile(b"((?:[%s]\x00){%d,})" % (ASCII_BYTE, 4))
REPEATS = ["A", "\x00", "\xfe", "\xff"]
SLICE_SIZE = 4096
String = namedtuple("String", ["s", "offset"])
def buf_filled_with(buf, character):
dupe_chunk = character * SLICE_SIZE
for offset in xrange(0, len(buf), SLICE_SIZE):
new_chunk = buf[offset: offset + SLICE_SIZE]
if dupe_chunk[:len(new_chunk)] != new_chunk:
return False
return True
def extract_unicode_strings(buf, n=4):
'''
Extract naive UTF-16 strings from the given binary data.
:param buf: A bytestring.
:type buf: str
:param n: The minimum length of strings to extract.
:type n: int
:rtype: Sequence[String]
'''
if not buf:
return
if (buf[0] in REPEATS) and buf_filled_with(buf, buf[0]):
return
if n == 4:
r = UNICODE_RE_4
else:
reg = b"((?:[%s]\x00){%d,})" % (ASCII_BYTE, n)
r = re.compile(reg)
for match in r.finditer(buf):
try:
yield String(match.group().decode("utf-16"), match.start())
except UnicodeDecodeError:
pass
def get_unicode_device_names():
path = idc.GetInputFile()
min_length = 4
possible_names = set()
with open(path, "rb") as f:
b = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
for s in extract_unicode_strings(b, n=min_length):
if str(s.s).startswith('\\Device\\'):
possible_names.add(str(s.s))
return possible_names
def find_unicode_device_name():
possible_names = get_unicode_device_names()
if len(possible_names) == 1:
if possible_names.pop() == '\\Device\\':
print "The Device prefix was found but no full device paths, the device name is likely obsfucated or created on the stack."
return False
elif len(possible_names) > 1:
print "Possible devices names found:"
for i in possible_names:
print "\t" + i
return True
else:
print "No potential device names found - it may be obsfucated or created on the stack in some way."
return False
if not find_unicode_device_name():
print "Device name not found, attempting to find obsfucated and stack based strings."
try:
import floss
import floss.identification_manager
import floss.main
import floss.stackstrings
import viv_utils
except:
print "Please install FLOSS to continue, see: https://github.com/fireeye/flare-floss/"
sample_file_path = idc.GetInputFile()
try:
vw = viv_utils.getWorkspace(sample_file_path, should_save=False)
except Exception, e:
print("Vivisect failed to load the input file: {0}".format(e.message))
sys.exit(1)
functions = set(vw.getFunctions())
plugins = floss.main.get_all_plugins()
device_names = set()
stack_strings = floss.stackstrings.extract_stackstrings(vw, functions)
for i in stack_strings:
device_names.add(i)
decoding_functions_candidates = floss.identification_manager.identify_decoding_functions(vw, plugins, functions)
function_index = viv_utils.InstructionFunctionIndex(vw)
decoded_strings = floss.main.decode_strings(vw, function_index, decoding_functions_candidates)
if len(decoded_strings) > 0:
for i in decoded_strings:
device_names.add(str(i.s))
print "Potential devices names from obsfucated or stack strings:"
for i in device_names:
if i.startswith('\\Device\\'):
print i
else:
print '\\Device\\' + i
else:
print "No obsfucated or stack strings found :("