msfbinscan 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. #!/usr/bin/env ruby
  2. # -*- coding: binary -*-
  3. #
  4. # $Id$
  5. # $Revision$
  6. #
  7. msfbase = __FILE__
  8. while File.symlink?(msfbase)
  9. msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
  10. end
  11. $:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib')))
  12. require 'msfenv'
  13. $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
  14. require 'metasm'
  15. require 'rex/elfparsey'
  16. require 'rex/elfscan'
  17. require 'rex/machparsey'
  18. require 'rex/machscan'
  19. require 'rex/peparsey'
  20. require 'rex/pescan'
  21. require 'rex/arch/x86'
  22. require 'optparse'
  23. def opt2i(o)
  24. o.index("0x")==0 ? o.hex : o.to_i
  25. end
  26. opt = OptionParser.new
  27. opt.banner = "Usage: #{$PROGRAM_NAME} [mode] <options> [targets]"
  28. opt.separator('')
  29. opt.separator('Modes:')
  30. worker = nil
  31. param = {}
  32. files = []
  33. mode = ""
  34. opt.on('-j', '--jump [regA,regB,regC]', 'Search for jump equivalent instructions [PE|ELF|MACHO]') do |t|
  35. # take csv of register names (like eax,ebx) and convert
  36. # them to an array of register numbers
  37. mode = "jump"
  38. regnums = t.split(',').collect { |o|
  39. begin
  40. Rex::Arch::X86.reg_number(o)
  41. rescue
  42. puts "Invalid register \"#{o}\""
  43. exit(1)
  44. end
  45. }
  46. param['args'] = regnums
  47. end
  48. opt.on('-p', '--poppopret', 'Search for pop+pop+ret combinations [PE|ELF|MACHO]') do |t|
  49. mode = "pop"
  50. param['args'] = t
  51. end
  52. opt.on('-r', '--regex [regex]', 'Search for regex match [PE|ELF|MACHO]') do |t|
  53. mode = "regex"
  54. param['args'] = t
  55. end
  56. opt.on('-a', '--analyze-address [address]', 'Display the code at the specified address [PE|ELF]') do |t|
  57. mode = "analyze-address"
  58. param['args'] = opt2i(t)
  59. end
  60. opt.on('-b', '--analyze-offset [offset]', 'Display the code at the specified offset [PE|ELF]') do |t|
  61. mode = "analyze-offset"
  62. param['args'] = opt2i(t)
  63. end
  64. opt.on('-f', '--fingerprint', 'Attempt to identify the packer/compiler [PE]') do |t|
  65. mode = "fingerprint"
  66. param['database'] = File.join(File.dirname(msfbase), 'data', 'msfpescan', 'identify.txt')
  67. end
  68. opt.on('-i', '--info', 'Display detailed information about the image [PE]') do |t|
  69. mode = "info"
  70. end
  71. opt.on('-R', '--ripper [directory]', 'Rip all module resources to disk [PE]') do |t|
  72. mode = "ripper"
  73. param['dir'] = t
  74. end
  75. opt.on('--context-map [directory]', 'Generate context-map files [PE]') do |t|
  76. mode = "context"
  77. param['dir'] = t
  78. end
  79. opt.separator('')
  80. opt.separator('Options:')
  81. opt.on('-A', '--after [bytes]', 'Number of bytes to show after match (-a/-b) [PE|ELF|MACHO]') do |t|
  82. param['after'] = opt2i(t)
  83. end
  84. opt.on('-B', '--before [bytes]', 'Number of bytes to show before match (-a/-b) [PE|ELF|MACHO]') do |t|
  85. param['before'] = opt2i(t)
  86. end
  87. opt.on('-I', '--image-base [address]', 'Specify an alternate ImageBase [PE|ELF|MACHO]') do |t|
  88. param['imagebase'] = opt2i(t)
  89. end
  90. opt.on('-D', '--disasm', 'Disassemble the bytes at this address [PE|ELF]') do |t|
  91. param['disasm'] = true
  92. end
  93. opt.on('-F', '--filter-addresses [regex]', 'Filter addresses based on a regular expression [PE]') do |t|
  94. param['filteraddr'] = t
  95. end
  96. opt.on_tail("-h", "--help", "Show this message") do
  97. $stderr.puts opt
  98. exit(1)
  99. end
  100. begin
  101. opt.parse!
  102. rescue OptionParser::InvalidOption, OptionParser::MissingArgument
  103. $stderr.puts "Invalid option, try -h for usage"
  104. exit(1)
  105. end
  106. if mode.empty?
  107. $stderr.puts "A mode must be selected"
  108. $stderr.puts opt
  109. exit(1)
  110. end
  111. # check if the file is a directory if it is collect all the entries
  112. ARGV.each do |file|
  113. if(File.directory?(file))
  114. dir = Dir.open(file)
  115. dir.entries.each do |ent|
  116. path = File.join(file, ent)
  117. next if not File.file?(path)
  118. files << File.join(path)
  119. end
  120. else
  121. files << file
  122. end
  123. end
  124. # we need to do some work to figure out the file format
  125. files.each do |file|
  126. param['file'] = file
  127. bin = Metasm::AutoExe.decode_file(file) if not file.empty?
  128. if bin.kind_of?(Metasm::PE)
  129. case mode
  130. when "jump"
  131. worker = Rex::PeScan::Scanner::JmpRegScanner
  132. when "pop"
  133. worker = Rex::PeScan::Scanner::PopPopRetScanner
  134. when "regex"
  135. worker = Rex::PeScan::Scanner::RegexScanner
  136. when "analyze-address"
  137. worker = Rex::PeScan::Search::DumpRVA
  138. when "analyze-offset"
  139. worker = Rex::PeScan::Search::DumpOffset
  140. when "fingerprint"
  141. worker = Rex::PeScan::Analyze::Fingerprint
  142. when "info"
  143. worker = Rex::PeScan::Analyze::Information
  144. when "ripper"
  145. worker = Rex::PeScan::Analyze::Ripper
  146. when "context"
  147. worker = Rex::PeScan::Analyze::ContextMapDumper
  148. else
  149. $stderr.puts("Mode unsupported by file format")
  150. end
  151. pe_klass = Rex::PeParsey::Pe
  152. begin
  153. pe = pe_klass.new_from_file(file, true)
  154. rescue ::Interrupt
  155. raise $!
  156. rescue Rex::PeParsey::FileHeaderError
  157. next if $!.message == "Couldn't find the PE magic!"
  158. raise $!
  159. rescue Errno::ENOENT
  160. $stdout.puts("File does not exist: #{file}")
  161. next
  162. rescue ::Rex::PeParsey::SkipError
  163. next
  164. rescue ::Exception => e
  165. $stdout.puts "[#{file}] #{e.class}: #{e}"
  166. next
  167. end
  168. if (param['imagebase'])
  169. pe.image_base = param['imagebase'];
  170. end
  171. if not worker
  172. $stderr.puts("A mode could not be set for this file.")
  173. next
  174. end
  175. o = worker.new(pe)
  176. o.scan(param)
  177. pe.close
  178. elsif bin.kind_of?(Metasm::ELF)
  179. case mode
  180. when "jump"
  181. worker = Rex::ElfScan::Scanner::JmpRegScanner
  182. when "pop"
  183. worker = Rex::ElfScan::Scanner::PopPopRetScanner
  184. when "regex"
  185. worker = Rex::ElfScan::Scanner::RegexScanner
  186. when "analyze-address"
  187. worker = Rex::ElfScan::Search::DumpRVA
  188. when "analyze-offset"
  189. worker = Rex::ElfScan::Search::DumpOffset
  190. else
  191. $stderr.puts("Mode unsupported by file format")
  192. end
  193. begin
  194. elf = Rex::ElfParsey::Elf.new_from_file(file, true)
  195. rescue Rex::ElfParsey::ElfHeaderError
  196. if $!.message == 'Invalid magic number'
  197. $stderr.puts("Skipping #{file}: #{$!}")
  198. next
  199. end
  200. raise $!
  201. rescue Errno::ENOENT
  202. $stderr.puts("File does not exist: #{file}")
  203. next
  204. end
  205. if (param['imagebase'])
  206. elf.base_addr = param['imagebase'];
  207. end
  208. if not worker
  209. $stderr.puts("A mode could not be set for this file.")
  210. next
  211. end
  212. o = worker.new(elf)
  213. o.scan(param)
  214. elf.close
  215. elsif bin.kind_of?(Metasm::MachO)
  216. case mode
  217. when "jump"
  218. worker = Rex::MachScan::Scanner::JmpRegScanner
  219. when "pop"
  220. worker = Rex::MachScan::Scanner::PopPopRetScanner
  221. when "regex"
  222. worker = Rex::MachScan::Scanner::RegexScanner
  223. else
  224. $stderr.puts("Mode unsupported by file format")
  225. end
  226. begin
  227. mach = Rex::MachParsey::Mach.new_from_file(file, true)
  228. o = worker.new(mach)
  229. o.scan(param)
  230. mach.close
  231. rescue Rex::MachParsey::MachHeaderError
  232. $stderr.puts("File is not a Mach-O binary, trying Fat..\n")
  233. begin
  234. fat = Rex::MachParsey::Fat.new_from_file(file, true)
  235. o = worker.new(fat)
  236. o.scan(param)
  237. fat.close
  238. rescue
  239. $stderr.puts("Error: " + $!.to_s)
  240. $stderr.puts("Skipping #{file}")
  241. end
  242. rescue Errno::ENOENT
  243. $stderr.puts("File does not exist: #{file}")
  244. next
  245. end
  246. end
  247. if not worker
  248. $stderr.puts("Unsupported file format")
  249. $stderr.puts("Skipping #{file}")
  250. next
  251. end
  252. end