コード例 #1
0
ファイル: cs361utils.py プロジェクト: ellismm/cs361_security
def init_cs361(is_remote, s1, s2):
    global r, student1, student2, server
    server = is_remote
    student1 = s1
    student2 = s2
    if (server):
        r = remote("34.73.178.97", 9000)
    else:
        r = process("./encrypt")

    l = getLogger(pwnlib.tubes.process.__name__)
    l.setLevel('error')

    l = getLogger(pwnlib.tubes.remote.__name__)
    l.setLevel('error')
コード例 #2
0
ファイル: __init__.py プロジェクト: h0rv4th/pwntools
def update_context_defaults(section):
    # Circular imports FTW!
    from pwnlib.util import safeeval
    from pwnlib.log import getLogger
    log = getLogger(__name__)
    for key, value in section.items():
        if key not in ContextType.defaults:
            log.warn("Unknown configuration option %r in section %r" %
                     (key, 'context'))
            continue

        default = ContextType.defaults[key]

        if isinstance(
                default,
                six.string_types + six.integer_types + (tuple, list, dict)):
            value = safeeval.expr(value)
        else:
            log.warn("Unsupported configuration option %r in section %r" %
                     (key, 'context'))

        # Attempt to set the value, to see if it is value:
        try:
            with context.local(**{key: value}):
                value = getattr(context, key)
        except (ValueError, AttributeError) as e:
            log.warn("Could not set context.%s=%s via pwn.conf (%s)", key,
                     section[key], e)
            continue

        ContextType.defaults[key] = value
コード例 #3
0
ファイル: __init__.py プロジェクト: cxh852456/pwntools
def update_context_defaults(section):
    # Circular imports FTW!
    from pwnlib.util import safeeval
    from pwnlib.log import getLogger
    log = getLogger(__name__)
    for key, value in section.items():
        if key not in ContextType.defaults:
            log.warn("Unknown configuration option %r in section %r" % (key, 'context'))
            continue
        if isinstance(ContextType.defaults[key], (str, unicode, tuple)):
            value = safeeval.expr(value)

        ContextType.defaults[key] = value
コード例 #4
0
ファイル: config.py プロジェクト: robertjriga/pwntools
def initialize():
    """Read the configuration files."""
    from pwnlib.log import getLogger
    log = getLogger(__name__)

    c = configparser.ConfigParser()
    c.read(['/etc/pwn.conf', os.path.expanduser('~/.pwn.conf')])

    for section in c.sections():
        if section not in registered_configs:
            log.warn("Unknown configuration section %r" % section)
            continue
        settings = dict(c.items(section))
        registered_configs[section](settings)
コード例 #5
0
ファイル: config.py プロジェクト: cxh852456/pwntools
def initialize():
    """Read the configuration files."""
    from pwnlib.log import getLogger
    log = getLogger(__name__)

    c = ConfigParser.ConfigParser()
    c.read(['/etc/pwn.conf', os.path.expanduser('~/.pwn.conf')])

    for section in c.sections():
        if section not in registered_configs:
            log.warn("Unknown configuration section %r" % section)
            continue
        settings = dict(c.items(section))
        registered_configs[section](settings)
コード例 #6
0
ファイル: __init__.py プロジェクト: mr-wrmsr/BlackServerOS
def update_context_defaults(section):
    # Circular imports FTW!
    from pwnlib.util import safeeval
    from pwnlib.log import getLogger
    log = getLogger(__name__)
    for key, value in section.items():
        if key not in ContextType.defaults:
            log.warn("Unknown configuration option %r in section %r" %
                     (key, 'context'))
            continue
        if isinstance(ContextType.defaults[key], (str, unicode, tuple)):
            value = safeeval.expr(value)

        ContextType.defaults[key] = value
コード例 #7
0
ファイル: __init__.py プロジェクト: seclib/pwntool
def update_context_defaults(section):
    # Circular imports FTW!
    from pwnlib.util import safeeval
    from pwnlib.log import getLogger
    log = getLogger(__name__)
    for key, value in section.items():
        if key not in ContextType.defaults:
            log.warn("Unknown configuration option %r in section %r" % (key, 'context'))
            continue

        default = ContextType.defaults[key]

        if isinstance(default, (str, unicode, tuple, int, long, list, dict)):
            value = safeeval.expr(value)
        else:
            log.warn("Unsupported configuration option %r in section %r" % (key, 'context'))

        ContextType.defaults[key] = type(default)(value)
