# # Copyright (c) 2006-2011 Hal Brodigan (postmodern.mod3 at gmail.com) # # This file is part of Ronin Support. # # Ronin Support is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ronin Support is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with Ronin Support. If not, see . # require 'ronin/formatting/extensions/binary/integer' require 'ronin/formatting/extensions/text' require 'base64' require 'enumerator' begin require 'zlib' rescue Gem::LoadError => e raise(e) rescue ::LoadError end class String # # Packs an Integer from a String, which was originally packed for # a specific architecture and address-length. # # @param [Ronin::Arch, #endian, #address_length, String] arch # The architecture that the Integer was originally packed with. # # @param [Integer] address_length # The number of bytes to depack. # # @return [Integer] # The depacked Integer. # # @raise [ArgumentError] # The given `arch` does not respond to the `endian` or # `address_length` methods. # # @example using archs other than `Ronin::Arch`. # arch = OpenStruct.new(:endian => :little, :address_length => 4) # # "A\0\0\0".depack(arch) # # => 65 # # @example using a `Ronin::Arch` arch. # "A\0\0\0".depack(Arch.i386) # # => 65 # # @example specifying a custom address-length. # "A\0".depack(Arch.ppc,2) # # => 65 # # @example using a `String#unpack` template String as the arch. # "A\0\0\0".depack('L') # # => 65 # # @see http://ruby-doc.org/core/classes/String.html#M000760 # def depack(arch,address_length=nil) if arch.kind_of?(String) return self.unpack(arch) end unless arch.respond_to?(:address_length) raise(ArgumentError,"first argument to Ineger#pack must respond to address_length") end unless arch.respond_to?(:endian) raise(ArgumentError,"first argument to Ineger#pack must respond to endian") end address_length ||= arch.address_length integer = 0x0 byte_index = 0 case arch.endian when :little, 'little' mask = lambda { |b| b << (byte_index * 8) } when :big, 'big' mask = lambda { |b| b << ((address_length - byte_index - 1) * 8) } else raise(ArgumentError,"invalid endian #{arch.endian.inspect}") end self.each_byte do |b| break if byte_index >= address_length integer |= mask.call(b) byte_index += 1 end return integer end # # @return [String] # The hex escaped version of the String. # # @example # "hello".hex_escape # # => "\\x68\\x65\\x6c\\x6c\\x6f" # # @see String#format_bytes # def hex_escape(options={}) format_bytes(options) { |b| b.hex_escape } end # # @return [String] # The unescaped version of the hex escaped String. # # @example # "\\x68\\x65\\x6c\\x6c\\x6f".hex_unescape # # => "hello" # def hex_unescape buffer = '' hex_index = 0 hex_length = length while (hex_index < hex_length) hex_substring = self[hex_index..-1] if hex_substring =~ /^\\[0-7]{3}/ buffer << hex_substring[0..3].to_i(8) hex_index += 3 elsif hex_substring =~ /^\\x[0-9a-fA-F]{1,2}/ hex_substring[2..-1].scan(/^[0-9a-fA-F]{1,2}/) do |hex_byte| buffer << hex_byte.to_i(16) hex_index += (2 + hex_byte.length) end elsif hex_substring =~ /^\\./ escaped_char = hex_substring[1..1] buffer << case escaped_char when '0' "\0" when 'a' "\a" when 'b' "\b" when 't' "\t" when 'n' "\n" when 'v' "\v" when 'f' "\f" when 'r' "\r" else escaped_char end hex_index += 2 else buffer << hex_substring[0] hex_index += 1 end end return buffer end # # XOR encodes the String. # # @param [Enumerable, Integer] key # The byte to XOR against each byte in the String. # # @return [String] # The XOR encoded String. # # @example # "hello".xor(0x41) # # => ")$--." # # @example # "hello again".xor([0x55, 0x41, 0xe1]) # # => "=$\x8d9.\xc14&\x80 0 current_addr = words.shift.to_i(address_base) first_addr ||= current_addr if repeated (((current_addr - last_addr) / segment.length) - 1).times do buffer += segment end repeated = false end segment.clear words.each do |word| if (base != 10 && word =~ /^(\\[0abtnvfr\\]|.)$/) word.hex_unescape.each_byte { |b| segment << b } else segment += word.to_i(base).bytes(word_size) end end segment = segment[0...segment_length] buffer += segment last_addr = current_addr end end return buffer[0...(last_addr - first_addr)] end end