Class: Memorandom::Scanner
- Inherits:
-
Object
- Object
- Memorandom::Scanner
- Defined in:
- lib/memorandom/scanner.rb
Instance Attribute Summary collapse
-
#output ⇒ Object
Returns the value of attribute output.
-
#overlap ⇒ Object
Returns the value of attribute overlap.
-
#plugins ⇒ Object
Returns the value of attribute plugins.
-
#source ⇒ Object
Returns the value of attribute source.
-
#window ⇒ Object
Returns the value of attribute window.
Instance Method Summary collapse
- #clean_filename(name) ⇒ Object
- #display(str) ⇒ Object
-
#initialize(opts = {}) ⇒ Scanner
constructor
A new instance of Scanner.
- #report_hit(info) ⇒ Object
- #scan(target, source_name = nil) ⇒ Object
Constructor Details
#initialize(opts = {}) ⇒ Scanner
Returns a new instance of Scanner.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/memorandom/scanner.rb', line 11 def initialize(opts={}) plugin_names = Memorandom::PluginManager.plugins.keys # Allow only a subset of plugins to be selected if opts[:plugins] plugin_names = Memorandom::PluginManager.plugins.keys.select{ |name| opts[:plugins].include?(name) } end # Load the selected plugins self.plugins = plugin_names.map { |name| Memorandom::PluginManager.plugins[name].new(self) } self.window = opts[:window] || 1024*1024 self.overlap = opts[:overlap] || 1024*4 self.output = opts[:output] FileUtils.mkdir_p(self.output) if self.output end |
Instance Attribute Details
#output ⇒ Object
Returns the value of attribute output.
9 10 11 |
# File 'lib/memorandom/scanner.rb', line 9 def output @output end |
#overlap ⇒ Object
Returns the value of attribute overlap.
9 10 11 |
# File 'lib/memorandom/scanner.rb', line 9 def overlap @overlap end |
#plugins ⇒ Object
Returns the value of attribute plugins.
8 9 10 |
# File 'lib/memorandom/scanner.rb', line 8 def plugins @plugins end |
#source ⇒ Object
Returns the value of attribute source.
8 9 10 |
# File 'lib/memorandom/scanner.rb', line 8 def source @source end |
#window ⇒ Object
Returns the value of attribute window.
9 10 11 |
# File 'lib/memorandom/scanner.rb', line 9 def window @window end |
Instance Method Details
#clean_filename(name) ⇒ Object
144 145 146 |
# File 'lib/memorandom/scanner.rb', line 144 def clean_filename(name) name.gsub(/[^a-zA-Z0-9_\.\-]/, '_').gsub(/_+/, '_') end |
#display(str) ⇒ Object
148 149 150 |
# File 'lib/memorandom/scanner.rb', line 148 def display(str) $stdout.puts(str) end |
#report_hit(info) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/memorandom/scanner.rb', line 113 def report_hit(info) unless self.output display("[+] #{source} #{info[:type]}@#{info[:offset]} (#{info[:data][0,32].inspect}...)") return end fname = source.split("/").last[0,128] + "_" fname << info[:data][0,16].unpack("H*").first fname << "_#{info[:offset]}.#{info[:type]}" yname = fname + ".yml" fname = clean_filename(fname) yname = clean_filename(yname) fpath = ::File.join(self.output, fname) ::File.open(fpath, "wb") { |fd| fd.write(info[:data]) } display("[+] #{source} #{info[:type]}@#{info[:offset]} (#{info[:data][0,32].inspect}) stored in #{fpath}") ypath = ::File.join(self.output, yname) yhash = { :source => source, :type => info[:type], :offset => info[:offset], :timestamp => Time.now, :length => info[:data].length } ::File.open(ypath, "wb") { |fd| fd.write(yhash.to_yaml) } end |
#scan(target, source_name = nil) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/memorandom/scanner.rb', line 34 def scan(target, source_name = nil) fd = nil if target.respond_to?(:read) self.source = source_name || target.to_s else case target when '-' fd = $stdin self.source = source_name || '<stdin>' else unless ( File.file?(target) or File.blockdev?(target) or File.chardev?(target) ) display("[-] Skipping #{target}: not a file") return end begin fd = File.open(target, "rb") self.source = source_name ||"file:#{target.dup}" rescue ::Interrupt raise $! rescue ::Exception display("[-] Skipping #{target}: #{$!.class} #{$!}") return end end end # Reset the plugin state between each target self.plugins.each { |plugin| plugin.reset } buffer = fd.read(self.window) offset = 0 # Skip empty sources (an empty first read) return unless buffer while buffer.length > 0 self.plugins.each do |plugin| # display("[*] Scanning #{buffer.length} bytes at offset #{offset}") # Track a temporary per-plugin buffer and offset scan_buffer = buffer scan_offset = offset # Adjust the buffer and offset if any hits have been found that are # greater than offset. This is required because of overlap between # search windows. It is rare, but should be handled here so that # plugins don't have to worry about it. adjust = plugin.hits.keys.select{|hit| hit > offset }.last if adjust start_index = ( adjust - offset + 1 ) scan_buffer = buffer[start_index, buffer.length-start_index] scan_offset += start_index end # Scan the buffer with the plugin plugin.scan(scan_buffer, scan_offset) end # Calculate the next sliding window seeked = self.window - self.overlap offset += seeked nbytes = fd.read(seeked) break if not nbytes # Append new data to the end of the buffer buffer << nbytes # Delete most of the previous data from the beginning buffer[0, seeked] = '' end # Close the file descriptor if we opened it fd.close if source =~ /^file:/ end |