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


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

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

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

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


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

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


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

def dlls
  @dlls
end

Class Method Details

.builtin_dllsObject


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

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.


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

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.


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

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.


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

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

#constant_managerObject

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


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

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


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

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


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

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)


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

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)


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

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


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

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.


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

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

  return @util
end