diff --git a/tools/find_badchars.rb b/tools/find_badchars.rb new file mode 100755 index 0000000000..69e736d714 --- /dev/null +++ b/tools/find_badchars.rb @@ -0,0 +1,141 @@ +#!/usr/bin/env ruby +# +# This script is intended to assist an exploit developer in deducing what +# "bad characters" exist for a given input path to a program. +# + +$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) + +require 'rex' + +OutStatus = "[*] " +OutError = "[-] " + +$args = Rex::Parser::Arguments.new( + "-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ], + "-h" => [ false, "Help banner" ], + "-i" => [ true, "Read memory contents from the supplied file path" ], + "-t" => [ true, "The format that the memory contents are in (empty to list)" ]) + +def usage + $stderr.puts("\n" + " Usage: #{File.basename($0)} \n" + $args.usage) + exit +end + +def show_format_list + $stderr.puts("Supported formats:\n") + $stderr.puts(" raw raw binary data\n") + $stderr.puts(" windbg output from windbg's \"db\" command\n") + $stderr.puts(" gdb output from gdb's \"x/bx\" command\n") +end + +def debug_buffer(name, buf) + str = "\n#{buf.length} bytes of " + str << name + str += ":" if buf.length > 0 + str += "\n\n" + $stderr.puts str + if buf.length > 0 + $stderr.puts Rex::Text.to_hex_dump(buf) + end +end + + +# Input defaults +badchars = '' +fmt = 'raw' +input = $stdin + +# Output +new_badchars = '' + +# Parse the argument and rock that shit. +$args.parse(ARGV) { |opt, idx, val| + case opt + when "-i" + begin + input = File.new(val) + rescue + $stderr.puts(OutError + "Failed to open file #{val}: #{$!}") + exit + end + when "-b" + badchars = Rex::Text.hex_to_raw(val) + when "-t" + if (val =~ /^(raw|windbg|gdb)$/) + fmt = val + else + if val.nil? or val.length < 1 + show_format_list + else + $stderr.puts(OutError + "Invalid format: #{val}") + end + exit + end + when "-h" + usage + end +} + +if input == $stdin + $stderr.puts(OutStatus + "Please paste the memory contents in \"" + fmt + "\" format below (end with EOF):\n") +end + + + +# Working data set +from_msf = Rex::Text.charset_exclude(badchars) +from_dbg = '' + + +# Process the input +from_dbg = input.read +case fmt + when "raw" + # this should already be in the correct format :) + + when "windbg" + translated = '' + from_dbg.each_line do |ln| + translated << ln.chomp[10,47].gsub!(/(-| )/, '') + end + from_dbg = Rex::Text.hex_to_raw(translated) + + when "gdb" + translated = '' + from_dbg.each_line do |ln| + translated << ln.chomp.split(':')[1].gsub!(/0x/, '\x').gsub!(/ /, '') + end + from_dbg = Rex::Text.hex_to_raw(translated) +end + + + +=begin +# Uncomment these to debug stuff .. +debug_buffer("BadChars", badchars) +debug_buffer("memory contents", from_dbg) +debug_buffer("Rex::Text.charset_exclude() output", from_msf) +=end + + +# Find differences between the two data sets +minlen = from_msf.length +minlen = from_dbg.length if from_dbg.length < minlen +(0..(minlen-1)).each do |idx| + ch1 = from_msf[idx] + ch2 = from_dbg[idx] + if ch1 != ch2 + str = "Byte at index 0x%04x differs (0x%02x became 0x%02x)" % [idx, ch1, ch2] + $stderr.puts OutStatus + str + new_badchars << ch1 + end +end + + +# show the results +if new_badchars.length < 1 + $stderr.puts(OutStatus + "All characters matched, no new bad characters discovered.") +else + $stderr.puts(OutStatus + "Proposed BadChars: \"" + Rex::Text.to_hex(new_badchars) + "\"") +end