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

Source Code for Module rekall.plugins.linux.bash

  1  # Rekall Memory Forensics 
  2  # 
  3  # Copyright 2013 Google Inc. All Rights Reserved. 
  4  # 
  5  # Authors: 
  6  # Michael Cohen <scudette@users.sourceforge.net> 
  7  # 
  8  # This program is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or (at 
 11  # your option) any later version. 
 12  # 
 13  # This program is distributed in the hope that it will be useful, but 
 14  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 16  # General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with this program; if not, write to the Free Software 
 20  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 21  # 
 22  # pylint: disable=protected-access 
 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   
36 -class TimestampScanner(scan.BaseScanner):
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 # We use a quick string search first for this rather unique string. 44 ('StringCheck', dict(needle="#")), 45 46 # Refine the search with a more precise regex. 47 ('RegexCheck', dict(regex=r"\#\d{10}")), 48 ]
49 50
51 -class HeapTimestampScanner(common.HeapScannerMixIn, TimestampScanner):
52 pass
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
77 -class BashProfile64(basic.ProfileLP64, basic.BasicClasses):
78 """Profile to parse internal bash data structures.""" 79 80 __abstract = True 81 82 # types come from bash's ./lib/readline/history.h 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
91 - def __init__(self, **kwargs):
92 super(BashProfile64, self).__init__(**kwargs) 93 self.add_types(self.bash_vtype_64)
94 95
96 -class BashProfile32(basic.Profile32Bits, basic.BasicClasses):
97 """Profile to parse internal bash data structures.""" 98 99 __abstract = True 100 101 # types come from bash's ./lib/readline/history.h 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
110 - def __init__(self, **kwargs):
111 super(BashProfile32, self).__init__(**kwargs) 112 self.add_types(self.bash_vtype_32)
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
152 - def collect(self):
153 for task in self.filter_processes(): 154 process_as = task.get_process_address_space() 155 156 # Choose the correct scanner to use depending on the flags. 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