diff --git a/lib/msf/ui/console/driver.rb b/lib/msf/ui/console/driver.rb index 89722bc9f5..488378b83e 100644 --- a/lib/msf/ui/console/driver.rb +++ b/lib/msf/ui/console/driver.rb @@ -48,8 +48,13 @@ class Driver < Msf::Ui::Driver # def initialize(prompt = DefaultPrompt, prompt_char = DefaultPromptChar, opts = {}) - # The command prompt doesn't like bling bling'in colors. + # Windows-specific hackery if (RUBY_PLATFORM =~ /win/) + + # Start the readline console hack + Rex::Compat.win32_readline_daemon() + + # Disable the color support prompt = "msf" prompt_char = ">" end diff --git a/lib/rex.rb b/lib/rex.rb index d76ac341d5..f412795b01 100644 --- a/lib/rex.rb +++ b/lib/rex.rb @@ -47,6 +47,10 @@ require 'rex/proto' require 'rex/parser/arguments' require 'rex/parser/ini' + +# Compatibility +require 'rex/compat' + # Overload the Kernel.sleep() function to be thread-safe Kernel.class_eval(" def sleep(seconds) diff --git a/lib/rex/compat.rb b/lib/rex/compat.rb new file mode 100644 index 0000000000..91f9f68a55 --- /dev/null +++ b/lib/rex/compat.rb @@ -0,0 +1,112 @@ +require 'dl' + +module Rex + +### +# +# This class provides os-specific functionality +# +### +module Compat + +STD_INPUT_HANDLE = -10 +ENABLE_LINE_INPUT = 2 +ENABLE_ECHO_INPUT = 4 +ENABLE_PROCESSED_INPUT = 1 + +def self.win32_stdin_unblock + begin + @@k32 ||= DL.dlopen("kernel32.dll") + gsh = @@k32['GetStdHandle', 'LL'] + gcm = @@k32['GetConsoleMode', 'LLP'] + scm = @@k32['SetConsoleMode', 'LLL'] + + inp = gsh.call(STD_INPUT_HANDLE)[0] + inf = DL.malloc(DL.sizeof('L')) + gcm.call(inp, inf) + old_mode = inf.to_a('L', 1)[0] + new_mode = old_mode & ~(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT) + scm.call(inp, new_mode) + + rescue ::Exception + raise $! + end +end + +def self.win32_stdin_block + begin + @@k32 ||= DL.dlopen("kernel32.dll") + gsh = @@k32['GetStdHandle', 'LL'] + gcm = @@k32['GetConsoleMode', 'LLP'] + scm = @@k32['SetConsoleMode', 'LLL'] + + inp = gsh.call(STD_INPUT_HANDLE)[0] + inf = DL.malloc(DL.sizeof('L')) + gcm.call(inp, inf) + old_mode = inf.to_a('L', 1)[0] + new_mode = old_mode | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT + scm.call(inp, new_mode) + + rescue ::Exception + raise $! + end +end + +def self.win32_ruby_path + begin + @@k32 ||= DL.dlopen("kernel32.dll") + gmh = @@k32['GetModuleHandle', 'LP'] + gmf = @@k32['GetModuleFileName', 'LLPL'] + + mod = gmh.call(nil)[0] + inf = DL.malloc(1024) + + gmf.call(mod, inf, 1024) + return inf.to_s + + rescue ::Exception + raise $! + end +end + +def self.win32_winexec(cmd) + begin + @@k32 ||= DL.dlopen("kernel32.dll") + win = @@k32['WinExec', 'LPL'] + win.call(cmd.to_ptr, 0) + rescue ::Exception + raise $! + end +end + +def self.win32_readline_daemon + serv = nil + port = 1024 + + while (! serv and port < 65535) + begin + serv = TCPServer.new('127.0.0.1', (port += 1)) + rescue ::Exception + end + end + + path = win32_ruby_path() + rubyw = File.join(File.dirname(path.to_s), "ruby.exe") + helpr = File.join(File.dirname(__FILE__), 'win32_stdio.rb') + + win32_winexec( [rubyw, helpr, port.to_s].join(" ") ) + + # Accept the forked child + clnt = serv.accept + + # Shutdown the server + serv.close + + # Replace stdin with the socket + $stdin.close + $stdin = clnt +end + +end +end + diff --git a/lib/rex/win32_stdio.rb b/lib/rex/win32_stdio.rb new file mode 100644 index 0000000000..a14aa67fda --- /dev/null +++ b/lib/rex/win32_stdio.rb @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby + + +require 'dl' +require 'socket' + +$:.push(File.dirname(__FILE__)) +require 'compat' + +port = ARGV.shift() || exit(0) + + +# This script is used to provide async stdio on Windows +begin + sock = TCPSocket.new('127.0.0.1', port) + + Rex::Compat.win32_stdin_unblock() + + $stderr.puts "Starting stdio daemon..." + + while (true) + c = $stdin.sysread(1) + $stderr.printf("%.2x \n", c[0]) + sock.write(c) + sock.flush + end +rescue ::Exception + $stderr.puts "Exception: #{$!.to_s}" +end + +Rex::Compat.win32_stdin_block()