Mirror of metasploit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cache.rb 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. # -*- coding: binary -*-
  2. #
  3. # Gems
  4. #
  5. require 'active_support/concern'
  6. # Concerns the module cache maintained by the {Msf::ModuleManager}.
  7. module Msf::ModuleManager::Cache
  8. extend ActiveSupport::Concern
  9. # Returns whether the cache is empty
  10. #
  11. # @return [true] if the cache has no entries.
  12. # @return [false] if the cache has any entries.
  13. def cache_empty?
  14. module_info_by_path.empty?
  15. end
  16. # @note path, reference_name, and type must be passed as options because when +class_or_module+ is a payload Module,
  17. # those attributes will either not be set or not exist on the module.
  18. #
  19. # Updates the in-memory cache so that {#file_changed?} will report +false+ if
  20. # the module is loaded again.
  21. #
  22. # @param class_or_module [Class<Msf::Module>, ::Module] either a module Class
  23. # or a payload Module.
  24. # @param options [Hash{Symbol => String}]
  25. # @option options [String] :path the path to the file from which
  26. # +class_or_module+ was loaded.
  27. # @option options [String] :reference_name the reference name for
  28. # +class_or_module+.
  29. # @option options [String] :type the module type
  30. # @return [void]
  31. # @raise [KeyError] unless +:path+ is given.
  32. # @raise [KeyError] unless +:reference_name+ is given.
  33. # @raise [KeyError] unless +:type+ is given.
  34. def cache_in_memory(class_or_module, options={})
  35. options.assert_valid_keys(:path, :reference_name, :type)
  36. path = options.fetch(:path)
  37. begin
  38. modification_time = File.mtime(path)
  39. rescue Errno::ENOENT => error
  40. log_lines = []
  41. log_lines << "Could not find the modification of time of #{path}:"
  42. log_lines << error.class.to_s
  43. log_lines << error.to_s
  44. log_lines << "Call stack:"
  45. log_lines += error.backtrace
  46. log_message = log_lines.join("\n")
  47. elog(log_message)
  48. else
  49. parent_path = class_or_module.parent.parent_path
  50. reference_name = options.fetch(:reference_name)
  51. type = options.fetch(:type)
  52. module_info_by_path[path] = {
  53. :modification_time => modification_time,
  54. :parent_path => parent_path,
  55. :reference_name => reference_name,
  56. :type => type
  57. }
  58. end
  59. end
  60. # Forces loading of the module with the given type and module reference name from the cache.
  61. #
  62. # @param [String] type the type of the module.
  63. # @param [String] reference_name the module reference name.
  64. # @return [false] if a module with the given type and reference name does not exist in the cache.
  65. # @return (see Msf::Modules::Loader::Base#load_module)
  66. def load_cached_module(type, reference_name)
  67. loaded = false
  68. module_info = self.module_info_by_path.values.find { |inner_info|
  69. inner_info[:type] == type and inner_info[:reference_name] == reference_name
  70. }
  71. if module_info
  72. parent_path = module_info[:parent_path]
  73. loaders.each do |loader|
  74. if loader.loadable?(parent_path)
  75. type = module_info[:type]
  76. reference_name = module_info[:reference_name]
  77. loaded = loader.load_module(parent_path, type, reference_name, :force => true)
  78. break
  79. end
  80. end
  81. end
  82. loaded
  83. end
  84. # @overload refresh_cache_from_module_files
  85. # Rebuilds database and in-memory cache for all modules.
  86. #
  87. # @return [void]
  88. # @overload refresh_cache_from_module_files(module_class_or_instance)
  89. # Rebuilds database and in-memory cache for given module_class_or_instance.
  90. #
  91. # @param (see Msf::DBManager#update_module_details)
  92. # @return [void]
  93. def refresh_cache_from_module_files(module_class_or_instance = nil)
  94. if framework_migrated?
  95. if module_class_or_instance
  96. framework.db.update_module_details(module_class_or_instance)
  97. else
  98. framework.db.update_all_module_details
  99. end
  100. refresh_cache_from_database(self.module_paths)
  101. end
  102. end
  103. # Refreshes the in-memory cache from the database cache.
  104. #
  105. # @return [void]
  106. def refresh_cache_from_database(allowed_paths=[""])
  107. self.module_info_by_path_from_database!(allowed_paths)
  108. end
  109. protected
  110. # Returns whether the framework migrations have been run already.
  111. #
  112. # @return [true] if migrations have been run
  113. # @return [false] otherwise
  114. def framework_migrated?
  115. framework.db && framework.db.migrated
  116. end
  117. # @!attribute [rw] module_info_by_path
  118. # @return (see #module_info_by_path_from_database!)
  119. attr_accessor :module_info_by_path
  120. # Return a module info from Mdm::Module::Details in database.
  121. #
  122. # @note Also sets module_set(module_type)[module_reference_name] to Msf::SymbolicModule if it is not already set.
  123. #
  124. # @return [Hash{String => Hash{Symbol => Object}}] Maps path (Mdm::Module::Detail#file) to module information. Module
  125. # information is a Hash derived from Mdm::Module::Detail. It includes :modification_time, :parent_path, :type,
  126. # :reference_name.
  127. def module_info_by_path_from_database!(allowed_paths=[""])
  128. self.module_info_by_path = {}
  129. if framework_migrated?
  130. allowed_paths = allowed_paths.map{|x| x + "/"}
  131. ActiveRecord::Base.connection_pool.with_connection do
  132. # TODO record module parent_path in Mdm::Module::Detail so it does not need to be derived from file.
  133. # Use find_each so Mdm::Module::Details are returned in batches, which will
  134. # handle the growing number of modules better than all.each.
  135. Mdm::Module::Detail.find_each do |module_detail|
  136. path = module_detail.file
  137. type = module_detail.mtype
  138. reference_name = module_detail.refname
  139. # Skip cached modules that are not in our allowed load paths
  140. next if allowed_paths.select{|x| path.index(x) == 0}.empty?
  141. typed_path = Msf::Modules::Loader::Base.typed_path(type, reference_name)
  142. # join to '' so that typed_path_prefix starts with file separator
  143. typed_path_suffix = File.join('', typed_path)
  144. escaped_typed_path = Regexp.escape(typed_path_suffix)
  145. parent_path = path.gsub(/#{escaped_typed_path}$/, '')
  146. module_info_by_path[path] = {
  147. :reference_name => reference_name,
  148. :type => type,
  149. :parent_path => parent_path,
  150. :modification_time => module_detail.mtime
  151. }
  152. typed_module_set = module_set(type)
  153. # Don't want to trigger as {Msf::ModuleSet#create} so check for
  154. # key instead of using ||= which would call {Msf::ModuleSet#[]}
  155. # which would potentially call {Msf::ModuleSet#create}.
  156. unless typed_module_set.has_key? reference_name
  157. typed_module_set[reference_name] = Msf::SymbolicModule
  158. end
  159. end
  160. end
  161. end
  162. self.module_info_by_path
  163. end
  164. end