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/lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb
Views: 1904
1
# -*- coding: binary -*-
2
# Copyright (c) 2010, [email protected]
3
# All rights reserved.
4
#
5
# Redistribution and use in source and binary forms, with or without
6
# modification, are permitted provided that the following conditions are met:
7
# * Redistributions of source code must retain the above copyright
8
# notice, this list of conditions and the following disclaimer.
9
# * Redistributions in binary form must reproduce the above copyright
10
# notice, this list of conditions and the following disclaimer in the
11
# documentation and/or other materials provided with the distribution.
12
# * The names of the author may not be used to endorse or promote products
13
# derived from this software without specific prior written permission.
14
#
15
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
# DISCLAIMED. IN NO EVENT SHALL [email protected] BE LIABLE FOR ANY
19
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26
#
27
# sf - Sept 2010 - Modified for x64 support and merged into the stdapi extension.
28
#
29
30
#
31
# chao - June 2011 - major overhaul of dll lazy loading, caching, and bit of everything
32
#
33
34
#
35
# zeroSteiner - April 2017 - added support for non-windows platforms
36
#
37
38
require 'pp'
39
require 'enumerator'
40
41
require 'rex/post/meterpreter/extensions/stdapi/railgun/tlv'
42
require 'rex/post/meterpreter/extensions/stdapi/railgun/util'
43
require 'rex/post/meterpreter/extensions/stdapi/railgun/const_manager'
44
require 'rex/post/meterpreter/extensions/stdapi/railgun/multicall'
45
require 'rex/post/meterpreter/extensions/stdapi/railgun/library'
46
require 'rex/post/meterpreter/extensions/stdapi/railgun/library_wrapper'
47
48
module Rex
49
module Post
50
module Meterpreter
51
module Extensions
52
module Stdapi
53
module Railgun
54
55
56
#
57
# The Railgun class to dynamically expose the Windows API.
58
#
59
class Railgun
60
61
#
62
# Railgun::Library's that have builtin definitions.
63
#
64
# If you want to add additional library definitions to be preloaded create a
65
# definition class 'rex/post/meterpreter/extensions/stdapi/railgun/def/$platform/'.
66
# Naming is important and should follow convention. For example, if your
67
# library's name was "my_library"
68
# file name: def_my_library.rb
69
# class name: Def_my_library
70
# entry below: 'my_library'
71
#
72
BUILTIN_LIBRARIES = {
73
'linux' => [
74
'libc'
75
].freeze,
76
'osx' => [
77
'libc',
78
'libobjc'
79
].freeze,
80
'windows' => [
81
'kernel32',
82
'ntdll',
83
'user32',
84
'ws2_32',
85
'iphlpapi',
86
'advapi32',
87
'shell32',
88
'netapi32',
89
'crypt32',
90
'wlanapi',
91
'wldap32',
92
'version',
93
'psapi',
94
'dbghelp',
95
'winspool',
96
'spoolss',
97
'secur32'
98
].freeze
99
}.freeze
100
101
##
102
# Returns a Hash containing libraries added to this instance with #add_library
103
# as well as references to any frozen cached libraries added directly in
104
# #get_library and copies of any frozen libraries (added directly with
105
# #add_function) that the user attempted to modify with #add_function.
106
#
107
# Keys are friendly library names and values are the corresponding library instance
108
attr_accessor :libraries
109
110
##
111
# Contains a reference to the client that corresponds to this instance of railgun
112
attr_accessor :client
113
114
##
115
# These libraries are loaded lazily and then shared amongst all railgun
116
# instances. For safety reasons this variable should only be read/written
117
# within #get_library.
118
@@cached_libraries = {}
119
120
# if you are going to touch @@cached_libraries, wear protection
121
@@cache_semaphore = Mutex.new
122
123
def initialize(client)
124
self.client = client
125
self.libraries = {}
126
end
127
128
def self.builtin_libraries
129
BUILTIN_LIBRARIES[client.platform]
130
end
131
132
#
133
# Return this Railgun's Util instance.
134
#
135
def util
136
if @util.nil?
137
@util = Util.new(self, client.native_arch)
138
end
139
140
return @util
141
end
142
143
#
144
# Return this Railgun's platform specific ApiConstants class.
145
#
146
def api_constants
147
if @api_constants.nil?
148
require "rex/post/meterpreter/extensions/stdapi/railgun/def/#{client.platform}/api_constants"
149
@api_constants = Def.const_get('DefApiConstants_' << client.platform)
150
end
151
152
return @api_constants
153
end
154
155
#
156
# Return this Railgun's ConstManager instance, initially populated with
157
# constants defined in ApiConstants.
158
#
159
def constant_manager
160
# Loads lazily
161
return api_constants.manager
162
end
163
164
#
165
# Read data from a memory address on the host (useful for working with
166
# LPVOID parameters)
167
#
168
def memread(address, length)
169
170
raise "Invalid parameters." if(not address or not length)
171
172
request = Packet.create_request(COMMAND_ID_STDAPI_RAILGUN_MEMREAD)
173
174
request.add_tlv(TLV_TYPE_RAILGUN_MEM_ADDRESS, address)
175
request.add_tlv(TLV_TYPE_RAILGUN_MEM_LENGTH, length)
176
177
response = client.send_request(request)
178
if(response.result == 0)
179
return response.get_tlv_value(TLV_TYPE_RAILGUN_MEM_DATA)
180
end
181
182
return nil
183
end
184
185
#
186
# Write data to a memory address on the host (useful for working with
187
# LPVOID parameters)
188
#
189
def memwrite(address, data, length=nil)
190
data = data.to_binary_s if data.is_a?(BinData::Struct)
191
length = data.length if length.nil?
192
raise "Invalid parameters." if(not address or not data or not length)
193
194
request = Packet.create_request(COMMAND_ID_STDAPI_RAILGUN_MEMWRITE)
195
request.add_tlv(TLV_TYPE_RAILGUN_MEM_ADDRESS, address)
196
request.add_tlv(TLV_TYPE_RAILGUN_MEM_DATA, data)
197
request.add_tlv(TLV_TYPE_RAILGUN_MEM_LENGTH, length)
198
199
response = client.send_request(request)
200
return response.result == 0
201
end
202
203
#
204
# Adds a function to an existing library definition.
205
#
206
# If the library definition is frozen (ideally this should be the case for all
207
# cached libraries) an unfrozen copy is created and used henceforth for this
208
# instance.
209
#
210
def add_function(lib_name, function_name, return_type, params, remote_name=nil, calling_conv='stdcall')
211
unless known_library_names.include?(lib_name)
212
raise "Library #{lib_name} not found. Known libraries: #{PP.pp(known_library_names, '')}"
213
end
214
215
lib = get_library(lib_name)
216
217
# For backwards compatibility, we ensure the library is thawed
218
if lib.frozen?
219
# Duplicate not only the library, but its functions as well, frozen status will be lost
220
lib = Marshal.load(Marshal.dump(lib))
221
222
# Update local libraries with the modifiable duplicate
223
libraries[lib_name] = lib
224
end
225
226
lib.add_function(function_name, return_type, params, remote_name, calling_conv)
227
end
228
229
#
230
# Adds a library to this Railgun.
231
#
232
# The +remote_name+ is the name used on the remote system and should be
233
# set appropriately if you want to include a path or the library name contains
234
# non-ruby-approved characters.
235
#
236
# Raises an exception if a library with the given name has already been
237
# defined.
238
#
239
def add_library(lib_name, remote_name=lib_name)
240
if libraries.has_key? lib_name
241
raise "A library of name #{lib_name} has already been loaded."
242
end
243
244
libraries[lib_name] = Library.new(remote_name, constant_manager)
245
end
246
alias_method :add_dll, :add_library
247
248
def known_library_names
249
return BUILTIN_LIBRARIES[client.platform] | libraries.keys
250
end
251
252
#
253
# Attempts to provide a library instance of the given name. Handles lazy
254
# loading and caching. Note that if a library of the given name does not exist
255
# then nil is returned.
256
#
257
def get_library(lib_name)
258
# If the library is not local, we now either load it from cache or load it
259
# lazily. In either case, a reference to the library is stored in the
260
# collection "libraries". If the library can not be found/created, no
261
# actions are taken.
262
unless libraries.has_key? lib_name
263
# use a platform-specific name for caching to avoid conflicts with
264
# libraries that exist on multiple platforms, e.g. libc.
265
cached_lib_name = "#{client.platform}.#{lib_name}"
266
# We read and write to @@cached_libraries and rely on state consistency
267
@@cache_semaphore.synchronize do
268
if @@cached_libraries.has_key? cached_lib_name
269
libraries[lib_name] = @@cached_libraries[cached_lib_name]
270
elsif BUILTIN_LIBRARIES[client.platform].include? lib_name
271
# I highly doubt this case will ever occur, but I am paranoid
272
if lib_name !~ /^\w+$/
273
raise "Library name #{lib_name} is bad. Correct Railgun::BUILTIN_LIBRARIES['#{client.platform}']"
274
end
275
276
require "rex/post/meterpreter/extensions/stdapi/railgun/def/#{client.platform}/def_#{lib_name}"
277
lib = Def.const_get("Def_#{client.platform}_#{lib_name}").create_library(constant_manager).freeze
278
279
@@cached_libraries[cached_lib_name] = lib
280
libraries[lib_name] = lib
281
end
282
end
283
284
end
285
286
return libraries[lib_name]
287
end
288
alias_method :get_dll, :get_library
289
290
#
291
# Fake having members like user32 and kernel32.
292
# reason is that
293
# ...user32.MessageBoxW()
294
# is prettier than
295
# ...libraries["user32"].functions["MessageBoxW"]()
296
#
297
def method_missing(lib_symbol, *args)
298
lib_name = lib_symbol.to_s
299
300
unless known_library_names.include? lib_name
301
raise "Library #{lib_name} not found. Known libraries: #{PP.pp(known_library_names, '')}"
302
end
303
304
lib = get_library(lib_name)
305
306
return LibraryWrapper.new(lib, client)
307
end
308
309
#
310
# Return a constant matching +str+.
311
#
312
def const(str)
313
return constant_manager.parse(str)
314
end
315
316
#
317
# The multi-call shorthand (["kernel32", "ExitProcess", [0]])
318
#
319
def multi(functions)
320
if @multicaller.nil?
321
@multicaller = MultiCaller.new(client, self, constant_manager)
322
end
323
324
return @multicaller.call(functions)
325
end
326
end
327
328
end; end; end; end; end; end
329
330