1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 """Scan for bash history entries.
25
26 Based on the algorithm by Andrew Case but greatly optimised for speed.
27 """
28
29 __author__ = "Michael Cohen <scudette@gmail.com>"
30
31 from rekall import scan
32 from rekall.plugins.overlays import basic
33 from rekall.plugins.linux import common
34
35
37 """Search for the realine timestamps.
38
39 These have a special signature which looks like "#" followed by the
40 time since the epoch - for example #1384457055.
41 """
42 checks = [
43
44 ('StringCheck', dict(needle="#")),
45
46
47 ('RegexCheck', dict(regex=r"\#\d{10}")),
48 ]
49
50
53
54
55 -class LinHistoryScanner(scan.PointerScanner):
56 """Scan for the realine history struct.
57
58 This looks for references to the timestamps discovered by the
59 TimestampScanner above.
60 """
61 - def scan(self, **kwargs):
62 for hit in super(LinHistoryScanner, self).scan(**kwargs):
63 timestamp_relative_offset = self.profile.get_obj_offset(
64 "_hist_entry", "timestamp")
65
66 hist_entry = self.profile._hist_entry(
67 offset=hit - timestamp_relative_offset,
68 vm=self.address_space)
69
70 yield hist_entry
71
72
73 -class HeapHistoryScanner(common.HeapScannerMixIn, LinHistoryScanner):
74 """Only scan for history in the heap."""
75
76
78 """Profile to parse internal bash data structures."""
79
80 __abstract = True
81
82
83 bash_vtype_64 = {
84 "_hist_entry": [24, {
85 "line": [0, ["Pointer", dict(target="String")]],
86 "timestamp": [8, ["Pointer", dict(target="String")]],
87 "data": [16, ["Pointer", dict(target="String")]],
88 }],
89 }
90
94
95
97 """Profile to parse internal bash data structures."""
98
99 __abstract = True
100
101
102 bash_vtype_32 = {
103 "_hist_entry": [0xC, {
104 "line": [0, ["Pointer", dict(target="String")]],
105 "timestamp": [4, ["Pointer", dict(target="String")]],
106 "data": [8, ["Pointer", dict(target="String")]],
107 }],
108 }
109
113
114
115 -class BashHistory(common.LinProcessFilter):
116 """Scan the bash process for history.
117
118 Based on original algorithm by Andrew Case.
119 """
120 __name = "bash"
121
122 __args = [
123 dict(name="scan_entire_address_space", type="Boolean",
124 help="Scan the entire process address space, not only the heap."),
125 dict(name="proc_regex", default="^bash$", type="RegEx",
126 help="The processes we should examine."),
127 ]
128
129 table_header = [
130 dict(name="divider", type="Divider"),
131 dict(name="task", hidden=True),
132 dict(name="timestamp", width=24),
133 dict(name="command"),
134 ]
135
136 - def __init__(self, *args, **kwargs):
137 super(BashHistory, self).__init__(*args, **kwargs)
138 if self.profile.metadata("arch") == "AMD64":
139 self.bash_profile = BashProfile64(session=self.session)
140 else:
141 self.bash_profile = BashProfile32(session=self.session)
142
143 - def get_timestamps(self, scanner):
144 """Scan process memory for things that look like a timestamp."""
145 results = {}
146 for hit in scanner.scan():
147 timestamp = int(scanner.address_space.read(hit+1, 10))
148 results[hit] = timestamp
149
150 return results
151
153 for task in self.filter_processes():
154 process_as = task.get_process_address_space()
155
156
157 if self.plugin_args.scan_entire_address_space:
158 timestamp_scanner = TimestampScanner(
159 profile=self.profile, session=self.session,
160 address_space=process_as)
161 else:
162 timestamp_scanner = HeapTimestampScanner(
163 profile=self.profile, session=self.session,
164 address_space=process_as, task=task)
165
166 timestamps = self.get_timestamps(timestamp_scanner)
167 if not timestamps:
168 continue
169
170 yield dict(divider="Task: %s (%s)" % (task.name,
171 task.pid))
172
173
174 if self.plugin_args.scan_entire_address_space:
175 scanner = LinHistoryScanner(
176 profile=self.bash_profile, session=self.session,
177 address_space=process_as, pointers=timestamps)
178 else:
179 scanner = HeapHistoryScanner(
180 profile=self.bash_profile, session=self.session,
181 address_space=process_as, task=task,
182 pointers=timestamps)
183
184 hits = sorted(scanner.scan(), key=lambda x: x.timestamp.deref())
185 for hit in hits:
186 timestamp = self.profile.UnixTimeStamp(
187 value=int(unicode(hit.timestamp.deref())[1:]))
188
189 yield dict(
190 task=task,
191 timestamp=timestamp,
192 command=hit.line.deref())
193