#1# Copyright (c) 2006-2025 Wade Alcorn - [email protected]2# Browser Exploitation Framework (BeEF) - https://beefproject.com3# See the file 'doc/COPYING' for copying permission4#56module BeEF7module API8#9# Registrar class to handle all registered timed API calls10#11class Registrar12include Singleton1314#15# Create registrar16#17def initialize18@registry = []19@count = 120end2122# Register timed API calls to an owner23#24# @param [Class] owner the owner of the API hook25# @param [Class] clss the API class the owner would like to hook into26# @param [String] method the method of the class the owner would like to execute27# @param [Array] params an array of parameters that need to be matched before the owner will be called28#29def register(owner, clss, method, params = [])30unless verify_api_path(clss, method)31print_error "API Registrar: Attempted to register non-existant API method #{clss} :#{method}"32return33end3435if registered?(owner, clss, method, params)36print_debug "API Registrar: Attempting to re-register API call #{clss} :#{method}"37return38end3940id = @count41@registry << {42'id' => id,43'owner' => owner,44'class' => clss,45'method' => method,46'params' => params47}48@count += 14950id51end5253#54# Tests whether the owner is registered for an API hook55#56# @param [Class] owner the owner of the API hook57# @param [Class] clss the API class58# @param [String] method the method of the class59# @param [Array] params an array of parameters that need to be matched60#61# @return [Boolean] whether or not the owner is registered62#63def registered?(owner, clss, method, params = [])64@registry.each do |r|65next unless r['owner'] == owner66next unless r['class'] == clss67next unless r['method'] == method68next unless is_matched_params? r, params6970return true71end72false73end7475#76# Match a timed API call to determine if an API.fire() is required77#78# @param [Class] clss the target API class79# @param [String] method the method of the target API class80# @param [Array] params an array of parameters that need to be matched81#82# @return [Boolean] whether or not the arguments match an entry in the API registry83#84def matched?(clss, method, params = [])85@registry.each do |r|86next unless r['class'] == clss87next unless r['method'] == method88next unless is_matched_params? r, params8990return true91end92false93end9495#96# Un-registers an API hook97#98# @param [Integer] id the ID of the API hook99#100def unregister(id)101@registry.delete_if { |r| r['id'] == id }102end103104#105# Retrieves all the owners and ID's of an API hook106# @param [Class] clss the target API class107# @param [String] method the method of the target API class108# @param [Array] params an array of parameters that need to be matched109#110# @return [Array] an array of hashes consisting of two keys :owner and :id111#112def get_owners(clss, method, params = [])113owners = []114@registry.each do |r|115next unless r['class'] == clss116next unless r['method'] == method117next unless is_matched_params? r, params118119owners << { owner: r['owner'], id: r['id'] }120end121owners122end123124#125# Verifies that the api_path has been regitered126# Verifies the API path has been registered.127#128# @note This is a security precaution129#130# @param [Class] clss the target API class to verify131# @param [String] mthd the target method to verify132#133def verify_api_path(clss, mthd)134(clss.const_defined?('API_PATHS') && clss.const_get('API_PATHS').key?(mthd))135end136137#138# Retrieves the registered symbol reference for an API hook139#140# @param [Class] clss the target API class to verify141# @param [String] mthd the target method to verify142#143# @return [Symbol] the API path144#145def get_api_path(clss, mthd)146verify_api_path(clss, mthd) ? clss.const_get('API_PATHS')[mthd] : nil147end148149#150# Matches stored API params to params151#152# @note If a stored API parameter has a NilClass the parameter matching is skipped for that parameter153# @note By default this method returns true, this is either because the API.fire() did not include any parameters or there were no parameters defined for this registry entry154#155# @param [Hash] reg hash of registry element, must contain 'params' key156# @param [Array] params array of parameters to be compared to the stored parameters157#158# @return [Boolean] whether params matches the stored API parameters159#160def is_matched_params?(reg, params)161stored = reg['params']162return true unless stored.length == params.length163164stored.each_index do |i|165next if stored[i].nil?166return false unless stored[i] == params[i]167end168169true170end171172#173# Fires all owners registered to this API hook174#175# @param [Class] clss the target API class176# @param [String] mthd the target API method177# @param [Array] *args parameters passed for the API call178#179# @return [Hash, NilClass] returns either a Hash of :api_id and :data180# if the owners return data, otherwise NilClass181#182def fire(clss, mthd, *args)183mods = get_owners(clss, mthd, args)184return nil unless mods.length.positive?185186unless verify_api_path(clss, mthd) && clss.ancestors.first.to_s.start_with?('BeEF::API')187print_error "API Path not defined for Class: #{clss} method: #{mthd}"188return []189end190191data = []192method = get_api_path(clss, mthd)193mods.each do |mod|194# Only used for API Development (very verbose)195# print_info "API: #{mod} fired #{method}"196197result = mod[:owner].method(method).call(*args)198data << { api_id: mod[:id], data: result } unless result.nil?199rescue StandardError => e200print_error "API Fire Error: #{e.message} in #{mod}.#{method}()"201end202203data204end205end206end207end208209require 'core/api/module'210require 'core/api/modules'211require 'core/api/extension'212require 'core/api/extensions'213require 'core/api/main/migration'214require 'core/api/main/network_stack/assethandler'215require 'core/api/main/server'216require 'core/api/main/server/hook'217require 'core/api/main/configuration'218219220