Package rekall :: Package plugins :: Package linux :: Module check_syscall
[frames] | no frames]

Source Code for Module rekall.plugins.linux.check_syscall

  1  # Rekall Memory Forensics 
  2  # Copyright (C) 2007-2013 Volatility Foundation 
  3  # Copyright 2013 Google Inc. All Rights Reserved. 
  4  # 
  5  # This file is part of Rekall Memory Forensics. 
  6  # 
  7  # Rekall Memory Forensics is free software; you can redistribute it and/or 
  8  # modify it under the terms of the GNU General Public License Version 2 as 
  9  # published by the Free Software Foundation.  You may not use, modify or 
 10  # distribute this program under any other version of the GNU General Public 
 11  # License. 
 12  # 
 13  # Rekall Memory Forensics is distributed in the hope that it will be useful, 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  # GNU General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License along with 
 19  # Rekall Memory Forensics.  If not, see <http://www.gnu.org/licenses/>. 
 20  # 
 21   
 22  """ 
 23  @author:       Andrew Case 
 24  @license:      GNU General Public License 2.0 
 25  @contact:      atcuno@gmail.com 
 26  @organization: 
 27  """ 
 28   
 29  from rekall.plugins.linux import common 
 30  from rekall.plugins.tools import dynamic_profiles 
 31   
 32   
33 -class CheckSyscall(common.LinuxPlugin):
34 """Checks if the system call table has been altered.""" 35 36 __name = "check_syscall" 37 38 table_header = [ 39 dict(name="divider", type="Divider"), 40 dict(name="table", hidden=True), 41 dict(name="index", style="address"), 42 dict(name="address", style="address"), 43 dict(name="symbol", width=80) 44 ] 45 46
47 - def Find_sys_call_tables(self):
48 """Calculates the size of the syscall table. 49 50 Here we need the symbol __NR_syscall_max. We derive it from 51 disassembling the following system calls: 52 53 - system_call_fastpath function: 54 55 http://lxr.linux.no/linux+v3.12/arch/x86/kernel/entry_64.S#L620 56 system_call_fastpath: 57 #if __SYSCALL_MASK == ~0 58 cmpq $__NR_syscall_max,%rax 59 #else 60 andl $__SYSCALL_MASK,%eax 61 cmpl $__NR_syscall_max,%eax 62 #endif 63 64 - ret_from_sys_call function (with a small rewind): 65 http://lxr.linux.no/linux+v2.6.26/arch/x86/kernel/entry_64.S#L249 66 67 249 cmpq $__NR_syscall_max,%rax 68 250 ja badsys 69 251 movq %r10,%rcx 70 252 call *sys_call_table(,%rax,8) # XXX: rip relative 71 253 movq %rax,RAX-ARGOFFSET(%rsp) 72 254 /* 73 255 * Syscall return path ending with SYSRET (fast path) 74 256 * Has incomplete stack frame and undefined top of stack. 75 257 */ 76 258 ret_from_sys_call: 77 259 movl $_TIF_ALLWORK_MASK,%edi 78 260 /* edi: flagmask */ 79 80 81 - sysenter_do_call 82 Linux> dis "linux!sysenter_do_call" 83 Address Rel Op Codes Instruction Comment 84 ------- ---------- -------------------- ------------------ ------- 85 ------ linux!sysenter_do_call ------: 0xc12c834d 86 0xc12c834d 0x0 3d5d010000 CMP EAX, 0x15d 87 0xc12c8352 0x5 0f8397baffff JAE 0xc12c3def linux!syscall_badsys 88 89 """ 90 rules = [ 91 # Look for a comparison of the register (EAX) with a fixed value. 92 {'mnemonic': 'CMP', 'operands': [ 93 {'type': 'REG'}, {'type': 'IMM', 'target': "$value"}]}, 94 95 # Immediately followed by a branch to linux!badsys, 96 # linux!ia32_badsys etc. 97 {'comment': '~.+badsys'} 98 ] 99 func = None 100 tables = set() 101 for func_name, table_name in [ 102 # http://lxr.free-electrons.com/source/arch/x86_64/kernel/entry.S?v=2.4.37 103 ("system_call", "sys_call_table"), 104 # http://lxr.free-electrons.com/source/arch/x86/kernel/entry_64.S?v=3.16 105 ("system_call_fastpath", "sys_call_table"), 106 107 108 # http://lxr.free-electrons.com/source/arch/x86/ia32/ia32entry.S?v=3.14 109 ("ia32_sysenter_target", "ia32_sys_call_table"), 110 ("sysenter_auditsys", "ia32_sys_call_table"), 111 112 # http://lxr.free-electrons.com/source/arch/x86/kernel/entry_32.S?v=3.3 113 ("sysenter_do_call", "sys_call_table")]: 114 115 if table_name in tables: 116 continue 117 118 # This table does not exist in this profile dont bother looking for 119 # its size. 120 if self.profile.get_constant(table_name) == None: 121 continue 122 123 func = self.profile.get_constant_object( 124 func_name, target="Function") 125 if func == None: 126 continue 127 128 matcher = dynamic_profiles.DisassembleMatcher( 129 name="sys_call_table_size", 130 mode=func.mode, rules=rules, session=self.session) 131 132 result = matcher.MatchFunction(func) 133 if result: 134 tables.add(table_name) 135 yield table_name, result["$value"] + 1 136 137 # Fallback. Note this underestimates the size quite a bit. 138 if func == None: 139 table_size = len([x for x in self.profile.constants 140 if x.startswith("__syscall_meta__")]) or 0x300 141 yield "ia32_sys_call_table", table_size 142 yield "sys_call_table", table_size
143
144 - def collect(self):
145 """ 146 This works by walking the system call table 147 and verifies that each is a symbol in the kernel 148 """ 149 for table_name, table_size in self.Find_sys_call_tables(): 150 # The syscall table is simply an array of pointers to functions. 151 table = self.profile.get_constant_object( 152 table_name, 153 target="Array", 154 target_args=dict( 155 count=table_size, 156 target="Pointer", 157 target_args=dict( 158 target="Function" 159 ) 160 ) 161 ) 162 163 yield dict(divider="Table %s" % table_name) 164 165 resolver = self.session.address_resolver 166 for i, entry in enumerate(table): 167 sym_name = resolver.format_address(entry.deref())[:2] 168 yield dict( 169 table=table_name, index=i, 170 address=entry, 171 symbol=sym_name or "Unknown", 172 highlight=None if sym_name else "important")
173