コード例 #8
0
def initialize():
    """Read the configuration files."""
    from pwnlib.log import getLogger
    log = getLogger(__name__)

    xdg_config_home = (os.environ.get('XDG_CONFIG_HOME')
                       or os.path.expanduser("~/.config"))

    c = configparser.ConfigParser()
    c.read([
        '/etc/pwn.conf',
        os.path.join(xdg_config_home, 'pwn.conf'),
        os.path.expanduser('~/.pwn.conf')
    ])

    for section in c.sections():
        if section not in registered_configs:
            log.warn("Unknown configuration section %r" % section)
            continue
        settings = dict(c.items(section))
        registered_configs[section](settings)
コード例 #9
0
#!/usr/bin/env python2
import argparse, string, sys
from pwnlib.util import cyclic, packing
from pwnlib.log import getLogger, install_default_handler
install_default_handler()

log = getLogger('pwnlib.commandline.cyclic')

parser = argparse.ArgumentParser(description="Cyclic pattern creator/finder")

parser.add_argument(
    '-a',
    '--alphabet',
    metavar='alphabet',
    default=string.ascii_lowercase,
    help=
    'The alphabet to use in the cyclic pattern (defaults to all lower case letters)',
)

parser.add_argument('-n',
                    '--length',
                    metavar='length',
                    default=4,
                    type=int,
                    help='Size of the unique subsequences (defaults to 4).')

group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
    '-l',
    '-o',
    '--offset',
コード例 #10
0
#!/usr/bin/python2
from pwn import *
from pwnlib.log import getLogger

elf_log = getLogger("pwnlib.tubes.process")
elf_log.setLevel("error")


def run(payload):
    elf = process('../vuln')
    elf.recvuntil('> ')
    elf.sendline(str(len(payload)))
    elf.recvuntil('Input> ')

    elf.sendline(payload)
    return elf.recvall()


def find_canary():
    # send only the buffer (32-bytes) and the next unknown canary byte
    #   if there is no "Stack Smash" the the byte is correct and we can move on to the next one
    canary_found = ""
    for i in range(4):
        with log.progress('Searching Canary') as p:
            for i in range(256):
                p.status("At %c" % chr(i))

                payload = 'A' * 32
                payload += canary_found + chr(i)
                res = run(payload)
コード例 #11
0
ファイル: toplevel.py プロジェクト: rhythmize/pwntools
from pwnlib.util.crc import BitPolynom
from pwnlib.util.cyclic import *
from pwnlib.util.fiddling import *
from pwnlib.util.getdents import *
from pwnlib.util.hashes import *
from pwnlib.util.lists import *
from pwnlib.util.misc import *
from pwnlib.util.packing import *
from pwnlib.util.proc import pidof
from pwnlib.util.sh_string import sh_string, sh_prepare, sh_command_with
from pwnlib.util.splash import *
from pwnlib.util.web import *

# Promote these modules, so that "from pwn import *" will let you access them

from six.moves import cPickle as pickle, cStringIO as StringIO
from six import BytesIO

log = getLogger("pwnlib.exploit")
error = log.error
warning = log.warning
warn = log.warning
info = log.info
debug = log.debug
success = log.success

colored_traceback.add_hook()

# Equivalence with the default behavior of "from import *"
# __all__ = [x for x in tuple(globals()) if not x.startswith('_')]
コード例 #12
0
ファイル: shellcraft.py プロジェクト: cronos91/pwntools
#!/usr/bin/env python2
import argparse, sys, os, types
import pwnlib
from pwnlib import util
import pwnlib.term.text as text
from pwnlib.context import context
from pwnlib.log import getLogger, install_default_handler
install_default_handler()

log = getLogger('pwnlib.commandline.shellcraft')

r = text.red
g = text.green
b = text.blue

