require 'open3' module Metasploit module Framework module Compiler module Mingw MINGW_X86 = 'i686-w64-mingw32-gcc' MINGW_X64 = 'x86_64-w64-mingw32-gcc' INCLUDE_DIR = File.join(Msf::Config.data_directory, 'headers', 'windows', 'c_payload_util') UTILITY_DIR = File.join(Msf::Config.data_directory, 'utilities', 'encrypted_payload') OPTIMIZATION_FLAGS = [ 'Os', 'O0', 'O1', 'O2', 'O3', 'Og' ] def compile_c(src) cmd = build_cmd(src) if self.show_compile_cmd print("#{cmd}\n") end stdin_err, status = Open3.capture2e(cmd) stdin_err end def build_cmd(src) src_file = "#{self.file_name}.c" exe_file = "#{self.file_name}.exe" cmd = '' link_options = '-Wl,' File.write(src_file, src) opt_level = OPTIMIZATION_FLAGS.include?(self.opt_lvl) ? "-#{self.opt_lvl} " : "-O2 " cmd << "#{self.mingw_bin} " cmd << "#{src_file} -I #{INCLUDE_DIR} " cmd << "#{self.include_dirs.map { |include_dir| "-iquote #{include_dir}" }.join(' ')} " if self.include_dirs.any? cmd << "-o #{exe_file} " # gives each function its own section # allowing them to be reordered cmd << '-ffunction-sections ' cmd << '-fno-asynchronous-unwind-tables ' cmd << '-fno-ident ' cmd << opt_level if self.compile_options cmd << self.compile_options else link_options << '--image-base=0x0,' cmd << '-nostdlib ' end link_options << '--no-seh' link_options << ',-s' if self.strip_syms link_options << ",-T#{self.link_script}" if self.link_script cmd << link_options cmd end def cleanup_files src_file = "#{self.file_name}.c" exe_file = "#{self.file_name}.exe" unless self.keep_src File.delete(src_file) if File.exist?(src_file) end unless self.keep_exe File.delete(exe_file) if File.exist?(exe_file) end rescue Errno::ENOENT print_error("Failed to delete file") end class X86 include Mingw attr_reader :file_name, :keep_exe, :keep_src, :strip_syms, :link_script, :opt_lvl, :mingw_bin, :compile_options, :show_compile_cmd, :include_dirs def initialize(opts={}) @file_name = opts[:f_name] @keep_exe = opts[:keep_exe] @keep_src = opts[:keep_src] @strip_syms = opts[:strip_symbols] @show_compile_cmd = opts[:show_compile_cmd] @link_script = opts[:linker_script] @compile_options = opts[:compile_options] @opt_lvl = opts[:opt_lvl] @include_dirs = opts[:include_dirs] || [] @mingw_bin = MINGW_X86 end def self.available? !!(Msf::Util::Helper.which(MINGW_X86)) end end class X64 include Mingw attr_reader :file_name, :keep_exe, :keep_src, :strip_syms, :link_script, :opt_lvl, :mingw_bin, :compile_options, :show_compile_cmd, :include_dirs def initialize(opts={}) @file_name = opts[:f_name] @keep_exe = opts[:keep_exe] @keep_src = opts[:keep_src] @strip_syms = opts[:strip_symbols] @show_compile_cmd = opts[:show_compile_cmd] @link_script = opts[:linker_script] @compile_options = opts[:compile_options] @opt_lvl = opts[:opt_lvl] @include_dirs = opts[:include_dirs] || [] @mingw_bin = MINGW_X64 end def self.available? !!(Msf::Util::Helper.which(MINGW_X64)) end end class UncompilablePayloadError < StandardError def initialize(msg='') super(msg) end end class CompiledPayloadNotFoundError < StandardError def initialize(msg='Compiled executable not found') super(msg) end end end end end end