aboutsummaryrefslogtreecommitdiff
path: root/SystemCalls.py
diff options
context:
space:
mode:
Diffstat (limited to 'SystemCalls.py')
-rw-r--r--SystemCalls.py297
1 files changed, 297 insertions, 0 deletions
diff --git a/SystemCalls.py b/SystemCalls.py
new file mode 100644
index 0000000..4fcffd2
--- /dev/null
+++ b/SystemCalls.py
@@ -0,0 +1,297 @@
+""" IDAPython plugin to display all system calls of AMD/Intel 32/64bit
+ Linux userland binaries.
+
+ Work in progress...
+
+ The system call ABI from the following link are supported.
+ http://esec-lab.sogeti.com/post/2011/07/05/Linux-syscall-ABI
+
+ by n0p
+"""
+
+from miasm2.core.bin_stream_ida import bin_stream_ida
+from miasm2.analysis.depgraph import DependencyGraph
+
+from utils import guess_machine
+
+from idaapi import *
+
+from SystemCalls_constants import *
+
+
+
+class SystemCallView(Choose2):
+
+ def __init__(self, systemCalls):
+
+ self.systemCalls = systemCalls
+
+ Choose2.__init__(self,
+ "System call",
+ [ ["Address", 13],
+ ["Type", 10],
+ ["Number", 10],
+ ["Name", 20],
+ ["Pointer Size", 12] ])
+
+ self.items = list()
+
+ self.nop_items = list()
+
+ self.icon = 150
+
+ self.cmd_nop = None
+
+ self.initialized = False
+
+ def __fillView(self):
+
+ self.systemCalls.searchSystemCalls()
+
+ self.items = list()
+
+ if len(self.systemCalls.x86) != 0:
+ for call in self.systemCalls.x86:
+ try:
+ callNr = int(self.systemCalls.getSystemCallNumber(call[0])[0])
+ self.items.append(["0x%X" % call[0],
+ systemCallTypes[call[1]],
+ "0x%03X" % callNr,
+ x86SystemCalls[callNr],
+ "32bit"])
+ except:
+ # No hex system call number found.
+ self.items.append(["0x%X" % call[0],
+ systemCallTypes[call[1]],
+ "",
+ "",
+ "32bit"])
+
+ if len(self.systemCalls.x86_64) != 0:
+ for call in self.systemCalls.x86_64:
+ try:
+ callNr = int(self.systemCalls.getSystemCallNumber(call[0])[0])
+ self.items.append(["0x%X" % call[0],
+ systemCallTypes[call[1]],
+ "0x%03X" % callNr,
+ x86_64SystemCalls[callNr],
+ "64bit"])
+ except:
+ # No hex system call number found.
+ self.items.append(["0x%X" % call[0],
+ systemCallTypes[call[1]],
+ "",
+ "",
+ "64bit"])
+
+ def OnClose(self):
+ pass
+
+ def OnCommand(self, n, cmd_id):
+ if cmd_id == self.cmd_nop:
+ start_ea = int(self.items[n][0], 16)
+ end_ea = start_ea+ItemSize(start_ea)
+
+ self.nop_items.append(self.items[n][0])
+
+ for i in xrange(start_ea, end_ea):
+ PatchByte(i, 0x90)
+
+ def OnGetIcon(self, n):
+ if not len(self.items) > 0:
+ return -1
+
+ if self.items[n][2] == "":
+ # No system call number found => display red icon.
+ return 59
+ else:
+ # Display green icon.
+ return 61
+
+ def OnGetLine(self, n):
+ return self.items[n]
+
+ def OnGetLineAttr(self, n):
+ if len(self.nop_items) > 0:
+ if self.items[n][0] in self.nop_items:
+ return [0xB6B6B4, 0]
+
+ def OnGetSize(self):
+ return len(self.items)
+
+ def OnRefresh(self, n):
+ self.__fillView()
+
+ def OnSelectLine(self, n):
+ idaapi.jumpto(int(self.items[n][0], 16))
+
+ def show(self):
+ if not self.initialized:
+ self.initialized = True
+ self.__fillView()
+
+ if self.Show() < 0: return False
+
+ if self.cmd_nop == None:
+ self.cmd_nop = self.AddCommand("NOP system call",
+ flags = idaapi.CHOOSER_POPUP_MENU,
+ icon=50)
+
+ return True
+
+
+class SystemCall():
+
+ def __init__(self):
+
+ # Init miasm stuff.
+ self.machine = guess_machine()
+ self.mn, self.dis_engine, self.ira = self.machine.mn, self.machine.dis_engine, self.machine.ira
+
+ self.mdis = self.dis_engine(bin_stream_ida(), dont_dis_nulstart_bloc=True)
+ self.ir_arch = self.ira(self.mdis.symbol_pool)
+
+ # Populate symbols with ida names
+ for ad, name in Names():
+ if name is None:
+ continue
+ self.mdis.symbol_pool.add_label(name, ad)
+
+ self.x86 = list()
+ self.x86_64 = list()
+
+ self.systemCallView = SystemCallView(self)
+
+ def getSystemCallNumber(self, addr):
+ """ Get the value of rax/eax at the time of the system call.
+ """
+
+ sol = list()
+
+ # Get the current function
+ f = get_func(addr)
+
+ if not f:
+ return sol
+
+ blocs = self.mdis.dis_multibloc(f.startEA)
+
+ # Generate IR
+ for bloc in blocs:
+ self.ir_arch.add_bloc(bloc)
+
+ # Check if addr is in a basic block without an entry.
+ if len(self.ir_arch.getby_offset(addr)) == 0:
+ fc = qflow_chart_t("", f, BADADDR, BADADDR, FC_PREDS)
+
+ # Iterate through all basic blocks.
+ for i in xrange(0, fc.size()):
+ if fc[i].startEA <= addr and addr < fc[i].endEA:
+ # Basic block without entry found.
+ blocs = self.mdis.dis_multibloc(fc[i].startEA)
+
+ # Generate IR
+ for bloc in blocs:
+ self.ir_arch.add_bloc(bloc)
+
+ cur_bloc = list(self.ir_arch.getby_offset(addr))[0]
+ cur_label = cur_bloc.label
+
+ elements = set([self.mn.regs.RAX])
+
+ for line_nb, l in enumerate(cur_bloc.lines):
+ if l.offset == addr:
+ break
+
+ # Get dependency graphs
+ dg = DependencyGraph(self.ir_arch, follow_call=False)
+ graphs = dg.get(cur_label, elements, line_nb,
+ set([self.ir_arch.symbol_pool.getby_offset(f.startEA)]))
+
+ while 1:
+ try:
+ graph = graphs.next()
+ except StopIteration:
+ break
+
+ if not graph.has_loop:
+ sol.append(graph.emul().values()[0])
+
+ return sol
+
+ def searchSystemCalls(self):
+ """ Looks for 'int 80', 'sysenter', 'syscall' and 'gs:[10h]' system calls.
+ """
+ # Iterating through all segments
+ for segment in Segments():
+ # Iterating through all instructions in each segment
+ for head in Heads(segment, SegEnd(segment)):
+ if isCode(GetFlags(head)):
+ inst = DecodeInstruction(head)
+
+ if ( inst.size == 2
+ and get_many_bytes(head, inst.size)
+ .startswith("\xCD\x80")):
+ # int 80h. Just on 32bit.
+ if not [head, 0] in self.x86:
+ self.x86.append([head, 0])
+
+ elif ( inst.size == 2
+ and get_many_bytes(head, inst.size)
+ .startswith("\x0F\x34")):
+ # sysenter. Just on 32bit.
+ if not [head, 1] in self.x86:
+ self.x86.append([head, 1])
+
+ elif ( inst.size == 2
+ and get_many_bytes(head, inst.size)
+ .startswith("\x0F\x05")):
+ # syscall. 32bit just on AMD. 64bit on AMD and Intel.
+ if idaapi.ph.flag & idaapi.PR_USE64:
+ if not [head, 2] in self.x86_64:
+ self.x86_64.append([head, 2])
+ else:
+ if not [head, 2] in self.x86:
+ self.x86.append([head, 2])
+
+ elif ( inst.size == 7
+ and get_many_bytes(head, inst.size)
+ .startswith("\x65\xFF\x15\x10\x00\x00\x00")):
+ # gs:[10h]. Just on 32bit.
+ if not [head, 3] in self.x86:
+ self.x86.append([head, 3])
+
+ def showView(self):
+ self.systemCallView.show()
+
+
+class SystemCallPlugin_t(idaapi.plugin_t):
+ flags = 0
+ comment = ""
+ help = ""
+ wanted_name = "System Calls"
+ wanted_hotkey = ""
+
+ def init(self):
+ global systemCalls
+
+ if idaapi.ph_get_id() == idaapi.PLFM_386:
+ # Check if already initialized
+ if not 'systemCalls' in globals():
+ systemCalls = SystemCall()
+
+ return idaapi.PLUGIN_KEEP
+ else:
+ return idaapi.PLUGIN_SKIP
+
+ def run(self, arg):
+ global systemCalls
+
+ systemCalls.showView()
+
+ def term(self):
+ if 'systemCalls' in globals():
+ del globals()['systemCalls']
+
+def PLUGIN_ENTRY():
+ return SystemCallPlugin_t()