banner = '\n'.join(['  ' + r('____') + '  ' + g('_') + '          ' + r('_') + ' ' + g('_') + '                 ' + b('__') + ' ' + r('_'),
                    ' ' + r('/ ___|') + g('| |__') + '   ' + b('___') + r('| |') + ' ' + g('|') + ' ' + b('___') + ' ' + r('_ __') + ' ' + g('__ _') + ' ' + b('/ _|') + ' ' + r('|_'),
                    ' ' + r('\___ \\') + g('| \'_ \\') + ' ' + b('/ _ \\') + ' ' + r('|') + ' ' + g('|') + b('/ __|') + ' ' + r('\'__/') + ' ' + g('_` |') + ' ' + b('|_') + r('| __|'),
                    '  ' + r('___) |') + ' ' + g('| | |') + '  ' + b('__/') + ' ' + r('|') + ' ' + g('|') + ' ' + b('(__') + r('| |') + ' ' + g('| (_| |') + '  ' + b('_|') + ' ' + r('|_'),
                    ' ' + r('|____/') + g('|_| |_|') + b('\\___|') + r('_|') + g('_|') + b('\\___|') + r('_|') + '  ' + g('\\__,_|') + b('_|') + '  ' + r('\\__|'),
                    '\n'
                    ])


#  ____  _          _ _                 __ _
# / ___|| |__   ___| | | ___ _ __ __ _ / _| |_
# \___ \| '_ \ / _ \ | |/ __| '__/ _` | |_| __|
#  ___) | | | |  __/ | | (__| | | (_| |  _| |_
# |____/|_| |_|\___|_|_|\___|_|  \__,_|_|  \__|

def _string(s):
コード例 #13
0
ファイル: cyclic.py プロジェクト: 601040605/pwntools
#!/usr/bin/env python2
import argparse
import string
import sys

from pwnlib.log import getLogger
from pwnlib.log import install_default_handler
from pwnlib.util import cyclic
from pwnlib.util import packing

install_default_handler()

log = getLogger('pwnlib.commandline.cyclic')

parser = argparse.ArgumentParser(
    description = "Cyclic pattern creator/finder"
)

parser.add_argument(
    '-a', '--alphabet',
    metavar = 'alphabet',
    default = string.ascii_lowercase,
    help = 'The alphabet to use in the cyclic pattern (defaults to all lower case letters)',
)

