mirror of
https://github.com/volatilityfoundation/volatility
synced 2026-06-08 18:04:46 +00:00
521 lines
24 KiB
Python
521 lines
24 KiB
Python
# Volatility
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or (at
|
|
# your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
"""
|
|
@author: Cem Gurkok
|
|
@license: GNU General Public License 2.0 or later
|
|
@contact: cemgurkok@gmail.com
|
|
@organization:
|
|
"""
|
|
|
|
import volatility.obj as obj
|
|
import common
|
|
import volatility.commands as commands
|
|
import distorm3
|
|
import volatility.plugins.mac.check_sysctl as check_sysctl
|
|
import volatility.plugins.mac.check_trap_table as check_trap_table
|
|
|
|
class mac_apihooks_kernel(common.AbstractMacCommand):
|
|
""" Checks to see if system call and kernel functions are hooked """
|
|
|
|
def __init__(self, config, *args, **kwargs):
|
|
self.addr_space = None
|
|
commands.Command.__init__(self, config, *args, **kwargs)
|
|
|
|
self._config.add_option("CHECKKEXTS", short_option = 'X', default = False,
|
|
cache_invalidator = False, help = "Check all kext functions in the kext's symbol table for hooking, including kernel symbol table", action = "store_true")
|
|
self._config.add_option("CHECKKERNEL", short_option = 'K', default = False,
|
|
cache_invalidator = False, help = "Check only kernel symbol table functions for hooking", action = "store_true")
|
|
|
|
def getKextSymbols(self, kext_obj = None, kext_name = None, kext_addr = 0, onlyFunctions = False, fmodel = '64bit'):
|
|
# get symbol table based on https://github.com/gdbinit/hydra/blob/master/hydra/hydra/kernel_info.c (works)
|
|
# and https://github.com/snarez/KernelResolver/blob/master/KernelResolver/KernelResolver.c (http://ho.ax/tag/kexts/, almost works)
|
|
# return only functions if requested, this is done by checking if symbol entry points to the __TEXT segment's __text section, which contains executable code
|
|
|
|
mach_header_struct = 'macho64_header'
|
|
segment_command_struct = 'macho64_segment_command'
|
|
section_struct = 'macho64_section'
|
|
nlist_struct = 'macho64_nlist'
|
|
LC_SEGMENT = 0x19 # x64
|
|
|
|
if fmodel == '32bit':
|
|
mach_header_struct = 'macho32_header'
|
|
segment_command_struct = 'macho32_segment_command'
|
|
section_struct = 'macho32_section'
|
|
nlist_struct = 'macho32_nlist'
|
|
LC_SEGMENT = 0x1
|
|
|
|
# if kext_name is given get kext_address based on name
|
|
if kext_name != None:
|
|
if kext_name in ["kernel", "__kernel__"]:
|
|
kext_addr = self.addr_space.profile.get_symbol("_g_kernel_kmod_info")
|
|
else:
|
|
# get list of kexts and loop thru them to find match
|
|
kmodaddr = obj.Object("Pointer", offset = self.addr_space.profile.get_symbol("_kmod"), vm = self.addr_space)
|
|
kmod = kmodaddr.dereference_as("kmod_info")
|
|
while kmod.is_valid():
|
|
if str(kmod.name) == kext_name:
|
|
kext_addr = kmod.address
|
|
break
|
|
kmod = kmod.next
|
|
if kext_addr == None:
|
|
yield
|
|
elif kext_obj != None:
|
|
kext_addr = kext_obj.adddress
|
|
|
|
# get Mach-O header to get start of segments
|
|
mh = obj.Object(mach_header_struct, offset = kext_addr, vm=self.addr_space)
|
|
seg_offset = mh.obj_offset + self.addr_space.profile.get_obj_size(mach_header_struct)
|
|
|
|
linkedit_vmaddr = 0 # the first struct nlist is here
|
|
symtab_symoff = 0 # specifies the offset in the file to the symbol table
|
|
symtab_stroff = 0 # specifies the offset in the file to the string table
|
|
symbol_cnt = 0
|
|
linkedit_fileoffset = 0
|
|
linkedit_size = 0
|
|
text_sect_num = 0
|
|
sect_cnt = 0
|
|
|
|
for i in xrange(0, mh.ncmds):
|
|
seg = obj.Object(segment_command_struct, offset = seg_offset, vm = self.addr_space)
|
|
if seg.cmd == 0x19 and seg.segname and str(seg.segname) == "__LINKEDIT":
|
|
linkedit_vmaddr = seg.vmaddr
|
|
linkedit_fileoffset = seg.fileoff
|
|
linkedit_size = seg.filesize
|
|
elif seg.cmd == 0x02: #SYMTAB
|
|
symtab = obj.Object('symtab_command', offset = seg_offset, vm = self.addr_space)
|
|
symtab_symoff = symtab.symoff
|
|
symtab_stroff = symtab.stroff
|
|
symbol_cnt = symtab.nsyms
|
|
|
|
# only looking at LC_SEGMENT for sections
|
|
if seg.cmd == LC_SEGMENT:
|
|
# loop thru segment's sections to locate __TEXT segment's __text section number, used to determine executable code
|
|
for j in xrange(0, seg.nsects):
|
|
sect_cnt += 1
|
|
sect = obj.Object(section_struct, offset = seg_offset + self.addr_space.profile.get_obj_size(segment_command_struct) + (self.addr_space.profile.get_obj_size(section_struct) * j), vm = self.addr_space)
|
|
sect_name = "".join(map(str, str(sect.sectname))).strip(' \t\r\n\0')
|
|
# find __TEXT segment's __text section since all executable code is here
|
|
if seg.cmd == 0x19 and seg.segname and str(seg.segname) == "__TEXT" and sect_name == "__text":
|
|
text_sect_num = sect_cnt
|
|
seg_offset += seg.cmdsize
|
|
|
|
symbol_offset = symtab_symoff - linkedit_fileoffset
|
|
string_offset = symtab_stroff- linkedit_fileoffset
|
|
|
|
# loop thru symbols within __LINKEDIT given offset
|
|
for i in range(0, symbol_cnt-1):
|
|
sym = obj.Object(nlist_struct, offset = linkedit_vmaddr + symbol_offset + (i * self.addr_space.profile.get_obj_size(nlist_struct)), vm = self.addr_space)
|
|
sym_addr = sym.n_strx + linkedit_vmaddr + string_offset
|
|
sym_name = obj.Object('String', offset = sym_addr, vm = self.addr_space, length = 256)
|
|
|
|
if sym_name.is_valid():
|
|
if onlyFunctions:
|
|
if sym.n_sect == text_sect_num:
|
|
yield (sym_name, sym.n_value)
|
|
else:
|
|
yield (sym_name, sym.n_value)
|
|
|
|
def findKextWithAddress(self, addr):
|
|
# function to find what kext the given address lies within
|
|
kexts = []
|
|
#get kernel kext
|
|
kp = self.addr_space.profile.get_symbol("_g_kernel_kmod_info")
|
|
kmodk = obj.Object("kmod_info", offset = kp, vm = self.addr_space)
|
|
kexts.append(kmodk)
|
|
|
|
# get other kexts
|
|
p = self.addr_space.profile.get_symbol("_kmod")
|
|
kmodaddr = obj.Object("Pointer", offset = p, vm = self.addr_space)
|
|
kmod = kmodaddr.dereference_as("kmod_info")
|
|
while kmod.is_valid():
|
|
kexts.append(kmod)
|
|
kmod = kmod.next
|
|
|
|
for kext in kexts:
|
|
if addr >= kext.address and addr <= (kext.address + kext.m('size')):
|
|
return kext.name
|
|
|
|
return "UNKNOWN"
|
|
|
|
def isCallReferenceModified(self, model, distorm_mode, func_addr, kernel_syms, kmods):
|
|
# check if CALL targets are within the kernel/kext range to detect possible call reference modification
|
|
|
|
modified = False
|
|
|
|
#modified malware/apihooks.py/check_inline function
|
|
data = self.addr_space.read(func_addr, 750)
|
|
|
|
# Number of instructions disassembled so far
|
|
n = 0
|
|
# Destination address of hooks
|
|
d = None
|
|
# Save the last PUSH before a CALL
|
|
push_val = None
|
|
# Save the general purpose registers
|
|
regs = {}
|
|
ops = []
|
|
|
|
for op in distorm3.Decompose(func_addr, data, distorm_mode):
|
|
ops.append(op)
|
|
|
|
for op in distorm3.Decompose(func_addr, data, distorm_mode):
|
|
# Quit when a decomposition error is encountered
|
|
# or when reach function end
|
|
if not op.valid or op.mnemonic == "NOP":
|
|
break
|
|
|
|
if op.flowControl == 'FC_CALL':
|
|
# Clear the push value
|
|
if push_val:
|
|
push_val = None
|
|
if op.mnemonic == "CALL" and op.operands[0].type == 'AbsoluteMemoryAddress':
|
|
# Check for CALL [ADDR]
|
|
if model == '32bit':
|
|
const = op.operands[0].disp & 0xFFFFFFFF
|
|
d = obj.Object("unsigned int", offset = const, vm = self.addr_space)
|
|
else:
|
|
const = op.operands[0].disp
|
|
d = obj.Object("unsigned long long", offset = const, vm = self.addr_space)
|
|
if self.outside_module(d, kernel_syms, kmods):
|
|
break
|
|
elif op.operands[0].type == 'Immediate':
|
|
# Check for CALL ADDR
|
|
d = op.operands[0].value
|
|
if self.outside_module(d, kernel_syms, kmods):
|
|
break
|
|
elif op.operands[0].type == 'Register':
|
|
# Check for CALL REG
|
|
d = regs.get(op.operands[0].name)
|
|
if d and self.outside_module(d, kernel_syms, kmods):
|
|
break
|
|
n += 1
|
|
|
|
# filtering out false positives due to structs, you can tweak this as needed
|
|
if d and self.outside_module(d, kernel_syms, kmods) == True and str(ops[n+1].mnemonic) not in ["DB 0xff", "ADD", "XCHG", "OUTS"]:
|
|
modified = True
|
|
|
|
return (modified, d)
|
|
|
|
def isPrologInlined(self, model, distorm_mode, func_addr):
|
|
##check if function prologs are modified
|
|
inlined = False
|
|
content = self.addr_space.read(func_addr, 24)
|
|
|
|
op_cnt = 1
|
|
for op in distorm3.Decompose(func_addr, content, distorm_mode):
|
|
if op_cnt == 2:
|
|
if model == "32bit":
|
|
if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
|
|
op.operands[1].type == "Register" and op.operands[0].name == "EBP" and op.operands[1].name == "ESP" and
|
|
prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "EBP"):
|
|
pass
|
|
else:
|
|
inlined = True
|
|
elif model == "64bit":
|
|
if (op.mnemonic == "MOV" and len(op.operands) == 2 and op.operands[0].type == "Register" and
|
|
op.operands[1].type == "Register" and op.operands[0].name == "RBP" and op.operands[1].name == "RSP" and
|
|
prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP"):
|
|
pass
|
|
elif (prev_op.mnemonic == "PUSH" and len(prev_op.operands) == 1 and prev_op.operands[0].type == "Register" and prev_op.operands[0].name == "RBP" and
|
|
op.mnemonic == "PUSH" and len(op.operands) == 1 and op.operands[0].type == "Register" and op.operands[0].name in ["RSP","RBX","R12","R13","R14","R15"]):
|
|
# Registers preserved across calls, http://people.freebsd.org/~lstewart/references/amd64.pdf
|
|
pass
|
|
else:
|
|
inlined = True
|
|
break
|
|
prev_op = op
|
|
op_cnt += 1
|
|
|
|
return inlined
|
|
|
|
# NOTES FROM ANDREW
|
|
# This function orignally checked for any call outside the kernel module
|
|
# This produces too many false positives so its modified to check if the call
|
|
# is to a known module or a kernel symbol
|
|
def outside_module(self, addr, kernel_syms, kmods):
|
|
(good, _) = common.is_known_address_name(addr, kernel_syms, kmods)
|
|
|
|
return not good
|
|
|
|
def isInlined(self, model, distorm_mode, func_addr, kernel_syms, kmods):
|
|
inlined = False
|
|
|
|
#modified malware/apihooks.py/check_inline function
|
|
data = self.addr_space.read(func_addr, 24)
|
|
|
|
# Number of instructions disassembled so far
|
|
n = 0
|
|
# Destination address of hooks
|
|
d = None
|
|
# Save the last PUSH before a CALL
|
|
push_val = None
|
|
# Save the general purpose registers
|
|
regs = {}
|
|
ops = []
|
|
|
|
for op in distorm3.Decompose(func_addr, data, distorm_mode):
|
|
ops.append(op)
|
|
|
|
for op in distorm3.Decompose(func_addr, data, distorm_mode):
|
|
# Quit the loop when we have three instructions or when
|
|
# a decomposition error is encountered, whichever is first.
|
|
if not op.valid or n == 3:
|
|
break
|
|
|
|
if op.flowControl == 'FC_CALL':
|
|
# Clear the push value
|
|
if push_val:
|
|
push_val = None
|
|
if op.mnemonic == "CALL" and op.operands[0].type == 'AbsoluteMemoryAddress':
|
|
# Check for CALL [ADDR]
|
|
if model == '32bit':
|
|
const = op.operands[0].disp & 0xFFFFFFFF
|
|
d = obj.Object("unsigned int", offset = const, vm = addr_space)
|
|
else:
|
|
const = op.operands[0].disp
|
|
d = obj.Object("unsigned long long", offset = const, vm = addr_space)
|
|
if self.outside_module(d, kernel_syms, kmods):
|
|
break
|
|
elif op.operands[0].type == 'Immediate':
|
|
# Check for CALL ADDR
|
|
d = op.operands[0].value
|
|
if self.outside_module(d, kernel_syms, kmods):
|
|
break
|
|
elif op.operands[0].type == 'Register':
|
|
# Check for CALL REG
|
|
d = regs.get(op.operands[0].name)
|
|
if d and self.outside_module(d, kernel_syms, kmods):
|
|
break
|
|
elif op.flowControl == 'FC_UNC_BRANCH' and op.mnemonic == "JMP":
|
|
# Clear the push value
|
|
if push_val:
|
|
push_val = None
|
|
if op.size > 2:
|
|
if op.operands[0].type == 'AbsoluteMemoryAddress':
|
|
# Check for JMP [ADDR]
|
|
if model == '32bit':
|
|
const = op.operands[0].disp & 0xFFFFFFFF
|
|
d = obj.Object("unsigned int", offset = const, vm = addr_space)
|
|
else:
|
|
const = op.operands[0].disp
|
|
d = obj.Object("long long", offset = const, vm = addr_space)
|
|
if self.outside_module(d, kernel_syms, kmods):
|
|
break
|
|
elif op.operands[0].type == 'Immediate':
|
|
# Check for JMP ADDR
|
|
d = op.operands[0].value
|
|
if self.outside_module(d, kernel_syms, kmods):
|
|
break
|
|
elif op.size == 2 and op.operands[0].type == 'Register':
|
|
# Check for JMP REG
|
|
d = regs.get(op.operands[0].name)
|
|
if d and self.outside_module(d, kernel_syms, kmods):
|
|
break
|
|
elif op.flowControl == 'FC_NONE':
|
|
# Check for PUSH followed by a RET
|
|
if (op.mnemonic == "PUSH" and
|
|
op.operands[0].type == 'Immediate' and op.size == 5):
|
|
# Set the push value
|
|
push_val = op.operands[0].value
|
|
# Check for moving immediate values into a register
|
|
if (op.mnemonic == "MOV" and op.operands[0].type == 'Register'
|
|
and op.operands[1].type == 'Immediate'):
|
|
# Clear the push value
|
|
if push_val:
|
|
push_val = None
|
|
# Save the value put into the register
|
|
regs[op.operands[0].name] = op.operands[1].value
|
|
elif op.flowControl == 'FC_RET':
|
|
if push_val:
|
|
d = push_val
|
|
if self.outside_module(d, kernel_syms, kmods):
|
|
break
|
|
# This causes us to stop disassembling when
|
|
# reaching the end of a function
|
|
break
|
|
n += 1
|
|
|
|
# filtering out false positives due to structs, you can tweak this as needed
|
|
if d and self.outside_module(d, kernel_syms, kmods) == True and str(ops[n+1].mnemonic) not in ["DB 0xff", "ADD", "XCHG", "OUTS"]:
|
|
inlined = True
|
|
|
|
return (inlined, d)
|
|
|
|
def calculate(self):
|
|
common.set_plugin_members(self)
|
|
|
|
(kernel_symbol_addresses, kmod) = common.get_kernel_function_addrs(self)
|
|
|
|
model = self.addr_space.profile.metadata.get('memory_model', 0)
|
|
if model == '32bit':
|
|
distorm_mode = distorm3.Decode32Bits
|
|
else:
|
|
distorm_mode = distorm3.Decode64Bits
|
|
|
|
sym_addrs = self.profile.get_all_function_addresses()
|
|
|
|
# get kernel start, end
|
|
kp = self.addr_space.profile.get_symbol("_g_kernel_kmod_info")
|
|
kmodk = obj.Object("kmod_info", offset = kp, vm = self.addr_space)
|
|
k_start = kmodk.address
|
|
k_end = k_start + kmodk.m('size')
|
|
|
|
####### STEP 1 - CHECK SYSTEM CALL INLINE HOOKS ############
|
|
|
|
# get syscall table
|
|
nsysent = obj.Object("int", offset = self.addr_space.profile.get_symbol("_nsysent"), vm = self.addr_space)
|
|
sysents = obj.Object(theType = "Array", offset = self.addr_space.profile.get_symbol("_sysent"), vm = self.addr_space, count = nsysent, targetType = "sysent")
|
|
|
|
# check if syscall table entries have been modified
|
|
dict_syscall_funcs = {}
|
|
list_syscall_names = []
|
|
for (i, sysent) in enumerate(sysents):
|
|
ent_addr = sysent.sy_call.v()
|
|
hooked = ent_addr not in sym_addrs # using check_syscalls method
|
|
inlined, dst_addr = self.isInlined(model, distorm_mode, ent_addr, kernel_symbol_addresses, [kmodk])
|
|
prolog_inlined = self.isPrologInlined(model, distorm_mode, ent_addr)
|
|
if hooked == True or inlined == True or prolog_inlined == True:
|
|
if dst_addr != None:
|
|
kext = self.findKextWithAddress(dst_addr)
|
|
else:
|
|
kext = self.findKextWithAddress(ent_addr)
|
|
yield ("SyscallTable1", i, ent_addr, hooked, (inlined or prolog_inlined), False, '-', kext)
|
|
else:
|
|
ent_name = self.profile.get_symbol_by_address_type("kernel", ent_addr, "N_FUN")
|
|
# check for duplicate syscall functions
|
|
if ent_name != "_nosys" and ent_name in dict_syscall_funcs:
|
|
prev_ent = dict_syscall_funcs[ent_name]
|
|
kext = self.findKextWithAddress(ent_addr)
|
|
yield ("SyscallTable", list_syscall_names.index(ent_name), prev_ent.sy_call.v(), False, False, False, '-', kext)
|
|
yield ("DuplicateSyscall -> {0}".format(ent_name), i, ent_addr, True, False, False, '-', kext)
|
|
else:
|
|
# check for dtrace syscall hooks
|
|
if ent_name.find("dtrace") > -1:
|
|
kext = self.findKextWithAddress(ent_addr)
|
|
yield ("SyscallTable", i, ent_addr, False, False, False, '-', kext)
|
|
else:
|
|
# add to list
|
|
list_syscall_names.append(ent_name)
|
|
dict_syscall_funcs[ent_name] = sysent
|
|
|
|
|
|
####### STEP 2 - KERNEL & KEXTS ###############
|
|
|
|
# get symbols from kext __TEXT in memory rather than file
|
|
kext_addr_list = []
|
|
|
|
# get kernel address
|
|
kmod = obj.Object("kmod_info", offset = self.addr_space.profile.get_symbol("_g_kernel_kmod_info"), vm = self.addr_space)
|
|
kext_addr_list.append((kmod.address.v(), kmod.address + kmod.m('size'), '__kernel__'))
|
|
|
|
# get other kext addresses
|
|
p = self.addr_space.profile.get_symbol("_kmod")
|
|
kmodaddr = obj.Object("Pointer", offset = p, vm = self.addr_space)
|
|
kmod = kmodaddr.dereference_as("kmod_info")
|
|
while kmod.is_valid():
|
|
kext_addr_list.append((kmod.address.v(), kmod.address + kmod.m('size'), kmod.name))
|
|
kmod = kmod.next
|
|
|
|
# loop thru kexts
|
|
for kext_address, kext_end, kext_name in kext_addr_list:
|
|
#loop thru kext functions
|
|
for func_name, func_addr in self.getKextSymbols(kext_addr = kext_address, onlyFunctions = True, fmodel = model):
|
|
inlined = False
|
|
|
|
# false positive, remove if needed
|
|
if func_name in ["pthreads_dummy_symbol"]:
|
|
continue
|
|
|
|
# check if function's been modified
|
|
modified, dst_addr = self.isCallReferenceModified(model, distorm_mode, func_addr, kernel_symbol_addresses, kext_addr_list)
|
|
if modified:
|
|
if dst_addr != None:
|
|
hook_kext = self.findKextWithAddress(dst_addr)
|
|
else:
|
|
hook_kext = kext_name
|
|
|
|
yield ("SymbolsTable", '-', func_addr, False, modified, False, '-', hook_kext)
|
|
|
|
inlined, dst_addr = self.isInlined(model, distorm_mode, func_addr, kernel_symbol_addresses, kext_addr_list)
|
|
if inlined:
|
|
if dst_addr != None:
|
|
hook_kext = self.findKextWithAddress(dst_addr)
|
|
else:
|
|
hook_kext = kext_name
|
|
yield ("SymbolsTable", '-', func_addr, False, inlined, False, '-', hook_kext)
|
|
|
|
########## STEP 3 - TRAP TABLE ###############
|
|
|
|
# check if trap table hooked using check_trap_table
|
|
args = ()
|
|
trap = check_trap_table.mac_check_trap_table(self._config, args)
|
|
for (table_addr, table_name, i, call_addr, sym_name, hooked) in trap.calculate():
|
|
if hooked == True or 'dtrace' in sym_name:
|
|
kext = self.findKextWithAddress(call_addr)
|
|
yield ("TrapTable", i, call_addr, hooked, False, False, '-', kext)
|
|
|
|
else:
|
|
inlined, dst_addr = self.isInlined(model, distorm_mode, call_addr, kernel_symbol_addresses, [kmodk])
|
|
if inlined:
|
|
if dst_addr != None:
|
|
hook_kext = self.findKextWithAddress(dst_addr)
|
|
else:
|
|
hook_kext = kext_name
|
|
|
|
yield ("TrapTable", '-', func_addr, False, inlined, False, '-', hook_kext)
|
|
else:
|
|
modified, dst_addr = self.isCallReferenceModified(model, distorm_mode, call_addr, kernel_symbol_addresses, [kmodk])
|
|
|
|
if modified:
|
|
if dst_addr != None:
|
|
hook_kext = self.findKextWithAddress(dst_addr)
|
|
else:
|
|
hook_kext = kext_name
|
|
|
|
yield ("TrapTable", '-', func_addr, False, modified, False, '-', hook_kext)
|
|
|
|
def render_text(self, outfd, data):
|
|
self.table_header(outfd, [("Table Name", "<30"), ("Index", "<6"), ("Address", "[addrpad]"), ("Symbol", "<30"), ("Inlined", "<5"), ("Shadowed","<5"), ("Perms","<6"), ("Hook In", "")])
|
|
for (table_name, i, call_addr, hooked, inlined, syscall_shadowed, perms, kext) in data:
|
|
if hooked == False:
|
|
sym_name = self.profile.get_symbol_by_address_type("kernel", call_addr, "N_FUN")
|
|
if sym_name.find("dtrace") > -1:
|
|
sym_name = "[HOOKED] {0}".format(sym_name)
|
|
elif hooked == True:
|
|
sym_name = "HOOKED"
|
|
else:
|
|
sym_name = hooked
|
|
|
|
if inlined == False:
|
|
txt_inlined = "No"
|
|
elif inlined == True:
|
|
txt_inlined = "Yes"
|
|
else:
|
|
txt_inlined = "-"
|
|
|
|
if syscall_shadowed == False:
|
|
txt_shadowed = "No"
|
|
elif syscall_shadowed == True:
|
|
txt_shadowed = "Yes"
|
|
else:
|
|
txt_shadowed = "-"
|
|
|
|
self.table_row(outfd, table_name, i, call_addr, sym_name, txt_inlined, txt_shadowed, perms, kext)
|
|
|
|
|
|
|