Skip to content

Commit 63ec844

Browse files
committed
fix #8; save auto-analysis, IOCTLs, dumb IOCTLs, pooltags
1 parent 5017dfb commit 63ec844

File tree

7 files changed

+188
-64
lines changed

7 files changed

+188
-64
lines changed

DriverBuddyReloaded.py

Lines changed: 68 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
DriverBuddyReloaded.py: Entry point for IDA python plugin used in Windows driver vulnerability research.
1313
Updated in 2021 by Paolo Stagno aka VoidSec: https://voidsec.com - https://twitter.com/Void_Sec
1414
"""
15+
# needed GLOBALs
16+
driver_name = idaapi.get_root_filename()
17+
path = "{}{}".format(os.getcwd(), os.sep)
18+
ioctl_file_name = "{}-{}-{}-IOCTLs.txt".format(driver_name, utils.today(), utils.timestamp())
19+
analysis_file_name = "{}-{}-{}-DriverBuddyReloaded_autoanalysis.txt".format(driver_name, utils.today(),
20+
utils.timestamp())
21+
pool_file_name = "{}-{}-{}-pooltags.txt".format(driver_name, utils.today(), utils.timestamp())
1522

1623

1724
class UiAction(idaapi.action_handler_t):
@@ -147,8 +154,6 @@ def print_table(self, ioctls):
147154
:param ioctls: IOCTL to decode
148155
:return:
149156
"""
150-
driver_name = idaapi.get_root_filename()
151-
ioctl_file_name = "{}-{}-IOCTLs.txt".format(driver_name, utils.today())
152157
try:
153158
with open(ioctl_file_name, "w") as IOCTL_file:
154159
print("\nDriver Buddy Reloaded - IOCTLs\n"
@@ -169,9 +174,10 @@ def print_table(self, ioctls):
169174
access_code)
170175
print("0x%-8X | 0x%-8X | %-31s 0x%-8X | 0x%-8X | %-17s %-4d | %s (%d)" % all_vars)
171176
IOCTL_file.write("0x%-8X | 0x%-8X | %-31s 0x%-8X | 0x%-8X | %-17s %-4d | %s (%d)\n" % all_vars)
172-
print("\n[>] Saved decoded IOCTLs to \"{}{}{}\"".format(os.getcwd(), os.sep, ioctl_file_name))
177+
print("\n[>] Saved decoded IOCTLs to \"{}{}\"".format(path, ioctl_file_name))
173178
except IOError as e:
174-
print("ERROR #{}: Can't write to {}; {}".format(e.errno, ioctl_file_name, e.strerror))
179+
print("ERROR #{}: {}\nCan't save decoded IOCTLs to \"{}{}\"".format(e.errno, e.strerror, path,
180+
ioctl_file_name))
175181
print("\nDriver Buddy Reloaded - IOCTLs\n"
176182
"-----------------------------------------------")
177183
print("%-10s | %-10s | %-42s | %-10s | %-22s | %s" % (
@@ -195,7 +201,7 @@ def find_all_ioctls():
195201
"""
196202

197203
ioctls = []
198-
# Find the currently selected function and get a list of all of it's basic blocks
204+
# Find the currently selected function and get a list of all of its basic blocks
199205
addr = idc.get_screen_ea()
200206
f = idaapi.get_func(addr)
201207
fc = idaapi.FlowChart(f, flags=idaapi.FC_PREDS)
@@ -422,33 +428,64 @@ def run(self, args):
422428
:param args:
423429
:return:
424430
"""
425-
print("\nDriver Buddy Reloaded Auto-analysis\n"
426-
"-----------------------------------------------")
427-
idc.auto_wait() # Wait for IDA analysis to complete
428-
file_type = idaapi.get_file_type_name()
429-
if "portable executable" not in file_type.lower():
430-
print("[!] ERR: Loaded file is not a valid PE")
431-
else:
432-
driver_entry_addr = utils.is_driver()
433-
if driver_entry_addr is False:
434-
print("[!] ERR: Loaded file is not a Driver")
435-
else:
436-
print("[+] `DriverEntry` found at: 0x{addr:08x}".format(addr=driver_entry_addr))
437-
print("[>] Searching for `DeviceNames`...")
438-
device_name_finder.search()
439-
print("[>] Searching for `Pooltags`... ")
440-
pool = dump_pool_tags.get_all_pooltags()
441-
if pool:
442-
print(pool)
443-
if utils.populate_data_structures() is True:
444-
driver_type = utils.get_driver_id(driver_entry_addr)
445-
print(("[+] Driver type detected: {}".format(driver_type)))
446-
if ioctl_decoder.find_ioctls_dumb() is False:
447-
print("[!] Unable to automatically find any IOCTLs")
431+
try:
432+
with open(analysis_file_name, "w") as log_file:
433+
print("\nDriver Buddy Reloaded Auto-analysis\n"
434+
"-----------------------------------------------")
435+
log_file.write("\nDriver Buddy Reloaded Auto-analysis\n"
436+
"-----------------------------------------------\n")
437+
idc.auto_wait() # Wait for IDA analysis to complete
438+
file_type = idaapi.get_file_type_name()
439+
if "portable executable" not in file_type.lower():
440+
print("[!] ERR: Loaded file is not a valid PE")
441+
log_file.write("[!] ERR: Loaded file is not a valid PE\n")
448442
else:
449-
print("[!] ERR: Unable to enumerate functions")
450-
print("[+] Analysis Completed!\n"
451-
"-----------------------------------------------")
443+
driver_entry_addr = utils.is_driver()
444+
if driver_entry_addr is False:
445+
print("[!] ERR: Loaded file is not a Driver")
446+
log_file.write("[!] ERR: Loaded file is not a Driver\n")
447+
else:
448+
print("[+] `DriverEntry` found at: 0x{addr:08x}".format(addr=driver_entry_addr))
449+
log_file.write("[+] `DriverEntry` found at: 0x{addr:08x}\n".format(addr=driver_entry_addr))
450+
print("[>] Searching for `DeviceNames`...")
451+
log_file.write("[>] Searching for `DeviceNames`...\n")
452+
device_name_finder.search(log_file)
453+
print("[>] Searching for `Pooltags`...")
454+
log_file.write("[>] Searching for `Pooltags`...\n")
455+
pool = dump_pool_tags.get_all_pooltags()
456+
if pool:
457+
print(pool)
458+
try:
459+
with open(pool_file_name, "w") as pool_file:
460+
pool_file.write(pool)
461+
except IOError as e:
462+
print(
463+
"ERROR #{}: {}\nCan't write pool file to \"{}{}\"".format(e.errno, e.strerror, path,
464+
pool_file_name))
465+
if utils.populate_data_structures(log_file) is True:
466+
driver_type = utils.get_driver_id(driver_entry_addr, log_file)
467+
print("[+] Driver type detected: {}".format(driver_type))
468+
log_file.write("[+] Driver type detected: {}\n".format(driver_type))
469+
if ioctl_decoder.find_ioctls_dumb(log_file, ioctl_file_name) is False:
470+
print("[!] Unable to automatically find any IOCTLs")
471+
log_file.write("[!] Unable to automatically find any IOCTLs\n")
472+
else:
473+
print("\n[>] Saved decoded IOCTLs log file to \"{}{}_dumb.txt\"".format(path,
474+
ioctl_file_name))
475+
else:
476+
print("[!] ERR: Unable to enumerate functions")
477+
log_file.write("[!] ERR: Unable to enumerate functions\n")
478+
print("[+] Analysis Completed!\n"
479+
"-----------------------------------------------")
480+
log_file.write("[+] Analysis Completed!\n"
481+
"-----------------------------------------------")
482+
print("\n[>] Saved Autoanalysis log file to \"{}{}\"".format(path, analysis_file_name))
483+
if pool:
484+
print("[>] Saved Pooltags file to \"{}{}\"".format(path, pool_file_name))
485+
except IOError as e:
486+
print("ERROR #{}: {}\nAutoanalysis aborted, can't write log file to \"{}{}\"".format(e.errno, e.strerror,
487+
path,
488+
analysis_file_name))
452489
return
453490

454491
def term(self):

DriverBuddyReloaded/device_name_finder.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import ida_nalt
99

10-
ASCII_BYTE = b" !\"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}\\\~\t"
10+
ASCII_BYTE = b" !\"#\\$%&\'\\(\\)\\*\\+,-\\./0123456789:;<=>\\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\[\\]\\^_`abcdefghijklmnopqrstuvwxyz\\{\\|\\}\\\\~\t"
1111
UNICODE_RE_4 = re.compile(b"((?:[%s]\x00){%d,})" % (ASCII_BYTE, 4))
1212
REPEATS = ["A", "\x00", "\xfe", "\xff"]
1313
SLICE_SIZE = 4096
@@ -73,9 +73,10 @@ def get_unicode_device_names():
7373
return possible_names
7474

7575

76-
def find_unicode_device_name():
76+
def find_unicode_device_name(log_file):
7777
"""
7878
Attempts to find and output potential DeviceNames - returning False if none are found so further analysis can be done
79+
:param log_file: log file handler
7980
"""
8081

8182
possible_names = get_unicode_device_names()
@@ -84,39 +85,51 @@ def find_unicode_device_name():
8485
if len(possible_names) == 1:
8586
print(
8687
"[!] The Device prefix was found but no full Device Paths; the DeviceName is likely obfuscated or created on the stack.")
88+
log_file.write(
89+
"[!] The Device prefix was found but no full Device Paths; the DeviceName is likely obfuscated or created on the stack.\n")
8790
return False
8891
elif '\\Device\\' in possible_names and '\\DosDevices\\' in possible_names:
8992
print(
9093
"[!] The Device prefix was found but no full Device Paths; the DeviceName is likely obfuscated or created on the stack.")
94+
log_file.write(
95+
"[!] The Device prefix was found but no full Device Paths; the DeviceName is likely obfuscated or created on the stack.\n")
9196
return False
9297
else:
9398
# print("Potential DeviceName: ")
9499
for i in possible_names:
95100
if i != '\\Device\\' and i != '\\DosDevices\\':
96101
print("\t- {}".format(i))
102+
log_file.write("\t- {}\n".format(i))
97103
return True
98104
else:
99105
# print("Potential DeviceNames: ")
100106
for i in possible_names:
101107
print("\t- {}".format(i))
108+
log_file.write("\t- {}\n".format(i))
102109
return True
103110
elif len(possible_names) > 2:
104111
# print("Possible devices names found:")
105112
for i in possible_names:
106113
print("\t- {}".format(i))
114+
log_file.write("\t- {}\n".format(i))
107115
return True
108116
else:
109117
print("[!] No potential DeviceNames found; it may be obfuscated or created on the stack in some way.")
118+
log_file.write(
119+
"[!] No potential DeviceNames found; it may be obfuscated or created on the stack in some way.\n")
110120
return False
111121

112122

113-
def search():
123+
def search(log_file):
114124
"""
115125
Attempts to find potential DeviceNames in the currently opened binary.
116126
It starts by searching for Unicode DeviceNames, if this fails then it suggests the analyst to use FLOSS
117127
in order to search for stack based and obfuscated strings.
128+
:param log_file: log file handler
118129
"""
119130

120-
if not find_unicode_device_name():
131+
if not find_unicode_device_name(log_file):
121132
print(
122133
"[!] Unicode DeviceName not found; try using FLOSS in order to recover obfuscated and stack based strings.")
134+
log_file.write(
135+
"[!] Unicode DeviceName not found; try using FLOSS in order to recover obfuscated and stack based strings.\n")

DriverBuddyReloaded/find_opcodes.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def FindInstructions(instr, asm_where=None):
5353
seg = ida_segment.get_first_seg()
5454
asm_where = seg.start_ea if seg else ida_idaapi.BADADDR
5555
if asm_where == ida_idaapi.BADADDR:
56-
return (False, "No segments defined")
56+
return False, "No segments defined"
5757

5858
# regular expression to distinguish between opcodes and instructions
5959
re_opcode = re.compile('^[0-9a-f]{2} *', re.I)
@@ -71,7 +71,7 @@ def FindInstructions(instr, asm_where=None):
7171
# assemble the instruction
7272
ret, buf = idautils.Assemble(asm_where, line)
7373
if not ret:
74-
return (False, "Failed to assemble:" + line)
74+
return False, "Failed to assemble: {}".format(line)
7575
# add the assembled buffer
7676
bufs.append(buf)
7777

@@ -96,9 +96,9 @@ def FindInstructions(instr, asm_where=None):
9696
# ida_kernwin.msg(".")
9797
ea += tlen
9898
if not ret:
99-
return (False, "Could not match {} - [{}]".format(instr, bin_str))
99+
return False, "Could not match {} - [{}]".format(instr, bin_str)
100100
# ida_kernwin.msg("\n")
101-
return (True, ret)
101+
return True, ret
102102

103103

104104
# Chooser class
@@ -129,7 +129,7 @@ def OnSelectLine(self, n):
129129

130130
# class to represent the results
131131
class SearchResult:
132-
def __init__(self, ea):
132+
def __init__(self, ea, log_file):
133133
self.ea = ea
134134
self.funcname_or_segname = ""
135135
self.text = ""
@@ -151,13 +151,18 @@ def __init__(self, ea):
151151
if opcode in self.text:
152152
print(
153153
"\t- Found {} in {} at 0x{addr:08x}".format(self.text, self.funcname_or_segname, addr=self.ea))
154+
log_file.write("\t- Found {} in {} at 0x{addr:08x}\n".format(self.text, self.funcname_or_segname,
155+
addr=self.ea))
154156
else:
155157
print("\t- Found {} in {} at 0x{addr:08x}".format(self.text, self.funcname_or_segname, addr=self.ea))
158+
log_file.write(
159+
"\t- Found {} in {} at 0x{addr:08x}\n".format(self.text, self.funcname_or_segname, addr=self.ea))
156160

157161

158-
def find(s=None, x=False, asm_where=None):
162+
def find(log_file, s=None, x=False, asm_where=None):
159163
"""
160164
Search for opcode/instruction
165+
:param log_file: log file handler
161166
:param s: opcode/instruction
162167
:param x: if true search for executable code segments only
163168
:param asm_where: where to start searching
@@ -173,9 +178,9 @@ def find(s=None, x=False, asm_where=None):
173178
seg = ida_segment.getseg(ea)
174179
if (not seg) or (seg.perm & ida_segment.SEGPERM_EXEC) == 0:
175180
continue
176-
results.append(SearchResult(ea))
181+
results.append(SearchResult(ea, log_file))
177182
else:
178-
results = [SearchResult(ea) for ea in ret]
183+
results = [SearchResult(ea, log_file) for ea in ret]
179184
"""title = "Search result for: [%s]" % s
180185
ida_kernwin.close_chooser(title)
181186
c = SearchResultChoose(title, results)

DriverBuddyReloaded/ioctl_decoder.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,15 +163,19 @@ def get_define(ioctl_code):
163163
return "#define %s CTL_CODE(0x%X, 0x%X, %s, %s)" % (name, device_code, function, method_name, access_name)
164164

165165

166-
def find_ioctls_dumb():
166+
def find_ioctls_dumb(log_file, ioctl_file_name):
167167
"""
168168
Attempts to locate any IOCTLs in driver automatically.
169169
:return boolean: True if any IOCTLs found, False otherwise
170+
:param log_file: log file handler
171+
:param ioctl_file_name: IOCTL log file name
170172
"""
173+
ioctl_file_name = ioctl_file_name + "_dumb.txt"
171174
result = False
172175
cur = idc.ida_ida.inf_get_min_ea()
173176
max = idc.ida_ida.inf_get_max_ea()
174177
print("[>] Searching for IOCTLs found by IDA...")
178+
log_file.write("[>] Searching for IOCTLs found by IDA...\n")
175179
while cur < max:
176180
# idc.find_text(ea, flag, y, x, searchstr, from_bc695=False)
177181
# cur = find_text(cur, SEARCH_DOWN, 0, 0, "IoControlCode")
@@ -182,11 +186,41 @@ def find_ioctls_dumb():
182186
else:
183187
if idc.get_operand_type(cur, 0) == 5:
184188
idc.op_dec(cur, 0)
185-
get_ioctl_code(int(idc.print_operand(cur, 0)))
189+
ioctl_code = int(idc.print_operand(cur, 0))
190+
function = get_function(ioctl_code)
191+
device_name, device_code = get_ioctl_code(ioctl_code)
192+
method_name, method_code = get_method(ioctl_code)
193+
access_name, access_code = get_access(ioctl_code)
194+
all_vars = (
195+
ioctl_code, device_name, device_code, function, method_name, method_code, access_name,
196+
access_code)
197+
try:
198+
with open(ioctl_file_name, "w") as IOCTL_file:
199+
IOCTL_file.write("0x%-8X | %-31s 0x%-8X | 0x%-8X | %-17s %-4d | %s (%d)\n" % all_vars)
200+
except IOError as e:
201+
print("ERROR #{}: {}\nCan't save decoded IOCTLs to \"{}\"".format(e.errno, e.strerror,
202+
ioctl_file_name))
203+
print("0x%-8X | %-31s 0x%-8X | 0x%-8X | %-17s %-4d | %s (%d)" % all_vars)
204+
log_file.write("0x%-8X | %-31s 0x%-8X | 0x%-8X | %-17s %-4d | %s (%d)\n" % all_vars)
186205
result = True
187206
elif idc.get_operand_type(cur, 1) == 5:
188207
idc.op_dec(cur, 1)
189-
get_ioctl_code(int(idc.print_operand(cur, 1)))
208+
ioctl_code = int(idc.print_operand(cur, 1))
209+
function = get_function(ioctl_code)
210+
device_name, device_code = get_ioctl_code(ioctl_code)
211+
method_name, method_code = get_method(ioctl_code)
212+
access_name, access_code = get_access(ioctl_code)
213+
all_vars = (
214+
ioctl_code, device_name, device_code, function, method_name, method_code, access_name,
215+
access_code)
216+
try:
217+
with open(ioctl_file_name, "w") as IOCTL_file:
218+
IOCTL_file.write("0x%-8X | %-31s 0x%-8X | 0x%-8X | %-17s %-4d | %s (%d)\n" % all_vars)
219+
except IOError as e:
220+
print("ERROR #{}: {}\nCan't save decoded IOCTLs to \"{}\"".format(e.errno, e.strerror,
221+
ioctl_file_name))
222+
print("0x%-8X | %-31s 0x%-8X | 0x%-8X | %-17s %-4d | %s (%d)" % all_vars)
223+
log_file.write("0x%-8X | %-31s 0x%-8X | 0x%-8X | %-17s %-4d | %s (%d)\n" % all_vars)
190224
result = True
191225
# else:
192226
# print("[!] Cannot get IOCTL from {} at {} ".format(idc.GetDisasm(cur), hex(cur)))

0 commit comments

Comments
 (0)