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.



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.



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.



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.



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.



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