1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
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
92 {'mnemonic': 'CMP', 'operands': [
93 {'type': 'REG'}, {'type': 'IMM', 'target': "$value"}]},
94
95
96
97 {'comment': '~.+badsys'}
98 ]
99 func = None
100 tables = set()
101 for func_name, table_name in [
102
103 ("system_call", "sys_call_table"),
104
105 ("system_call_fastpath", "sys_call_table"),
106
107
108
109 ("ia32_sysenter_target", "ia32_sys_call_table"),
110 ("sysenter_auditsys", "ia32_sys_call_table"),
111
112
113 ("sysenter_do_call", "sys_call_table")]:
114
115 if table_name in tables:
116 continue
117
118
119
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
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
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
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