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.

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',
  'wldap32',
  'version',
  'psapi'
].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.



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

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


270
271
272
273
274
275
276
277
278
279
280
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 270

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



96
97
98
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 96

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



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

def dlls
  @dlls
end

Class Method Details

.builtin_dllsObject



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

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.



215
216
217
218
219
220
221
222
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 215

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, calling_conv = "stdcall") ⇒ 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.



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

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

  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, calling_conv)
end

#const(str) ⇒ Object

Return a Windows constant matching str.



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

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

#constant_managerObject

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



130
131
132
133
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 130

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



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
259
260
261
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 234

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



225
226
227
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 225

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)



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

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)



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

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



292
293
294
295
296
297
298
# File 'lib/rex/post/meterpreter/extensions/stdapi/railgun/railgun.rb', line 292

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

  return @multicaller.call(functions)
end

#utilObject

Return this Railgun’s Util instance.



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

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

  return @util
end