# Copyright (c) 2013
# - Zachary Cutlip <uid000@gmail.com>
# - Tactical Network Solutions, LLC
# 
# See LICENSE.txt for more details.
# 
import struct
import string
from ...common.support import BigEndian,LittleEndian
from ...common import hackers_quotes

class Trampoline(object):
    """
    A small (8 byte) trampoline payload.

    Attributes
    ----------
    MAX_BACK_JUMP: The maximum negative offset that can be encoded into the branch.
    MAX_FWD_JUMP: The maximum positive offset that can be encoded into the branch.

    """
    MAX_BACK_JUMP=-0x20000
    MAX_FWD_JUMP=0x1fffc
    shellcodes={}
    shellcodes[BigEndian] = string.join([
        "\x11\xefHIHILOLO",    # beq	t7,t7,0xffff0010
        "\x01\xe0\x78\x26"    # xor	t7,t7,zero
	],'')
    shellcodes[LittleEndian] = string.join([
        "LOLOHIHI\xef\x11",    # beq    t7,t7,0x20000
        "\x26\x78\xe0\x01"    # xor t7,t7,zero
    ],'')

    def __init__(self,endianness,offset):
        """
        Class constructor.

        Parameters
        ----------
        endianness: Endianness of the target system. (See the support.BigEndian
            and support.LittleEndian)
        offset: Positive or negative offset to jump.

        Raises EncoderException if the offset is outside of the MAX_BACK_JUMP -
            MAX_FWD_JUMP range.
            
        Note: 1028, or 0x0404, is the smallest amount you can trampoline forward
             without a NULL byte in the encoded beq instruction.
        Note: Remember, the program counter has already advanced +4 from whatever
              the location is of this payload object. So your jump offset will be
              relative to this object's offset+4.
        """
        if (offset < self.__class__.MAX_BACK_JUMP or
                offset > self.__class__.MAX_FWD_JUMP):
            raise EncoderException("Offset %d is outside of %d backwards or %d forwards." %
                    (offset,self.__class__.MAX_BACK_JUMP,self.__class__.MAX_BACK_JUMP))
        self.endianness=endianness
        self.shellcode=self.__class__.shellcodes[endianness]

        #TODO: is this endianness-safe?
        packedbytes=struct.pack(">h",(offset>>2))

        low_byte=packedbytes[1]
        high_byte=packedbytes[0]

        self.shellcode=self.shellcode.replace("HIHI",high_byte)
        self.shellcode=self.shellcode.replace("LOLO",low_byte)
        
        if hackers_quotes:
            hackers_quotes.log_random_quote()


