Skip to content

Commit

Permalink
Add history support to nasm and metasm shells
Browse files Browse the repository at this point in the history
  • Loading branch information
adfoster-r7 committed Oct 4, 2023
1 parent e70f356 commit 1f60093
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 17 deletions.
28 changes: 20 additions & 8 deletions lib/rex/ui/text/shell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,8 @@ def run(&block)
# Pry is a development dependency, if not available suppressing history_load can be safely ignored.
end

framework.history_manager.with_context(history_file: histfile, name: name) do
self.hist_last_saved = Readline::HISTORY.length

with_history_manager_context do
begin

while true
# If the stop flag was set or we've hit EOF, break out
break if self.stop_flag || self.stop_count > 1
Expand Down Expand Up @@ -168,17 +165,13 @@ def run(&block)
run_single(line)
self.stop_count = 0
end

end
# Prevent accidental console quits
rescue ::Interrupt
output.print("Interrupt: use the 'exit' command to quit\n")
retry
end
end
ensure
framework.history_manager.flush
self.hist_last_saved = Readline::HISTORY.length
end

#
Expand Down Expand Up @@ -298,10 +291,29 @@ def print(msg='')
attr_accessor :on_command_proc
attr_accessor :on_print_proc
attr_accessor :framework
attr_accessor :history_manager
attr_accessor :hist_last_saved # the number of history lines when last saved/loaded

protected

# Executes the yielded block under the context of a new HistoryManager context. The shell's history will be flushed
# to disk when no longer interacting with the shell. If no history manager is available, the history will not be persisted.
def with_history_manager_context
history_manager = self.history_manager || framework&.history_manager
return yield unless history_manager

begin
history_manager.with_context(history_file: histfile, name: name) do
self.hist_last_saved = Readline::HISTORY.length

yield
end
ensure
history_manager.flush
self.hist_last_saved = Readline::HISTORY.length
end
end

def supports_color?
true
end
Expand Down
17 changes: 10 additions & 7 deletions lib/rex/ui/text/shell/history_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ class HistoryManager
def initialize
@contexts = []
@debug = false
# Values dequeued before work is started
@write_queue = ::Queue.new
@currently_processing = ::Queue.new
# Values dequeued after work is completed
@remaining_work = ::Queue.new
end

# Create a new history command context when executing the given block
Expand All @@ -38,7 +40,7 @@ def with_context(history_file: nil, name: nil, &block)

# Flush the contents of the write queue to disk. Blocks synchronously.
def flush
until @write_queue.empty? && @currently_processing.empty?
until @write_queue.empty? && @remaining_work.empty?
sleep 0.1
end

Expand Down Expand Up @@ -134,27 +136,28 @@ def switch_context(new_context, old_context=nil)

def write_history_file(history_file, cmds)
write_queue_ref = @write_queue
currently_processing_ref = @currently_processing
remaining_work_ref = @remaining_work
@write_thread ||= Rex::ThreadFactory.spawn("HistoryManagerWriter", false) do
while (event = write_queue_ref.pop)
begin
currently_processing_ref << event

history_file = event[:history_file]
cmds = event[:cmds]

File.open(history_file, 'wb+') do |f|
f.puts(cmds.reverse)
end

rescue => e
elog(e)
ensure
currently_processing_ref.pop
remaining_work_ref.pop
end
end
end

write_queue_ref << { type: :write, history_file: history_file, cmds: cmds }
event = { type: :write, history_file: history_file, cmds: cmds }
@write_queue << event
@remaining_work << event
end
end

Expand Down
4 changes: 3 additions & 1 deletion tools/exploit/metasm_shell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,10 @@ def parse_gas_file(filename)

# Start a pseudo shell and dispatch lines to be assembled and then
# disassembled.
shell = Rex::Ui::Text::PseudoShell.new("%bldmetasm%clr")
history_file = File.join(Msf::Config.config_directory, 'metasm_history')
shell = Rex::Ui::Text::PseudoShell.new("%bldmetasm%clr", '>', history_file)
shell.init_ui(Rex::Ui::Text::Input::Stdio.new, Rex::Ui::Text::Output::Stdio.new)
shell.history_manager = Rex::Ui::Text::Shell::HistoryManager.new

puts [
'type "exit" or "quit" to quit',
Expand Down
4 changes: 3 additions & 1 deletion tools/exploit/nasm_shell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@

# Start a pseudo shell and dispatch lines to be assembled and then
# disassembled.
shell = Rex::Ui::Text::PseudoShell.new("%bldnasm%clr")
history_file = File.join(Msf::Config.config_directory, 'nasm_history')
shell = Rex::Ui::Text::PseudoShell.new("%bldnasm%clr", '>', history_file)
shell.init_ui(Rex::Ui::Text::Input::Stdio.new, Rex::Ui::Text::Output::Stdio.new)
shell.history_manager = Rex::Ui::Text::Shell::HistoryManager.new

shell.run { |line|
line.gsub!(/(\r|\n)/, '')
Expand Down

0 comments on commit 1f60093

Please sign in to comment.