parser.add_argument(
    '-n', '--length',
    metavar = 'length',
    default = 4,
    type = int,
    help = 'Size of the unique subsequences (defaults to 4).'
コード例 #14
0
See examples/, or try starting with libc().
'''
from re import search
from glob import glob
from typing import Dict
from os import path, system
from subprocess import check_output, CalledProcessError
from pwnlib.ui import options
from pwnlib.log import getLogger
from pwnlib.util.misc import which
from pwnlib.util.lists import concat
from pwnlib.tubes.process import process
from pwnscripts.context import context
#from pwnlib.elf.elf import ELF
from pwnscripts.elf import ELF
log = getLogger('pwnlib.exploit')
__all__ = ['libc_database', 'libc']

# Helpfully taken from the one_gadget README.md
def _one_gadget(filename):
    return list(map(int, check_output(['one_gadget', '--raw', filename]).split(b' ')))

def _db(db_dir: str):
    '''Simple wrapper to return a libc_database() object for a given
    `db_dir`, using context.libc_database if db_dir is None.
    Not meant for public usage. Will raise IOError if everything is None.
    >>> _db('inexistant_dir')
    Traceback (most recent call last):
    ...
    OSError: ...
    >>> _db('libc-database')
コード例 #15
0
ファイル: logging2.py プロジェクト: haxkor/forkever
#
#   This is a very dirty hack
#   Because pwntools uses its own logging implementation, things get messy
#   when ptrace.debugger wants to make use of the standard module.
#   This logging2 module is a proxy that forwards the "critical imports"
#   by python-ptrace to pwntools
#

from logging import *
from pwnlib.log import getLogger

logger = getLogger("pwnlib")


def info(msg):
    logger.info(msg)

def debug(msg):
    logger.debug(msg)


def warning(msg):
    logger.warning(msg)


def error(msg):
    logger.error(msg)


def log(msg):
    logger.log(msg)
コード例 #16
0
def render_body(context, dest, src, stack_allowed=True, **pageargs):
    __M_caller = context.caller_stack._push_frame()
    try:
        __M_locals = __M_dict_builtin(dest=dest,
                                      src=src,
                                      pageargs=pageargs,
                                      stack_allowed=stack_allowed)
        tuple = context.get('tuple', UNDEFINED)
        int = context.get('int', UNDEFINED)
        hex = context.get('hex', UNDEFINED)
        long = context.get('long', UNDEFINED)
        str = context.get('str', UNDEFINED)
        isinstance = context.get('isinstance', UNDEFINED)
        __M_writer = context.writer()

        from pwnlib.shellcraft import eval, pretty, okay
        from pwnlib.util import lists, packing, fiddling, misc
        from pwnlib.log import getLogger
        from pwnlib.shellcraft.registers import get_register, is_register, bits_required
        log = getLogger('pwnlib.shellcraft.i386.mov')

        __M_locals_builtin_stored = __M_locals_builtin()
        __M_locals.update(
            __M_dict_builtin([
                (__M_key, __M_locals_builtin_stored[__M_key]) for __M_key in [
                    'okay', 'eval', 'misc', 'bits_required', 'lists',
                    'get_register', 'getLogger', 'packing', 'fiddling',
                    'pretty', 'is_register', 'log'
                ] if __M_key in __M_locals_builtin_stored
            ]))
        __M_writer(u'\n')
        __M_writer(u'\n')
        __M_writer(u'\n')

        src_name = src
        if not isinstance(src, (str, tuple)):
            src_name = pretty(src)

        if not get_register(dest):
            log.error('%r is not a register' % dest)

        dest = get_register(dest)

        if dest.size > 32 or dest.is64bit:
            log.error("cannot use %s on i386" % dest)

        if get_register(src):
            src = get_register(src)

            if src.is64bit:
                log.error("cannot use %s on i386" % src)

            if dest.size < src.size and src.name not in dest.bigger:
                log.error("cannot mov %s, %s: dddest is smaller than src" %
                          (dest, src))
        else:
            src = eval(src)

            if not dest.fits(src):
                log.error("cannot mov %s, %r: dest is smaller than src" %
                          (dest, src))

            src_size = bits_required(src)

            # Calculate the packed version
            mask = ((1 << 32) - 1)
            masked = src & mask
            srcp = packing.pack(masked, dest.size)

            # Calculate the unsigned and signed versions
            srcu = packing.unpack(srcp, dest.size, sign=False)
            srcs = packing.unpack(srcp, dest.size, sign=True)

            srcp_not = packing.pack(fiddling.bnot(masked))
            srcp_neg = packing.pack(fiddling.negate(masked))
            srcu_not = packing.unpack(srcp_not)
            srcu_neg = packing.unpack(srcp_neg)

        __M_locals_builtin_stored = __M_locals_builtin()
        __M_locals.update(
            __M_dict_builtin([
                (__M_key, __M_locals_builtin_stored[__M_key]) for __M_key in [
                    'src', 'srcp_neg', 'src_size', 'dest', 'mask', 'srcu_not',
                    'srcp_not', 'srcs', 'srcp', 'masked', 'srcu', 'src_name',
                    'srcu_neg'
                ] if __M_key in __M_locals_builtin_stored
            ]))
        if is_register(src):
            if src == dest:
                __M_writer(u'    /* moving ')
                __M_writer(unicode(src))
                __M_writer(u' into ')
                __M_writer(unicode(dest))
                __M_writer(u', but this is a no-op */\n')
            elif src.name in dest.bigger:
                __M_writer(u'    /* moving ')
                __M_writer(unicode(src))
                __M_writer(u' into ')
                __M_writer(unicode(dest))
                __M_writer(u', but this is a no-op */\n')
            elif dest.size > src.size:
                __M_writer(u'    movzx ')
                __M_writer(unicode(dest))
                __M_writer(u', ')
                __M_writer(unicode(src))
                __M_writer(u'\n')
            else:
                __M_writer(u'    mov ')
                __M_writer(unicode(dest))
                __M_writer(u', ')
                __M_writer(unicode(src))
                __M_writer(u'\n')
        elif isinstance(src, (int, long)):
            if src == 0:
                __M_writer(u'        xor ')
                __M_writer(unicode(dest))
                __M_writer(u', ')
                __M_writer(unicode(dest))
                __M_writer(u'\n')
            elif stack_allowed and dest.size == 32 and src == 10:
                __M_writer(u'        push 9 /* mov ')
                __M_writer(unicode(dest))
                __M_writer(u", '\\n' */\n        pop ")
                __M_writer(unicode(dest))
                __M_writer(u'\n        inc ')
                __M_writer(unicode(dest))
                __M_writer(u'\n')
            elif stack_allowed and dest.size == 32 and okay(srcp):
                __M_writer(u'        push ')
                __M_writer(unicode(pretty(src)))
                __M_writer(u'\n        pop ')
                __M_writer(unicode(dest))
                __M_writer(u'\n')
            elif stack_allowed and dest.size == 32 and -127 <= srcs < 128 and okay(
                    srcp[0]):
                __M_writer(u'        push ')
                __M_writer(unicode(pretty(src)))
                __M_writer(u'\n        pop ')
                __M_writer(unicode(dest))
                __M_writer(u'\n')
            elif okay(srcp):
                __M_writer(u'        mov ')
                __M_writer(unicode(dest))
                __M_writer(u', ')
                __M_writer(unicode(pretty(src)))
                __M_writer(u'\n')
            elif 0 <= srcu < 2**8 and okay(srcp[0]) and dest.sizes.get(8, 0):
                __M_writer(u'        xor ')
                __M_writer(unicode(dest))
                __M_writer(u', ')
                __M_writer(unicode(dest))
                __M_writer(u'\n        mov ')
                __M_writer(unicode(dest.sizes[8]))
                __M_writer(u', ')
                __M_writer(unicode(pretty(srcu)))
                __M_writer(u'\n')
            elif srcu == (srcu & 0xff00) and okay(srcp[1]) and dest.ff00:
                __M_writer(u'        xor ')
                __M_writer(unicode(dest))
                __M_writer(u', ')
                __M_writer(unicode(dest))
                __M_writer(u'\n        mov ')
                __M_writer(unicode(dest.ff00))
                __M_writer(u', ')
                __M_writer(unicode(pretty(src)))
                __M_writer(u' >> 8\n')
            elif 0 <= srcu < 2**16 and okay(srcp[:2]) and dest.sizes[16]:
                __M_writer(u'        xor ')
                __M_writer(unicode(dest))
                __M_writer(u', ')
                __M_writer(unicode(dest))
                __M_writer(u'\n        mov ')
                __M_writer(unicode(dest.sizes[16]))
                __M_writer(u', ')
                __M_writer(unicode(pretty(src)))
                __M_writer(u'\n')
            elif okay(srcp_neg):
                __M_writer(u'        mov ')
                __M_writer(unicode(dest))
                __M_writer(u', -')
                __M_writer(unicode(pretty(src)))
                __M_writer(u'\n        neg ')
                __M_writer(unicode(dest))
                __M_writer(u'\n')
            elif okay(srcp_not):
                __M_writer(u'        mov ')
                __M_writer(unicode(dest))
                __M_writer(u', (-1) ^ ')
                __M_writer(unicode(pretty(src)))
                __M_writer(u'\n        not ')
                __M_writer(unicode(dest))
                __M_writer(u'\n')
            else:
                __M_writer(u'        ')

                a, b = fiddling.xor_pair(srcp, avoid='\x00\n')
                a = hex(packing.unpack(a, dest.size))
                b = hex(packing.unpack(b, dest.size))

                __M_locals_builtin_stored = __M_locals_builtin()
                __M_locals.update(
                    __M_dict_builtin([
                        (__M_key, __M_locals_builtin_stored[__M_key])
                        for __M_key in ['a', 'b']
                        if __M_key in __M_locals_builtin_stored
                    ]))
                __M_writer(u'        mov ')
                __M_writer(unicode(dest))
                __M_writer(u', ')
                __M_writer(unicode(a))
                __M_writer(u'\n        xor ')
                __M_writer(unicode(dest))
                __M_writer(u', ')
                __M_writer(unicode(a))
                __M_writer(u' ^ ')
                __M_writer(unicode(pretty(src)))
                __M_writer(u'\n')
        return ''
    finally:
        context.caller_stack._pop_frame()
コード例 #17
0
ファイル: config.py プロジェクト: mathyba/snowcrash
PORT = "PORT"
USER = "******"
LEVEL_PATH = "LEVEL_PATH"
REMOTE_PATH = "REMOTE_PATH"
LOG_LEVEL = "LOG_LEVEL"
CONTAINER = "CONTAINER"

RED = "RED"
CYAN = "CYAN"
YELLOW = "YELLOW"
GREEN = "GREEN"

# Environment variables are set in the Dockerfile or via the docker-compose.yml
host = os.environ.get(HOST, "localhost")
vm = os.environ.get(VM, "localhost")
container = os.environ.get(CONTAINER, "localhost")
port = os.environ.get(PORT, 22)
user = os.environ.get(USER, "snowcrash")
local_level_path = os.environ.get(LEVEL_PATH, "/{user}")
remote_path = os.environ.get(REMOTE_PATH, "/")
log_level = os.environ.get(LOG_LEVEL, "info")

loggers = [
    logger.setLevel(log_level) for logger in [
        getLogger("pwnlib.tubes.ssh"),
        getLogger("paramiko.transport"),
        getLogger("pwnlib.elf.elf"),
        getLogger("pwnlib.asm"),
    ]
]
コード例 #18
0
ファイル: solve.py プロジェクト: naweiss/picoCTF2018
#!/usr/bin/python2
from pwn import *
from pwnlib.log import getLogger
import os

remote_log = getLogger("pwnlib.tubes.remote")
remote_log.setLevel("error")

elf_log = getLogger("pwnlib.tubes.process")
elf_log.setLevel("error")

def getProcess(is_remote = True):
    if is_remote:
        return remote('2018shell1.picoctf.com', 23397)
    else:
        return process('../echo')

def find_index():
    with log.progress('Trying offsets') as loger:
        for i in range(1, 60):
            loger.status("At %d" % i)
            
            # Do all the calculations locally
            p = getProcess(is_remote=False)
            p.recvuntil('> ')
            # Print the string at the i-th offset
            p.sendline('%%%d$s' % i)
            
            if 'flag' in p.recv():
                loger.success('Found at #%d' % i)
                return i
コード例 #19
0
import base64
import errno
import os
import platform
import re
import socket
import stat
import string

from pwnlib.context import context
from pwnlib.log import getLogger
from pwnlib.util import fiddling
from pwnlib.util import lists

log = getLogger(__name__)

def align(alignment, x):
    """align(alignment, x) -> int

    Rounds `x` up to nearest multiple of the `alignment`.

    Example:
      >>> [align(5, n) for n in range(15)]
      [0, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 15, 15, 15, 15]
    """
    return ((x + alignment - 1) // alignment) * alignment


def align_down(alignment, x):
    """align_down(alignment, x) -> int
コード例 #20
0
#!/usr/bin/env python2
import argparse, sys, os, types
import pwnlib
from pwnlib import util
import pwnlib.term.text as text
from pwnlib import shellcraft
from pwnlib.context import context
from pwnlib.log import getLogger, install_default_handler
install_default_handler()


log = getLogger('pwnlib.commandline.shellcraft')

r = text.red
g = text.green
b = text.blue

banner = '\n'.join(['  ' + r('____') + '  ' + g('_') + '          ' + r('_') + ' ' + g('_') + '                 ' + b('__') + ' ' + r('_'),
                    ' ' + r('/ ___|') + g('| |__') + '   ' + b('___') + r('| |') + ' ' + g('|') + ' ' + b('___') + ' ' + r('_ __') + ' ' + g('__ _') + ' ' + b('/ _|') + ' ' + r('|_'),
                    ' ' + r('\___ \\') + g('| \'_ \\') + ' ' + b('/ _ \\') + ' ' + r('|') + ' ' + g('|') + b('/ __|') + ' ' + r('\'__/') + ' ' + g('_` |') + ' ' + b('|_') + r('| __|'),
                    '  ' + r('___) |') + ' ' + g('| | |') + '  ' + b('__/') + ' ' + r('|') + ' ' + g('|') + ' ' + b('(__') + r('| |') + ' ' + g('| (_| |') + '  ' + b('_|') + ' ' + r('|_'),
                    ' ' + r('|____/') + g('|_| |_|') + b('\\___|') + r('_|') + g('_|') + b('\\___|') + r('_|') + '  ' + g('\\__,_|') + b('_|') + '  ' + r('\\__|'),
                    '\n'
                    ])


#  ____  _          _ _                 __ _
# / ___|| |__   ___| | | ___ _ __ __ _ / _| |_
# \___ \| '_ \ / _ \ | |/ __| '__/ _` | |_| __|
#  ___) | | | |  __/ | | (__| | | (_| |  _| |_
# |____/|_| |_|\___|_|_|\___|_|  \__,_|_|  \__|