class PEdump::CLI
Constants
- COMMENTS
- DEFAULT_ALL_ACTIONS
- DllCharacteristics
- KNOWN_ACTIONS
- MajorOperatingSystemVersion
- MajorSubsystemVersion
- MinorOperatingSystemVersion
- MinorSubsystemVersion
- URL_BASE
Attributes
argv[RW]
data[RW]
Public Class Methods
new(argv = ARGV)
click to toggle source
# File lib/pedump/cli.rb, line 46 def initialize argv = ARGV @argv = argv end
Public Instance Methods
_flags2string(flags)
click to toggle source
# File lib/pedump/cli.rb, line 439 def _flags2string flags return '' if !flags || flags.empty? a = [flags.shift.dup] flags.each do |f| if (a.last.size + f.size) < 40 a.last << ", " << f else a << f.dup end end a.join("\n"+ ' '*58) end
action_title(action)
click to toggle source
# File lib/pedump/cli.rb, line 325 def action_title action if @need_fname_header @need_fname_header = false puts if @file_idx > 0 puts "# -----------------------------------------------" puts "# #@file_name" puts "# -----------------------------------------------" end s = action.to_s.upcase.tr('_',' ') s += " Header" if [:mz, :pe, :rich].include?(action) s = "Packer / Compiler" if action == :packer s = "imphash" if action == :imphash "\n=== %s ===\n\n" % s end
console(f)
click to toggle source
# File lib/pedump/cli.rb, line 302 def console f require 'pedump/loader' require 'pp' ARGV.clear # clear ARGV so IRB is not confused require 'irb' f.rewind ldr = @ldr = PEdump::Loader.new(f) # override IRB.setup, called from IRB.start m0 = IRB.method(:setup) IRB.define_singleton_method :setup do |*args| m0.call *args conf[:IRB_RC] = Proc.new do |context| context.main.instance_variable_set '@ldr', ldr context.main.define_singleton_method(:ldr){ @ldr } end end puts "[.] ldr = PEdump::Loader.new(open(#{f.path.inspect}))".gray IRB.start end
create_pedump(io)
click to toggle source
# File lib/pedump/cli.rb, line 180 def create_pedump io PEdump.new(io, :force => @options[:force]).tap do |x| x.logger.level = case @options[:verbose] when -100..-3 Logger::FATAL + 1 when -2 Logger::FATAL when -1 Logger::ERROR when 0 Logger::WARN # default when 1 Logger::INFO when 2..100 Logger::DEBUG end end end
disasm(x)
click to toggle source
# File lib/pedump/cli.rb, line 859 def disasm x puts "TODO" end
dump(data, opts = {})
click to toggle source
# File lib/pedump/cli.rb, line 393 def dump data, opts = {} case opts[:format] || @options[:format] || :dump when :dump, :hexdump data.hexdump when :hex puts data.each_byte.map{ |x| "%02x" % x }.join(' ') when :binary print data when :c name = opts[:name] || "foo" puts "// #{data.size} bytes total" puts "unsigned char #{name}[] = {" data.unpack('C*').each_slice(12) do |row| puts " " + row.map{ |c| "0x%02x," % c}.join(" ") end puts "};" when :inspect require 'pp' pp data when :table dump_table data, opts when :yaml require 'yaml' puts data.to_yaml when :json require 'json' puts data.to_json end end
dump_action(action, f)
click to toggle source
# File lib/pedump/cli.rb, line 341 def dump_action action, f if action.is_a?(Array) case action[0] when :disasm return when :extract return extract action[1] when :set_os_version return set_os_version action[1] when :set_dll_char return set_dll_char action[1] when :va2file @pedump.sections(f) va = action[1] =~ /(^0x)|(h$)/i ? action[1].to_i(16) : action[1].to_i file_offset = @pedump.va2file(va) printf "va2file(0x%x) = 0x%x (%d)\n", va, file_offset, file_offset return else raise "unknown action #{action.inspect}" end end data = @pedump.send(action, f) return if !data || (data.respond_to?(:empty?) && data.empty?) puts action_title(action) unless @options[:format] == :binary || @actions == [:imphash] return dump(data, action: action) if [:inspect, :table, :json, :yaml].include?(@options[:format]) dump_opts = {:name => action} case action when :pe data = @pedump.pe.pack when :resources return dump_resources(data) when :strings return dump_strings(data) when :imports return dump_imports(data) when :exports return dump_exports(data) when :version_info return dump_version_info(data) else if data.is_a?(Struct) && data.respond_to?(:pack) data = data.pack elsif data.is_a?(Array) && data.all?{ |x| x.is_a?(Struct) && x.respond_to?(:pack)} data = data.map(&:pack).join end end dump data, dump_opts end
dump_data_dir(data)
click to toggle source
# File lib/pedump/cli.rb, line 829 def dump_data_dir data data.each do |row| printf " %-12s rva:0x%8x size:0x %8x\n", row.type, row.va.to_i, row.size.to_i end end
dump_efi_data_dir(data)
click to toggle source
# File lib/pedump/cli.rb, line 835 def dump_efi_data_dir data data.each_with_index do |row, idx| printf " %-12s rva:0x%8x size:0x %8x\n", PEdump::EFI_IMAGE_DATA_DIRECTORY::TYPES[idx], row.va.to_i, row.size.to_i end end
dump_exports(data)
click to toggle source
# File lib/pedump/cli.rb, line 632 def dump_exports data printf "# module %s\n", data.name.inspect printf "# description %s\n", data.description.inspect if data.description if data.Characteristics || data.TimeDateStamp || data.MajorVersion || data.MinorVersion || data.Base printf "# flags=0x%x ts=%s version=%d.%d ord_base=%d\n", data.Characteristics.to_i, Time.at(data.TimeDateStamp.to_i).utc.strftime('"%Y-%m-%d %H:%M:%S"'), data.MajorVersion.to_i, data.MinorVersion.to_i, data.Base.to_i end if @options[:verbose] > 0 [%w'Names', %w'EntryPoints Functions', %w'Ordinals NameOrdinals'].each do |x| va = data["AddressOf"+x.last] ofs = @pedump.va2file(va) || '?' printf("# %-12s rva=0x%08x file_offset=%8s\n", x.first, va, ofs) if va end end if data.NumberOfFunctions || data.NumberOfNames printf "# nFuncs=%d nNames=%d\n", data.NumberOfFunctions.to_i, data.NumberOfNames.to_i end if data.functions && data.functions.any? puts if @pedump.ne? printf "%5s %9s %s\n", "ORD", "SEG:OFFS", "NAME" data.functions.each do |f| printf "%5x %4x:%04x %s\n", f.ord, f.va>>16, f.va&0xffff, f.name end else printf "%5s %8s %s\n", "ORD", "ENTRY_VA", "NAME" data.functions.each do |f| printf "%5x %8x %s\n", f.ord, f.va, f.name end end end end
dump_generic_table(data)
click to toggle source
# File lib/pedump/cli.rb, line 452 def dump_generic_table data data.each_pair do |k,v| next if [:DataDirectory, :section_table].include?(k) case v when Numeric case k when /\AMajor.*Version\Z/ printf "%30s: %24s\n", k.to_s.sub('Major',''), "#{v}.#{data[k.to_s.sub('Major','Minor')]}" when /\AMinor.*Version\Z/ when /TimeDateStamp/ printf "%30s: %24s\n", k, Time.at(v).utc.strftime('"%Y-%m-%d %H:%M:%S"') else comment = '' if COMMENTS[k] comment = COMMENTS[k][v] || (COMMENTS[k].is_a?(Hash) ? COMMENTS[k]['default'] : '') || '' elsif data.is_a?(PEdump::IMAGE_FILE_HEADER) && k == :Characteristics comment = _flags2string(data.flags) elsif k == :DllCharacteristics comment = _flags2string(data.flags) end comment.strip! comment = " #{comment}" unless comment.empty? printf "%30s: %10d %12s%s\n", k, v, v<10 ? v : ("0x"+v.to_s(16)), comment end when Struct # IMAGE_FILE_HEADER: # IMAGE_OPTIONAL_HEADER: printf "\n# %s:\n", v.class.to_s.split('::').last dump_table v when Time printf "%30s: %24s\n", k, v.strftime('"%Y-%m-%d %H:%M:%S"') else printf "%30s: %24s\n", k, v.to_s.inspect end end end
dump_imports(data)
click to toggle source
# File lib/pedump/cli.rb, line 672 def dump_imports data fmt = "%-15s %5s %5s %s\n" printf fmt, "MODULE_NAME", "HINT", "ORD", "FUNCTION_NAME" data.each do |x| case x when PEdump::IMAGE_IMPORT_DESCRIPTOR (Array(x.original_first_thunk) + Array(x.first_thunk)).uniq.each do |f| next unless f # imported function printf fmt, x.module_name, f.hint ? f.hint.to_s(16) : '', f.ordinal ? f.ordinal.to_s(16) : '', f.name end when PEdump::ImportedFunction printf fmt, x.module_name, x.hint ? x.hint.to_s(16) : '', x.ordinal ? x.ordinal.to_s(16) : '', x.name else raise "invalid #{x.inspect}" end end end
dump_ne_segments(data)
click to toggle source
# File lib/pedump/cli.rb, line 820 def dump_ne_segments data fmt = "%2x %6x %6x %9x %9x %6x %s\n" printf fmt.tr('x','s'), *%w'# OFFSET SIZE MIN_ALLOC FILE_OFFS FLAGS', '' data.each_with_index do |seg,idx| printf fmt, idx+1, seg.offset, seg.size, seg.min_alloc_size, seg.file_offset, seg.flags, seg.flags_desc end end
dump_packer_only(fnames)
click to toggle source
# File lib/pedump/cli.rb, line 200 def dump_packer_only fnames max_fname_len = fnames.map(&:size).max fnames.each do |fname| if File.directory?(fname) if @options[:recursive] dump_packer_only(Dir[File.join(fname.shellescape,"*")]) else STDERR.puts "[?] #{fname} is a directory, and recursive flag is not set" end else File.open(fname,'rb') do |f| @pedump = create_pedump fname packers = @pedump.packers(f) pname = Array(packers).first.try(:packer).try(:name) pname ||= "unknown" if @options[:verbose] > 0 printf("%-*s %s\n", max_fname_len+1, "#{fname}:", pname) if pname end end end end
dump_packers(data)
click to toggle source
# File lib/pedump/cli.rb, line 621 def dump_packers data if @options[:verbose] > 0 data.each do |p| printf "%8x %4d %s\n", p.offset, p.packer.size, p.packer.name end else # show only largest detected unless verbose output requested puts " #{data.first.packer.name}" end end
dump_res_dir(entry, level = 0)
click to toggle source
# File lib/pedump/cli.rb, line 709 def dump_res_dir entry, level = 0 if entry.is_a?(PEdump::IMAGE_RESOURCE_DIRECTORY) # root entry printf "dir? %8s %8s %5s %5s", "FLAGS", "TIMESTMP", "VERS", 'nEnt' printf " | %-15s %8s | ", "NAME", "OFFSET" printf "data? %8s %8s %5s %8s\n", 'DATA_OFS', 'DATA_SZ', 'CP', 'RESERVED' end dir = case entry when PEdump::IMAGE_RESOURCE_DIRECTORY entry when PEdump::IMAGE_RESOURCE_DIRECTORY_ENTRY entry.data end fmt1 = "DIR: %8x %8x %5s %5d" fmt1s = fmt1.tr("xd\nDIR:","ss ") % ['','','',''] if dir.is_a?(PEdump::IMAGE_RESOURCE_DIRECTORY) printf fmt1, dir.Characteristics, dir.TimeDateStamp, [dir.MajorVersion,dir.MinorVersion].join('.'), dir.NumberOfNamedEntries + dir.NumberOfIdEntries else print fmt1s end name = case level when 0 then "ROOT" when 1 then PEdump::ROOT_RES_NAMES[entry.Name] || entry.name else entry.name end printf " | %-15s", name printf("\n%s %15s",fmt1s,'') if name.size > 15 printf " %8x | ", entry.respond_to?(:OffsetToData) ? entry.OffsetToData : 0 if dir.is_a?(PEdump::IMAGE_RESOURCE_DIRECTORY) puts dir.entries.each do |child| dump_res_dir child, level+1 end elsif dir printf "DATA: %8x %8x %5s %8x\n", dir.OffsetToData, dir.Size, dir.CodePage, dir.Reserved else puts # null dir end end
dump_resources(data)
click to toggle source
def dump_res_dir0 dir, level=0, dir_entry = nil
dir_entry ||= PEdump::IMAGE_RESOURCE_DIRECTORY_ENTRY.new printf "%-10s %8x %8x %8x %5s %5d\n", dir_entry.name || "ROOT", dir_entry.OffsetToData.to_i, dir.Characteristics, dir.TimeDateStamp, [dir.MajorVersion,dir.MinorVersion].join('.'), dir.NumberOfNamedEntries + dir.NumberOfIdEntries dir.entries.each do |child| if child.data.is_a?(PEdump::IMAGE_RESOURCE_DIRECTORY) dump_res_dir child.data, level+1, child else print " "*(level+1) + "CHILD" child.data.each_pair do |k,v| print " #{k[0,2]}=#{v}" end puts #p child end end
end
# File lib/pedump/cli.rb, line 780 def dump_resources data keys = []; fmt = [] fmt << "%11x " ; keys << :file_offset fmt << "%5d " ; keys << :cp fmt << "%5x " ; keys << :lang fmt << "%8d " ; keys << :size fmt << "%-13s "; keys << :type fmt << "%s\n" ; keys << :name printf fmt.join.tr('dx','s'), *keys.map(&:to_s).map(&:upcase) data.each do |res| fmt.each_with_index do |f,i| if v = res.send(keys[i]) if f['x'] printf f.tr('x','s'), v.to_i < 10 ? v.to_s : "0x#{v.to_s(16)}" else printf f, v end else # NULL value printf f.tr('xd','s'), '' end end end end
dump_rich_hdr(data)
click to toggle source
# File lib/pedump/cli.rb, line 841 def dump_rich_hdr data if decoded = data.decode puts " ID VER COUNT DESCRIPTION" decoded.each do |row| # printf " %5d %4x %7d %4x %12d %8x\n", # row.id, row.id, row.version, row.version, row.times, row.times printf " %4x %4x %12d %s\n", row.id, row.version, row.times, ::PEdump::RICH_IDS[(row.id<<16) + row.version] end else puts "# raw:" data.hexdump puts puts "# dexored:" data.dexor.hexdump end end
dump_sections(data)
click to toggle source
# File lib/pedump/cli.rb, line 805 def dump_sections data printf " %-8s %8s %8s %8s %8s %5s %8s %5s %8s %8s\n", 'NAME', 'RVA', 'VSZ','RAW_SZ','RAW_PTR','nREL','REL_PTR','nLINE','LINE_PTR','FLAGS' data.each do |s| name = s.Name[/[^a-z0-9_.]/i] ? s.Name.inspect : s.Name name = "#{name}\n " if name.size > 8 printf " %-8s %8x %8x %8x %8x %5x %8x %5x %8x %8x %s\n", name.to_s, s.VirtualAddress.to_i, s.VirtualSize.to_i, s.SizeOfRawData.to_i, s.PointerToRawData.to_i, s.NumberOfRelocations.to_i, s.PointerToRelocations.to_i, s.NumberOfLinenumbers.to_i, s.PointerToLinenumbers.to_i, s.flags.to_i, s.flags_desc end end
dump_security(data)
click to toggle source
# File lib/pedump/cli.rb, line 535 def dump_security data return unless data data.each do |win_cert| if win_cert.data.respond_to?(:certificates) win_cert.data.certificates.each do |cert| puts cert.to_text puts end else @pedump.logger.error "[?] no certificates in #{win_cert.class}" end end end
dump_strings(data)
click to toggle source
# File lib/pedump/cli.rb, line 699 def dump_strings data printf "%5s %5s %4s %s\n", "ID", "ID", "LANG", "STRING" prev_lang = nil data.sort_by{|s| [s.lang, s.id] }.each do |s| #puts if prev_lang && prev_lang != s.lang printf "%5d %5x %4s %s\n", s.id, s.id, s.lang && s.lang.to_s(16), s.value.inspect prev_lang = s.lang end end
dump_table(data, opts = {})
click to toggle source
# File lib/pedump/cli.rb, line 489 def dump_table data, opts = {} if data.is_a?(Struct) return dump_res_dir(data) if data.is_a?(PEdump::IMAGE_RESOURCE_DIRECTORY) return dump_exports(data) if data.is_a?(PEdump::IMAGE_EXPORT_DIRECTORY) dump_generic_table data elsif data.is_a?(Enumerable) && data.map(&:class).uniq.size == 1 case data.first when PEdump::IMAGE_DATA_DIRECTORY dump_data_dir data when PEdump::EFI_IMAGE_DATA_DIRECTORY dump_efi_data_dir data when PEdump::IMAGE_SECTION_HEADER dump_sections data when PEdump::Resource dump_resources data when PEdump::STRING dump_strings data when PEdump::IMAGE_IMPORT_DESCRIPTOR, PEdump::ImportedFunction dump_imports data when PEdump::Packer::Match dump_packers data when PEdump::VS_VERSIONINFO, PEdump::NE::VS_VERSIONINFO dump_version_info data when PEdump::IMAGE_TLS_DIRECTORY32, PEdump::IMAGE_TLS_DIRECTORY64 dump_tls data when PEdump::WIN_CERTIFICATE dump_security data when PEdump::NE::Segment dump_ne_segments data else puts "[?] don't know how to dump: #{data.inspect[0,50]}" unless data.empty? end elsif data.is_a?(PEdump::DOSStub) data.hexdump elsif data.is_a?(PEdump::RichHdr) dump_rich_hdr data else case opts[:action] when :imphash puts "#{data} #{@file_name}" else puts "[?] Don't know how to display #{data.inspect[0,50]}... as a table" end end end
dump_tls(data)
click to toggle source
# File lib/pedump/cli.rb, line 549 def dump_tls data fmt = "%10x %10x %8x %8x %8x %8x\n" printf fmt.tr('x','s'), *%w'RAW_START RAW_END INDEX CALLBKS ZEROFILL FLAGS' data.each do |tls| printf fmt, tls.StartAddressOfRawData.to_i, tls.EndAddressOfRawData.to_i, tls.AddressOfIndex.to_i, tls.AddressOfCallBacks.to_i, tls.SizeOfZeroFill.to_i, tls.Characteristics.to_i end end
dump_version_info(data)
click to toggle source
# File lib/pedump/cli.rb, line 563 def dump_version_info data if @options[:format] != :table File.open(@file_name,'rb') do |f| @pedump.resources.find_all{ |r| r.type == 'VERSION'}.each do |res| f.seek res.file_offset data = f.read(res.size) dump data end end return end fmt = " %-20s: %s\n" data.each do |vi| puts "# VS_FIXEDFILEINFO:" if @options[:verbose] > 0 || vi.Value.dwSignature != 0xfeef04bd printf(fmt, "Signature", "0x#{vi.Value.dwSignature.to_i.to_s(16)}") end printf fmt, 'FileVersion', [ vi.Value.dwFileVersionMS.to_i >> 16, vi.Value.dwFileVersionMS.to_i & 0xffff, vi.Value.dwFileVersionLS.to_i >> 16, vi.Value.dwFileVersionLS.to_i & 0xffff ].join('.') printf fmt, 'ProductVersion', [ vi.Value.dwProductVersionMS.to_i >> 16, vi.Value.dwProductVersionMS.to_i & 0xffff, vi.Value.dwProductVersionLS.to_i >> 16, vi.Value.dwProductVersionLS.to_i & 0xffff ].join('.') vi.Value.each_pair do |k,v| next if k[/[ML]S$/] || k == :valid || k == :dwSignature printf fmt, k.to_s.sub(/^dw/,''), v.to_i > 9 ? "0x#{v.to_s(16)}" : v end vi.Children.each do |file_info| case file_info when PEdump::StringFileInfo, PEdump::NE::StringFileInfo file_info.Children.each do |string_table| puts "\n# StringTable #{string_table.szKey}:" string_table.Children.each do |string| printf fmt, string.szKey, string.Value.inspect end end when PEdump::VarFileInfo, PEdump::NE::VarFileInfo puts printf fmt, "VarFileInfo", '[ 0x' + file_info.Children.Value.map{|v| v.to_s(16)}.join(", 0x") + ' ]' else puts "[?] unknown child type: #{file_info.inspect}, use -fi to inspect" end end end end
extract(x)
click to toggle source
# File lib/pedump/cli.rb, line 863 def extract x a = x.split(':',2) case a[0] when 'datadir' extract_datadir a[1] when 'resource' extract_resource a[1] when 'section' extract_section a[1] else raise "invalid #{x.inspect}" end end
extract_datadir(id)
click to toggle source
# File lib/pedump/cli.rb, line 877 def extract_datadir id entry = @pedump.data_directory.find{ |x| x.type == id } unless entry @pedump.logger.fatal "[!] entry #{id.inspect} not found" exit(1) end if entry.size != 0 _copy_stream @pedump.io, $stdout, entry.size, @pedump.va2file(entry.va) end end
extract_resource(id)
click to toggle source
# File lib/pedump/cli.rb, line 888 def extract_resource id res = nil if id =~ /\A0x\h+\Z/ needle = id.to_i(16) res = @pedump.resources.find{ |r| r.file_offset == needle } elsif id =~ /\A\d+\Z/ needle = id.to_i(10) res = @pedump.resources.find{ |r| r.file_offset == needle } elsif id['/'] type, name = id.split('/', 2) res = @pedump.resources.find{ |r| r.type == type && r.name == name } else @pedump.logger.fatal "[!] invalid resource id #{id.inspect}" exit(1) end unless res @pedump.logger.fatal "[!] resource #{id.inspect} not found" exit(1) end _copy_stream @pedump.io, $stdout, res.size, res.file_offset end
extract_section(id)
click to toggle source
# File lib/pedump/cli.rb, line 910 def extract_section id section = nil if id['/'] a = id.split('/',2) if a[0] == 'rva' value = a[1].to_i(0) # auto hex/dec, but also can parse oct and bin section = @pedump.sections.find{ |s| s.VirtualAddress == value } elsif a[0] == 'raw' value = a[1].to_i(0) # auto hex/dec, but also can parse oct and bin section = @pedump.sections.find{ |s| s.PointerToRawData == value } else @pedump.logger.fatal "[!] invalid section id #{id.inspect}" exit(1) end else section = @pedump.sections.find{ |s| s.Name == id } end unless section @pedump.logger.fatal "[!] section #{id.inspect} not found" exit(1) end _copy_stream @pedump.io, $stdout, section.SizeOfRawData, section.PointerToRawData end
run()
click to toggle source
# File lib/pedump/cli.rb, line 50 def run @actions = [] @options = { :format => :table, :verbose => 0 } optparser = OptionParser.new do |opts| opts.banner = "Usage: pedump [options]" opts.on "--version", "Print version information and exit" do puts PEdump::VERSION exit end opts.on "-v", "--verbose", "Run verbosely","(can be used multiple times)" do |v| @options[:verbose] += 1 end opts.on "-q", "--quiet", "Silent any warnings","(can be used multiple times)" do |v| @options[:verbose] -= 1 end opts.on "-F", "--force", "Try to dump by all means","(can cause exceptions & heavy wounds)" do |v| @options[:force] ||= 0 @options[:force] += 1 end opts.on "-f", "--format FORMAT", [:binary, :c, :dump, :hex, :inspect, :json, :table, :yaml], "Output format: bin,c,dump,hex,inspect,json,table,yaml","(default: table)" do |v| @options[:format] = v end KNOWN_ACTIONS.each do |t| a = [ "--#{t.to_s.tr('_','-')}", eval("lambda{ |_| @actions << :#{t.to_s.tr('-','_')} }") ] a.unshift(a[0][1,2].upcase) if a[0] =~ /--(((ex|im)port|section|resource)s|version-info)/ a.unshift(a[0][1,2]) if a[0] =~ /--strings/ opts.on *a end opts.on "--deep", "packer deep scan, significantly slower" do @options[:deep] ||= 0 @options[:deep] += 1 PEdump::Packer.default_deep = @options[:deep] end opts.on '-P', "--packer-only", "packer/compiler detect only,","mimics 'file' command output" do @actions << :packer_only end opts.on '-r', "--recursive", "recurse dirs in packer detect" do @options[:recursive] = true end opts.on "--all", "Dump all but resource-directory (default)" do @actions = DEFAULT_ALL_ACTIONS end opts.separator '' # opts.on "--disasm [X]", "Disassemble a symbol/VA" do |v| # @actions << [:disasm, v] # end opts.on("--extract ID", "Extract a resource/section/data_dir", "ID: datadir:EXPORT - datadir by type", "ID: resource:0x98478 - resource by offset", "ID: resource:ICON/#1 - resource by type & name", "ID: section:.text - section by name", "ID: section:rva/0x1000 - section by RVA", "ID: section:raw/0x400 - section by RAW_PTR", ) do |v| @actions << [:extract, v] end opts.on "--va2file VA", "Convert RVA to file offset" do |va| @actions << [:va2file, va] end opts.on "--set-os-version VER", "Patch OS version in PE header" do |ver| @actions << [:set_os_version, ver] end opts.on "--set-dll-char X", "Patch IMAGE_OPTIONAL_HEADER32.DllCharacteristics" do |x| @actions << [:set_dll_char, x] end opts.separator '' opts.on "-W", "--web", "Uploads files to a #{URL_BASE}","for a nice HTML tables with image previews,","candies & stuff" do @actions << :web end opts.on "-C", "--console", "opens IRB console with specified file loaded" do @actions << :console end end if (@argv = optparser.parse(@argv)).empty? puts optparser.help return end if (@actions-KNOWN_ACTIONS).any?{ |x| !x.is_a?(Array) } puts "[?] unknown actions: #{@actions-KNOWN_ACTIONS}" @actions.delete_if{ |x| !KNOWN_ACTIONS.include?(x) } end @actions = DEFAULT_ALL_ACTIONS if @actions.empty? if @actions.include?(:packer_only) raise "[!] can't mix --packer-only with other actions" if @actions.size > 1 dump_packer_only(argv) return end argv.each_with_index do |fname,idx| @need_fname_header = (argv.size > 1) @file_idx = idx @file_name = fname File.open(fname,'rb') do |f| @pedump = create_pedump f next if !@options[:force] && !@pedump.supported_file?(f) @actions.each do |action| case action when :web; upload f when :console; console f else dump_action action,f end end end end rescue Errno::EPIPE # output interrupt, f.ex. when piping output to a 'head' command # prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message end
set_dll_char(x)
click to toggle source
# File lib/pedump/cli.rb, line 934 def set_dll_char x @pedump.pe.image_optional_header.DllCharacteristics = x.to_i(0) io = @pedump.io.reopen(@file_name,'rb+') io.seek @pedump.pe.ioh_offset io.write @pedump.pe.image_optional_header.pack io.close end
set_os_version(ver)
click to toggle source
# File lib/pedump/cli.rb, line 942 def set_os_version ver raise "[!] invalid version #{ver.inspect}" unless ver =~ /\A(\d+)\.(\d+)\Z/ raise "[!] no IMAGE_OPTIONAL_HEADER" if @pedump.pe.ifh.SizeOfOptionalHeader.to_i == 0 major = $1.to_i minor = $2.to_i ver = "#{major}.#{minor}" ioh = @pedump.pe.image_optional_header prev_os_ver = "#{ioh.MajorOperatingSystemVersion}.#{ioh.MinorOperatingSystemVersion}" prev_ss_ver = "#{ioh.MajorSubsystemVersion}.#{ioh.MinorSubsystemVersion}" if prev_os_ver == ver && prev_ss_ver == ver @pedump.logger.warn "[?] already has #{ver}" return end if prev_os_ver != ver ioh.MajorOperatingSystemVersion = major ioh.MinorOperatingSystemVersion = minor @pedump.logger.warn "[.] MajorOperatingSystemVersion: #{prev_os_ver} -> #{ver}" end if prev_ss_ver != ver ioh.MajorSubsystemVersion = major ioh.MinorSubsystemVersion = minor @pedump.logger.warn "[.] MajorSubsystemVersion: #{prev_ss_ver} -> #{ver}" end io = @pedump.io.reopen(@file_name,'rb+') io.seek @pedump.pe.ioh_offset io.write ioh.pack io.close end
upload(f)
click to toggle source
# File lib/pedump/cli.rb, line 244 def upload f if @pedump.mz(f).signature != 'MZ' @pedump.logger.error "[!] refusing to upload a non-MZ file" return end require 'digest/md5' require 'open-uri' require 'net/http' require 'net/http/post/multipart' stdout_sync = $stdout.sync $stdout.sync = true md5 = Digest::MD5.file(f.path).hexdigest @pedump.logger.info "[.] md5: #{md5}" file_url = "#{URL_BASE}/#{md5}/" @pedump.logger.warn "[.] checking if file already uploaded.." uri = URI.parse URL_BASE Net::HTTP.start(uri.host, uri.port, use_ssl: (uri.scheme == 'https')) do |http| http.open_timeout = 10 http.read_timeout = 10 # doing HTTP HEAD is a lot faster than open-uri h = http.head("/#{md5}/") if h.code.to_i == 200 && h.content_type.to_s.strip.downcase == "text/html" @pedump.logger.warn "[.] file already uploaded: #{file_url}" return elsif h.code.to_i != 404 # 404 means that there's no such file and we're OK to upload @pedump.logger.fatal "[!] invalid server response: \"#{h.code} #{h.msg}\" (#{h.content_type})" exit(1) end end f.rewind # upload with progress post_url = URI.parse(URL_BASE+'/upload') # UploadIO is from multipart-post uio = UploadIO.new(f, "application/octet-stream", File.basename(f.path)) ppx = ProgressProxy.new(uio) req = Net::HTTP::Post::Multipart.new post_url.path, "file" => ppx res = Net::HTTP.start(post_url.host, post_url.port, use_ssl: (post_url.scheme == 'https')) do |http| http.request(req) end ppx.finish! unless [200, 302, 303].include?(res.code.to_i) @pedump.logger.fatal "[!] invalid server response: #{res.code} #{res.msg}" @pedump.logger.fatal res.body unless res.body.empty? exit(1) end puts "[.] uploaded: #{file_url}" ensure $stdout.sync = stdout_sync end
Private Instance Methods
_copy_stream(src, dst, src_length = nil, src_offset = 0)
click to toggle source
github.com/zed-0xff/pedump/issues/44 redmine.ruby-lang.org/issues/12280
# File lib/pedump/cli.rb, line 980 def _copy_stream(src, dst, src_length = nil, src_offset = 0) IO::copy_stream(src, dst, src_length, src_offset) rescue NotImplementedError # `copy_stream': pread() not implemented (NotImplementedError) src_length ||= src.size - src_offset bufsize = 16384 buf = ("\x00".force_encoding('binary')) * bufsize src.binmode dst.binmode saved_pos = src.tell src.seek(src_offset) bytes_copied = 0 while src_length > 0 && buf.size != 0 src.read([bufsize, src_length].min, buf) dst.write(buf) src_length -= buf.size bytes_copied += buf.size end src.seek(saved_pos) bytes_copied end