diff --git a/lib/msf/ui/console/command_dispatcher/developer.rb b/lib/msf/ui/console/command_dispatcher/developer.rb index c18505e42f..b23bb7f4b1 100644 --- a/lib/msf/ui/console/command_dispatcher/developer.rb +++ b/lib/msf/ui/console/command_dispatcher/developer.rb @@ -5,8 +5,9 @@ class Msf::Ui::Console::CommandDispatcher::Developer include Msf::Ui::Console::CommandDispatcher @@irb_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner." ], - "-e" => [ true, "Expression to evaluate." ]) + '-h' => [false, 'Help menu.' ], + '-e' => [true, 'Expression to evaluate.'] + ) def initialize(driver) super @@ -18,11 +19,11 @@ class Msf::Ui::Console::CommandDispatcher::Developer def commands { - 'irb' => 'Drop into irb scripting mode', - 'pry' => 'Open a Pry session on the current module or Framework', + 'irb' => 'Open an interactive Ruby shell in the current context', + 'pry' => 'Open the Pry debugger on the current module or Framework', 'edit' => 'Edit the current module or a file with the preferred editor', - 'reload_lib' => 'Reload one or more library files from specified paths', - 'log' => 'Displays framework.log starting at the bottom if possible' + 'reload_lib' => 'Reload Ruby library files from specified paths', + 'log' => 'Display framework.log paged to the end if possible' } end @@ -35,31 +36,52 @@ class Msf::Ui::Console::CommandDispatcher::Developer end # XXX: This will try to reload *any* .rb and break on modules - def reload_file(path) - unless File.exist?(path) && path.end_with?('.rb') - print_error("#{path} must exist and be a .rb file") + def reload_file(path, print_errors: true) + full_path = File.expand_path(path) + + unless File.exist?(full_path) && full_path.end_with?('.rb') + print_error("#{full_path} must exist and be a .rb file") if print_errors return end # The file must exist to reach this, so we try our best here - if path =~ %r{^(?:\./)?modules/} - print_error("Reloading Metasploit modules is not supported (try 'reload')") + if full_path.start_with?(Msf::Config.module_directory, Msf::Config.user_module_directory) + print_error('Reloading Metasploit modules is not supported (try "reload")') if print_errors return end - print_status("Reloading #{path}") - load path + print_status("Reloading #{full_path}") + load full_path + end + + def reload_changed_files + # Using an array avoids shelling out, so we avoid escaping/quoting + changed_files = %w[git diff --name-only] + + output, status = Open3.capture2e(*changed_files, chdir: Msf::Config.install_root) + + unless status.success? + print_error("Git is not available: #{output.chomp}") + return + end + + files = output.split("\n") + + files.each do |file| + f = File.join(Msf::Config.install_root, file) + reload_file(file, print_errors: false) + end end def cmd_irb_help - print_line "Usage: irb" + print_line 'Usage: irb' print_line - print_line "Execute commands in a Ruby environment" + print_line 'Open an interactive Ruby shell in the current context.' print @@irb_opts.usage end # - # Goes into IRB scripting mode + # Open an interactive Ruby shell in the current context # def cmd_irb(*args) expressions = [] @@ -76,10 +98,16 @@ class Msf::Ui::Console::CommandDispatcher::Developer end if expressions.empty? - print_status("Starting IRB shell...\n") + print_status('Starting IRB shell...') begin - Rex::Ui::Text::IrbShell.new(binding).run + if active_module + print_status("You are in #{active_module.fullname}\n") + Rex::Ui::Text::IrbShell.new(active_module).run + else + print_status("You are in the \"framework\" object\n") + Rex::Ui::Text::IrbShell.new(framework).run + end rescue print_error("Error during IRB: #{$!}\n\n#{$@.join("\n")}") end @@ -89,6 +117,11 @@ class Msf::Ui::Console::CommandDispatcher::Developer driver.input.reset_tab_completion end else + # XXX: No vprint_status here either + if framework.datastore['VERBOSE'].to_s == 'true' + print_status("You are executing expressions in #{binding.receiver}") + end + expressions.each { |expression| eval(expression, binding) } end end @@ -104,12 +137,12 @@ class Msf::Ui::Console::CommandDispatcher::Developer def cmd_pry_help print_line 'Usage: pry' print_line - print_line 'Open a Pry session on the current module or Framework.' + print_line 'Open the Pry debugger on the current module or Framework.' print_line end # - # Open a Pry session on the current module or Framework + # Open the Pry debugger on the current module or Framework # def cmd_pry(*args) if args.include?('-h') @@ -141,7 +174,7 @@ class Msf::Ui::Console::CommandDispatcher::Developer print_line print_line "Edit the currently active module or a local file with #{local_editor}." print_line 'If a library file is specified, it will automatically be reloaded after editing.' - print_line "Otherwise, you can reload the active module with 'reload' or 'rerun'." + print_line 'Otherwise, you can reload the active module with "reload" or "rerun".' print_line end @@ -192,22 +225,32 @@ class Msf::Ui::Console::CommandDispatcher::Developer end def cmd_reload_lib_help - print_line 'Usage: reload_lib lib/to/reload.rb [...]' - print_line - print_line 'Reload one or more library files from specified paths.' - print_line + cmd_reload_lib('-h') end # - # Reload one or more library files from specified paths + # Reload Ruby library files from specified paths # def cmd_reload_lib(*args) - if args.empty? || args.include?('-h') || args.include?('--help') - cmd_reload_lib_help - return + options = OptionParser.new do |opts| + opts.banner = 'Usage: reload_lib lib/to/reload.rb [...]' + opts.separator '' + opts.separator 'Reload Ruby library files from specified paths.' + opts.separator '' + + opts.on '-h', '--help', 'Help banner.' do + return print(opts.help) + end + + opts.on '-a', '--all', 'Reload all* changed files in your current Git working tree. + *Excludes modules and non-Ruby files.' do + reload_changed_files + return + end end - args.each { |path| reload_file(path) } + files = options.order(args) + files.each { |file| reload_file(file) } end # @@ -220,15 +263,15 @@ class Msf::Ui::Console::CommandDispatcher::Developer def cmd_log_help print_line 'Usage: log' print_line - print_line 'Displays framework.log starting at the bottom if possible.' - print_line "For full effect, 'setg LogLevel 3' before running modules." + print_line 'Display framework.log paged to the end if possible.' + print_line 'For full effect, "setg LogLevel 3" before running modules.' print_line print_line "Log location: #{File.join(Msf::Config.log_directory, 'framework.log')}" print_line end # - # Displays framework.log starting at the bottom if possible + # Display framework.log paged to the end if possible # def cmd_log(*args) path = File.join(Msf::Config.log_directory, 'framework.log') diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb index 352f6b6865..83428ec73a 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -34,12 +34,14 @@ class Console::CommandDispatcher::Core end @@irb_opts = Rex::Parser::Arguments.new( - '-h' => [false, 'Help banner.'], - '-e' => [true, 'Expression to evaluate.']) + '-h' => [false, 'Help menu.' ], + '-e' => [true, 'Expression to evaluate.'] + ) @@load_opts = Rex::Parser::Arguments.new( - '-l' => [false, 'List all available extensions'], - '-h' => [false, 'Help menu.']) + '-h' => [false, 'Help menu.' ], + '-l' => [false, 'List all available extensions.'] + ) # # List of supported commands. @@ -52,8 +54,8 @@ class Console::CommandDispatcher::Core 'channel' => 'Displays information or control active channels', 'exit' => 'Terminate the meterpreter session', 'help' => 'Help menu', - 'irb' => 'Drop into irb scripting mode', - 'pry' => 'Open a Pry session on the current session', + 'irb' => 'Open an interactive Ruby shell on the current session', + 'pry' => 'Open the Pry debugger on the current session', 'use' => 'Deprecated alias for "load"', 'load' => 'Load one or more meterpreter extensions', 'machine_id' => 'Get the MSF ID of the machine attached to the session', @@ -532,7 +534,7 @@ class Console::CommandDispatcher::Core def cmd_irb_help print_line('Usage: irb') print_line - print_line('Execute commands in a Ruby environment') + print_line('Open an interactive Ruby shell on the current session.') print @@irb_opts.usage end @@ -542,7 +544,7 @@ class Console::CommandDispatcher::Core end # - # Runs the IRB scripting shell + # Open an interactive Ruby shell on the current session # def cmd_irb(*args) expressions = [] @@ -561,12 +563,16 @@ class Console::CommandDispatcher::Core framework = client.framework if expressions.empty? - print_status('Starting IRB shell') - print_status('The "client" variable holds the meterpreter client') - print_line + print_status('Starting IRB shell...') + print_status("You are in the \"client\" (session) object\n") - Rex::Ui::Text::IrbShell.new(binding).run + Rex::Ui::Text::IrbShell.new(client).run else + # XXX: No vprint_status here + if framework.datastore['VERBOSE'].to_s == 'true' + print_status("You are executing expressions in #{binding.receiver}") + end + expressions.each { |expression| eval(expression, binding) } end end @@ -574,12 +580,12 @@ class Console::CommandDispatcher::Core def cmd_pry_help print_line 'Usage: pry' print_line - print_line 'Open a Pry session on the current session.' + print_line 'Open the Pry debugger on the current session.' print_line end # - # Open a Pry session on the current session + # Open the Pry debugger on the current session # def cmd_pry(*args) if args.include?('-h')