Class: Rex::Post::Meterpreter::CommandMapper

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/post/meterpreter/command_mapper.rb

Constant Summary collapse

@@cached_tlv_types =
{}

Class Method Summary collapse

Class Method Details

.get_command_id(name) ⇒ Integer?

Get the numeric command ID for the specified command name.

Parameters:

  • name (String)

    The name of the command to retrieve the ID for. This parameter is case insensitive.

Returns:

  • (Integer, nil)

    The command ID or nil if the name does not exist.

[View source]

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/rex/post/meterpreter/command_mapper.rb', line 19

def self.get_command_id(name)
  name = name.downcase

  return nil unless name.include?('_')

  mod_name, cmd_name = name.split('_', 2)
  if mod_name == 'core'
    mod = Rex::Post::Meterpreter
  else
    mod = Rex::Post::Meterpreter::ExtensionMapper.get_extension_module(mod_name)
  end

  return nil unless mod

  const_name = "COMMAND_ID_#{mod_name.upcase}_#{cmd_name.upcase}"
  return nil unless mod.const_defined?(const_name)

  mod.const_get(const_name)
end

.get_command_name(id) ⇒ String?

Get the string command name for the specified command ID.

Parameters:

  • id (Integer)

    The ID of the command to retrieve the name for.

Returns:

  • (String, nil)

    The command name or nil if the ID does not exist.

[View source]

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/rex/post/meterpreter/command_mapper.rb', line 43

def self.get_command_name(id)
  extension_id = id - (id % COMMAND_ID_RANGE)
  if extension_id == Rex::Post::Meterpreter::ClientCore.extension_id  # this is the meterpreter core which is not exactly an extension.
    mod = Rex::Post::Meterpreter
  else
    mod_name = Rex::Post::Meterpreter::ExtensionMapper.get_extension_name(extension_id)
    mod = Rex::Post::Meterpreter::ExtensionMapper.get_extension_module(mod_name)
  end

  return nil unless mod

  command_name = mod.constants.select { |c| c.to_s.start_with?('COMMAND_ID_') }.find { |c| id == mod.const_get(c) }

  return nil unless command_name

  command_name.to_s.delete_prefix('COMMAND_ID_').downcase
end

.get_command_names(*extensions) ⇒ Array<String>

Get all of the string command names for the specified extensions.

Parameters:

  • extensions (Array<String>)

    The names of the extensions to retrieve all of the command names for. The extension names are case insensitive. If no extensions are specified, all extensions will be enumerated.

Returns:

  • (Array<String>)

    An array of all of the enumerated command names.

[View source]

67
68
69
# File 'lib/rex/post/meterpreter/command_mapper.rb', line 67

def self.get_command_names(*extensions)
  self.get_commands(*extensions).keys
end

.get_commands(*extensions) ⇒ Hash<String, Integer>

Get a hash of all command name strings mapped to their numeric IDs.

Parameters:

  • extensions (Array<String>)

    The names of the extensions to retrieve all of the commands for. The extension names are case insensitive. If no extensions are specified, all extensions will be enumerated.

Returns:

  • (Hash<String, Integer>)

    An hash of all of the enumerated commands.

[View source]

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/rex/post/meterpreter/command_mapper.rb', line 77

def self.get_commands(*extensions)
  extensions = ['core'] + Rex::Post::Meterpreter::ExtensionMapper.get_extension_names if extensions.empty?

  commands = {}
  extensions.each do |mod_name|
    mod_name = mod_name.downcase

    if mod_name == 'core'
      mod = Rex::Post::Meterpreter
    else
      begin
        mod = Rex::Post::Meterpreter::ExtensionMapper.get_extension_module(mod_name)
      rescue RuntimeError
        next
      end
    end

    constants = mod.constants.select { |name| name.to_s.start_with?("COMMAND_ID_#{mod_name.upcase}") }
    commands.merge!(constants.map { |name| [name.to_s.delete_prefix('COMMAND_ID_').downcase, mod.const_get(name)] }.to_h)
  end

  commands
end

.get_tlv_names(value) ⇒ Array<Symbol>

Get the TLV Type symbols that are defined with the value Potential return values are [], [:TLV_TYPE_A], and [:TLV_TYPE_A, :PACKET_TYPE_B]

Returning an array is a solution to having multiple TLV types having the same value, as documented here: github.com/rapid7/metasploit-framework/issues/16267

Parameters:

  • Integer

    value The value of the TLV type to retrieve the TLV type names for.

Returns:

  • (Array<Symbol>)

    An array of symbols of all TLV types that are defined with the value. Can be empty.

[View source]

110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/rex/post/meterpreter/command_mapper.rb', line 110

def self.get_tlv_names(value)
  return @@cached_tlv_types[value] unless @@cached_tlv_types[value].nil? || @@cached_tlv_types[value].empty?

  # Default to arrays that contain TLV Types, so that we only deal with one data type
  @@cached_tlv_types = Hash.new { |h, k| h[k] = Set.new }

  available_modules = [
    ::Rex::Post::Meterpreter,
    *::Rex::Post::Meterpreter::ExtensionMapper.get_extension_klasses,
    # Railgun is a special case that defines extra TLV_TYPES inside an extension
    Rex::Post::Meterpreter::Extensions::Stdapi::Railgun
  ].uniq

  available_modules.each do |mod|
    mod.constants.each do |const|
      next unless const.to_s.start_with?('TLV_TYPE_') || const.to_s.start_with?('PACKET_')

      @@cached_tlv_types[mod.const_get(const)] << const
    end
  end

  @@cached_tlv_types[value]
end