Class: Rex::Post::Meterpreter::Extensions::Stdapi::Railgun::Railgun

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb

Overview

The Railgun class to dynamically expose the Windows API.

Defined Under Namespace

Classes: UnitTest

Constant Summary collapse

BUILTIN_DLLS =

Railgun::DLL’s that have builtin definitions.

If you want to add additional DLL definitions to be preloaded create a definition class ‘rex/post/meterpreter/extensions/stdapi/railgun/def/’. Naming is important and should follow convention. For example, if your dll’s name was “my_dll” file name: def_my_dll.rb class name: Def_my_dll entry below: ‘my_dll’

[
	'kernel32',
	'ntdll',
	'user32',
	'ws2_32',
	'iphlpapi',
	'advapi32',
	'shell32',
	'netapi32',
	'crypt32',
	'wlanapi',
].freeze
@@cached_dlls =

These DLLs are loaded lazily and then shared amongst all railgun instances. For safety reasons this variable should only be read/written within #get_dll.

{}
@@cache_semaphore =

if you are going to touch @@cached_dlls, wear protection

Mutex.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(client) ⇒ Railgun

Returns a new instance of Railgun.



103
104
105
106
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 103

def initialize(client)
	self.client = client
	self.dlls = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(dll_symbol, *args) ⇒ Object

Fake having members like user32 and kernel32. reason is that

...user32.MessageBoxW()

is prettier than

...dlls["user32"].functions["MessageBoxW"]()


267
268
269
270
271
272
273
274
275
276
277
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 267

def method_missing(dll_symbol, *args)
	dll_name = dll_symbol.to_s

	unless known_dll_names.include? dll_name
		raise "DLL #{dll_name} not found. Known DLLs: #{PP.pp(known_dll_names, '')}"
	end

	dll = get_dll(dll_name)

	return DLLWrapper.new(dll, client)
end

Instance Attribute Details

#clientObject

Contains a reference to the client that corresponds to this instance of railgun



93
94
95
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 93

def client
  @client
end

#dllsObject

Returns a Hash containing DLLs added to this instance with #add_dll as well as references to any frozen cached dlls added directly in #get_dll and copies of any frozen dlls (added directly with #add_function) that the user attempted to modify with #add_function.

Keys are friendly DLL names and values are the corresponding DLL instance



89
90
91
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 89

def dlls
  @dlls
end

Class Method Details

.builtin_dllsObject



108
109
110
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 108

def self.builtin_dlls
	BUILTIN_DLLS
end

Instance Method Details

#add_dll(dll_name, windows_name = dll_name) ⇒ Object

Adds a DLL to this Railgun.

The windows_name is the name used on the remote system and should be set appropriately if you want to include a path or the DLL name contains non-ruby-approved characters.

Raises an exception if a dll with the given name has already been defined.



212
213
214
215
216
217
218
219
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 212

def add_dll(dll_name, windows_name=dll_name)

	if dlls.has_key? dll_name
		raise "A DLL of name #{dll_name} has already been loaded."
	end

	dlls[dll_name] = DLL.new(windows_name, constant_manager)
end

#add_function(dll_name, function_name, return_type, params, windows_name = nil) ⇒ Object

Adds a function to an existing DLL definition.

If the DLL definition is frozen (ideally this should be the case for all cached dlls) an unfrozen copy is created and used henceforth for this instance.



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 182

def add_function(dll_name, function_name, return_type, params, windows_name=nil)

	unless known_dll_names.include?(dll_name)
		raise "DLL #{dll_name} not found. Known DLLs: #{PP.pp(known_dll_names, "")}"
	end

	dll = get_dll(dll_name)

	# For backwards compatibility, we ensure the dll is thawed
	if dll.frozen?
		# Duplicate not only the dll, but its functions as well. Frozen status will be lost
		dll = Marshal.load(Marshal.dump(dll))

		# Update local dlls with the modifiable duplicate
		dlls[dll_name] = dll
	end

	dll.add_function(function_name, return_type, params, windows_name)
end

#const(str) ⇒ Object

Return a Windows constant matching str.



282
283
284
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 282

def const(str)
	return constant_manager.parse(str)
end

#constant_managerObject

Return this Railgun’s WinConstManager instance, initially populated with constants defined in ApiConstants.



127
128
129
130
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 127

def constant_manager
	# Loads lazily
	return ApiConstants.manager
end

#get_dll(dll_name) ⇒ Object

Attempts to provide a DLL instance of the given name. Handles lazy loading and caching. Note that if a DLL of the given name does not exist, returns nil



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 231

def get_dll(dll_name)

	# If the DLL is not local, we now either load it from cache or load it lazily.
	# In either case, a reference to the dll is stored in the collection "dlls"
	# If the DLL can not be found/created, no actions are taken
	unless dlls.has_key? dll_name
		# We read and write to @@cached_dlls and rely on state consistency
		@@cache_semaphore.synchronize do
			if @@cached_dlls.has_key? dll_name
				dlls[dll_name] = @@cached_dlls[dll_name]
			elsif BUILTIN_DLLS.include? dll_name
				# I highly doubt this case will ever occur, but I am paranoid
				if dll_name !~ /^\w+$/
					raise "DLL name #{dll_name} is bad. Correct Railgun::BUILTIN_DLLS"
				end

				require 'rex/post/meterpreter/extensions/stdapi/railgun/def/def_' << dll_name
				dll = Def.const_get('Def_' << dll_name).create_dll.freeze

				@@cached_dlls[dll_name] = dll
				dlls[dll_name] = dll
			end
		end

	end

	return dlls[dll_name]
end

#known_dll_namesObject



222
223
224
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 222

def known_dll_names
	return BUILTIN_DLLS | dlls.keys
end

#memread(address, length) ⇒ Object

Read data from a memory address on the host (useful for working with LPVOID parameters)



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 136

def memread(address, length)

	raise "Invalid parameters." if(not address or not length)

	request = Packet.create_request('stdapi_railgun_memread')

	request.add_tlv(TLV_TYPE_RAILGUN_MEM_ADDRESS, address)
	request.add_tlv(TLV_TYPE_RAILGUN_MEM_LENGTH, length)

	response = client.send_request(request)
	if(response.result == 0)
		return response.get_tlv_value(TLV_TYPE_RAILGUN_MEM_DATA)
	end

	return nil
end

#memwrite(address, data, length) ⇒ Object

Write data to a memory address on the host (useful for working with LPVOID parameters)



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 157

def memwrite(address, data, length)

	raise "Invalid parameters." if(not address or not data or not length)

	request = Packet.create_request('stdapi_railgun_memwrite')

	request.add_tlv(TLV_TYPE_RAILGUN_MEM_ADDRESS, address)
	request.add_tlv(TLV_TYPE_RAILGUN_MEM_DATA, data)
	request.add_tlv(TLV_TYPE_RAILGUN_MEM_LENGTH, length)

	response = client.send_request(request)
	if(response.result == 0)
		return true
	end

	return false
end

#multi(functions) ⇒ Object

The multi-call shorthand ([“kernel32”, “ExitProcess”, [0]])



289
290
291
292
293
294
295
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 289

def multi(functions)
	if @multicaller.nil?
		@multicaller = MultiCaller.new(client, self)
	end

	return @multicaller.call(functions)
end

#utilObject

Return this Railgun’s Util instance.



115
116
117
118
119
120
121
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 115

def util
	if @util.nil?
		@util = Util.new(self, client.platform)
	end

	return @util
end