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'
].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

Instance Method Summary collapse

Constructor Details

#initialize(client) ⇒ Railgun

Returns a new instance of Railgun.



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

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"]()


261
262
263
264
265
266
267
268
269
270
271
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 261

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



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

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



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

def dlls
  @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.



206
207
208
209
210
211
212
213
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 206

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.



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 176

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?
		# dup will copy values, but not the frozen status
		dll = dll.dup

		# 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.



276
277
278
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 276

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

#constant_managerObject

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



121
122
123
124
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 121

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



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 225

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



216
217
218
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 216

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)



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 130

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)



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 151

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]])



283
284
285
286
287
288
289
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 283

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

	return @multicaller.call(functions)
end

#utilObject

Return this Railgun’s Util instance.



109
110
111
112
113
114
115
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 109

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

	return @util
end