# SPDX-License-Identifier: GPL-2.0-only # This file is part of Scapy # See https://scapy.net/ for more information # Copyright (C) Philippe Biondi # Copyright (C) Michael Farrell # Copyright (C) Gauthier Sebaux """ Fields that hold random numbers. """ import copy import random import time import math import re import uuid import struct import string from scapy.base_classes import Net from scapy.compat import bytes_encode, chb, plain_str from scapy.utils import corrupt_bits, corrupt_bytes from typing import ( List, TypeVar, Generic, Set, Union, Any, Dict, Optional, Tuple, cast, ) #################### # Random numbers # #################### class RandomEnumeration: """iterate through a sequence in random order. When all the values have been drawn, if forever=1, the drawing is done again. # noqa: E501 If renewkeys=0, the draw will be in the same order, guaranteeing that the same # noqa: E501 number will be drawn in not less than the number of integers of the sequence""" # noqa: E501 def __init__(self, inf, sup, seed=None, forever=1, renewkeys=0): # type: (int, int, Optional[int], int, int) -> None self.forever = forever self.renewkeys = renewkeys self.inf = inf self.rnd = random.Random(seed) self.sbox_size = 256 self.top = sup - inf + 1 n = 0 while (1 << n) < self.top: n += 1 self.n = n self.fs = min(3, (n + 1) // 2) self.fsmask = 2**self.fs - 1 self.rounds = max(self.n, 3) self.turns = 0 self.i = 0 def __iter__(self): # type: () -> RandomEnumeration return self def next(self): # type: () -> int while True: if self.turns == 0 or (self.i == 0 and self.renewkeys): self.cnt_key = self.rnd.randint(0, 2**self.n - 1) self.sbox = [self.rnd.randint(0, self.fsmask) for _ in range(self.sbox_size)] self.turns += 1 while self.i < 2**self.n: ct = self.i ^ self.cnt_key self.i += 1 for _ in range(self.rounds): # Unbalanced Feistel Network lsb = ct & self.fsmask ct >>= self.fs lsb ^= self.sbox[ct % self.sbox_size] ct |= lsb << (self.n - self.fs) if ct < self.top: return self.inf + ct self.i = 0 if not self.forever: raise StopIteration __next__ = next _T = TypeVar('_T') class VolatileValue(Generic[_T]): def __repr__(self): # type: () -> str return "<%s>" % self.__class__.__name__ def _command_args(self): # type: () -> str return '' def command(self, json=False): # type: (bool) -> Union[Dict[str, str], str] if json: return {"type": self.__class__.__name__, "value": self._command_args()} else: return "%s(%s)" % (self.__class__.__name__, self._command_args()) def __eq__(self, other): # type: (Any) -> bool x = self._fix() y = other._fix() if isinstance(other, VolatileValue) else other if not isinstance(x, type(y)): return False return bool(x == y) def __ne__(self, other): # type: (Any) -> bool # Python 2.7 compat return not self == other __hash__ = None # type: ignore def __getattr__(self, attr): # type: (str) -> Any if attr in ["__setstate__", "__getstate__"]: raise AttributeError(attr) return getattr(self._fix(), attr) def __str__(self): # type: () -> str return str(self._fix()) def __bytes__(self): # type: () -> bytes return bytes_encode(self._fix()) def __len__(self): # type: () -> int # Does not work for some types (int?) return len(self._fix()) # type: ignore def copy(self): # type: () -> Any return copy.copy(self) def _fix(self): # type: () -> _T return cast(_T, None) class RandField(VolatileValue[_T], Generic[_T]): pass _I = TypeVar("_I", int, float) class _RandNumeral(RandField[_I]): """Implements integer management in RandField""" def __int__(self): # type: () -> int return int(self._fix()) def __index__(self): # type: () -> int return int(self) def __nonzero__(self): # type: () -> bool return bool(self._fix()) __bool__ = __nonzero__ def __add__(self, other): # type: (_I) -> _I return self._fix() + other def __radd__(self, other): # type: (_I) -> _I return other + self._fix() def __sub__(self, other): # type: (_I) -> _I return self._fix() - other def __rsub__(self, other): # type: (_I) -> _I return other - self._fix() def __mul__(self, other): # type: (_I) -> _I return self._fix() * other def __rmul__(self, other): # type: (_I) -> _I return other * self._fix() def __floordiv__(self, other): # type: (_I) -> float return self._fix() / other __div__ = __floordiv__ def __lt__(self, other): # type: (_I) -> bool return self._fix() < other def __le__(self, other): # type: (_I) -> bool return self._fix() <= other def __ge__(self, other): # type: (_I) -> bool return self._fix() >= other def __gt__(self, other): # type: (_I) -> bool return self._fix() > other class RandNum(_RandNumeral[int]): """Instances evaluate to random integers in selected range""" min = 0 max = 0 def __init__(self, min, max): # type: (int, int) -> None self.min = min self.max = max def _command_args(self): # type: () -> str if self.__class__.__name__ == 'RandNum': return "min=%r, max=%r" % (self.min, self.max) return super(RandNum, self)._command_args() def _fix(self): # type: () -> int return random.randrange(self.min, self.max + 1) def __lshift__(self, other): # type: (int) -> int return self._fix() << other def __rshift__(self, other): # type: (int) -> int return self._fix() >> other def __and__(self, other): # type: (int) -> int return self._fix() & other def __rand__(self, other): # type: (int) -> int return other & self._fix() def __or__(self, other): # type: (int) -> int return self._fix() | other def __ror__(self, other): # type: (int) -> int return other | self._fix() class RandFloat(_RandNumeral[float]): def __init__(self, min, max): # type: (int, int) -> None self.min = min self.max = max def _fix(self): # type: () -> float return random.uniform(self.min, self.max) class RandBinFloat(RandFloat): def _fix(self): # type: () -> float return cast( float, struct.unpack("!f", bytes(RandBin(4)))[0] ) class RandNumGamma(RandNum): def __init__(self, alpha, beta): # type: (int, int) -> None self.alpha = alpha self.beta = beta def _command_args(self): # type: () -> str return "alpha=%r, beta=%r" % (self.alpha, self.beta) def _fix(self): # type: () -> int return int(round(random.gammavariate(self.alpha, self.beta))) class RandNumGauss(RandNum): def __init__(self, mu, sigma): # type: (int, int) -> None self.mu = mu self.sigma = sigma def _command_args(self): # type: () -> str return "mu=%r, sigma=%r" % (self.mu, self.sigma) def _fix(self): # type: () -> int return int(round(random.gauss(self.mu, self.sigma))) class RandNumExpo(RandNum): def __init__(self, lambd, base=0): # type: (float, int) -> None self.lambd = lambd self.base = base def _command_args(self): # type: () -> str ret = "lambd=%r" % self.lambd if self.base != 0: ret += ", base=%r" % self.base return ret def _fix(self): # type: () -> int return self.base + int(round(random.expovariate(self.lambd))) class RandEnum(RandNum): """Instances evaluate to integer sampling without replacement from the given interval""" # noqa: E501 def __init__(self, min, max, seed=None): # type: (int, int, Optional[int]) -> None self._seed = seed self.seq = RandomEnumeration(min, max, seed) super(RandEnum, self).__init__(min, max) def _command_args(self): # type: () -> str ret = "min=%r, max=%r" % (self.min, self.max) if self._seed: ret += ", seed=%r" % self._seed return ret def _fix(self): # type: () -> int return next(self.seq) class RandByte(RandNum): def __init__(self): # type: () -> None RandNum.__init__(self, 0, 2**8 - 1) class RandSByte(RandNum): def __init__(self): # type: () -> None RandNum.__init__(self, -2**7, 2**7 - 1) class RandShort(RandNum): def __init__(self): # type: () -> None RandNum.__init__(self, 0, 2**16 - 1) class RandSShort(RandNum): def __init__(self): # type: () -> None RandNum.__init__(self, -2**15, 2**15 - 1) class RandInt(RandNum): def __init__(self): # type: () -> None RandNum.__init__(self, 0, 2**32 - 1) class RandSInt(RandNum): def __init__(self): # type: () -> None RandNum.__init__(self, -2**31, 2**31 - 1) class RandLong(RandNum): def __init__(self): # type: () -> None RandNum.__init__(self, 0, 2**64 - 1) class RandSLong(RandNum): def __init__(self): # type: () -> None RandNum.__init__(self, -2**63, 2**63 - 1) class RandEnumByte(RandEnum): def __init__(self): # type: () -> None RandEnum.__init__(self, 0, 2**8 - 1) class RandEnumSByte(RandEnum): def __init__(self): # type: () -> None RandEnum.__init__(self, -2**7, 2**7 - 1) class RandEnumShort(RandEnum): def __init__(self): # type: () -> None RandEnum.__init__(self, 0, 2**16 - 1) class RandEnumSShort(RandEnum): def __init__(self): # type: () -> None RandEnum.__init__(self, -2**15, 2**15 - 1) class RandEnumInt(RandEnum): def __init__(self): # type: () -> None RandEnum.__init__(self, 0, 2**32 - 1) class RandEnumSInt(RandEnum): def __init__(self): # type: () -> None RandEnum.__init__(self, -2**31, 2**31 - 1) class RandEnumLong(RandEnum): def __init__(self): # type: () -> None RandEnum.__init__(self, 0, 2**64 - 1) class RandEnumSLong(RandEnum): def __init__(self): # type: () -> None RandEnum.__init__(self, -2**63, 2**63 - 1) class RandEnumKeys(RandEnum): """Picks a random value from dict keys list. """ def __init__(self, enum, seed=None): # type: (Dict[Any, Any], Optional[int]) -> None self.enum = list(enum) RandEnum.__init__(self, 0, len(self.enum) - 1, seed) def _command_args(self): # type: () -> str # Note: only outputs the list of keys, but values are irrelevant anyway ret = "enum=%r" % self.enum if self._seed: ret += ", seed=%r" % self._seed return ret def _fix(self): # type: () -> Any return self.enum[next(self.seq)] class RandChoice(RandField[Any]): def __init__(self, *args): # type: (*Any) -> None if not args: raise TypeError("RandChoice needs at least one choice") self._choice = list(args) def _command_args(self): # type: () -> str return ", ".join(self._choice) def _fix(self): # type: () -> Any return random.choice(self._choice) _S = TypeVar("_S", bytes, str) class _RandString(RandField[_S], Generic[_S]): def __str__(self): # type: () -> str return plain_str(self._fix()) def __bytes__(self): # type: () -> bytes return bytes_encode(self._fix()) def __mul__(self, n): # type: (int) -> _S return self._fix() * n class RandString(_RandString[str]): _DEFAULT_CHARS = (string.ascii_uppercase + string.ascii_lowercase + string.digits) def __init__(self, size=None, chars=_DEFAULT_CHARS): # type: (Optional[Union[int, RandNum]], str) -> None if size is None: size = RandNumExpo(0.01) self.size = size self.chars = chars def _command_args(self): # type: () -> str ret = "" if isinstance(self.size, VolatileValue): if self.size.lambd != 0.01 or self.size.base != 0: ret += "size=%r" % self.size.command() else: ret += "size=%r" % self.size if self.chars != self._DEFAULT_CHARS: ret += ", chars=%r" % self.chars return ret def _fix(self): # type: () -> str s = "" for _ in range(int(self.size)): s += random.choice(self.chars) return s class RandBin(_RandString[bytes]): _DEFAULT_CHARS = b"".join(chb(c) for c in range(256)) def __init__(self, size=None, chars=_DEFAULT_CHARS): # type: (Optional[Union[int, RandNum]], bytes) -> None if size is None: size = RandNumExpo(0.01) self.size = size self.chars = chars def _command_args(self): # type: () -> str if not isinstance(self.size, VolatileValue): return "size=%r" % self.size if isinstance(self.size, RandNumExpo) and \ self.size.lambd == 0.01 and self.size.base == 0: # Default size for RandString, skip return "" return "size=%r" % self.size.command() def _fix(self): # type: () -> bytes s = b"" for _ in range(int(self.size)): s += struct.pack("!B", random.choice(self.chars)) return s class RandTermString(RandBin): def __init__(self, size, term): # type: (Union[int, RandNum], bytes) -> None self.term = bytes_encode(term) super(RandTermString, self).__init__(size=size) self.chars = self.chars.replace(self.term, b"") def _command_args(self): # type: () -> str return ", ".join((super(RandTermString, self)._command_args(), "term=%r" % self.term)) def _fix(self): # type: () -> bytes return RandBin._fix(self) + self.term class RandIP(_RandString[str]): _DEFAULT_IPTEMPLATE = "0.0.0.0/0" def __init__(self, iptemplate=_DEFAULT_IPTEMPLATE): # type: (str) -> None super(RandIP, self).__init__() self.ip = Net(iptemplate) def _command_args(self): # type: () -> str rep = "%s/%s" % (self.ip.net, self.ip.mask) if rep == self._DEFAULT_IPTEMPLATE: return "" return "iptemplate=%r" % rep def _fix(self): # type: () -> str return self.ip.choice() class RandMAC(_RandString[str]): def __init__(self, _template="*"): # type: (str) -> None super(RandMAC, self).__init__() self._template = _template _template += ":*:*:*:*:*" template = _template.split(":") self.mac = () # type: Tuple[Union[int, RandNum], ...] for i in range(6): v = 0 # type: Union[int, RandNum] if template[i] == "*": v = RandByte() elif "-" in template[i]: x, y = template[i].split("-") v = RandNum(int(x, 16), int(y, 16)) else: v = int(template[i], 16) self.mac += (v,) def _command_args(self): # type: () -> str if self._template == "*": return "" return "template=%r" % self._template def _fix(self): # type: () -> str return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac # type: ignore class RandIP6(_RandString[str]): def __init__(self, ip6template="**"): # type: (str) -> None super(RandIP6, self).__init__() self.tmpl = ip6template self.sp = [] # type: List[Union[int, RandNum, str]] for v in self.tmpl.split(":"): if not v or v == "**": self.sp.append(v) continue if "-" in v: a, b = v.split("-") elif v == "*": a = b = "" else: a = b = v if not a: a = "0" if not b: b = "ffff" if a == b: self.sp.append(int(a, 16)) else: self.sp.append(RandNum(int(a, 16), int(b, 16))) self.variable = "" in self.sp self.multi = self.sp.count("**") def _command_args(self): # type: () -> str if self.tmpl == "**": return "" return "ip6template=%r" % self.tmpl def _fix(self): # type: () -> str nbm = self.multi ip = [] # type: List[str] for i, n in enumerate(self.sp): if n == "**": nbm -= 1 remain = 8 - (len(self.sp) - i - 1) - len(ip) + nbm if "" in self.sp: remain += 1 if nbm or self.variable: remain = random.randint(0, remain) for j in range(remain): ip.append("%04x" % random.randint(0, 65535)) elif isinstance(n, RandNum): ip.append("%04x" % int(n)) elif n == 0: ip.append("0") elif not n: ip.append("") else: ip.append("%04x" % int(n)) if len(ip) == 9: ip.remove("") if ip[-1] == "": ip[-1] = "0" return ":".join(ip) class RandOID(_RandString[str]): def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)): # noqa: E501 # type: (Optional[str], RandNumExpo, RandNumExpo) -> None super(RandOID, self).__init__() self.ori_fmt = fmt self.fmt = None # type: Optional[List[Union[str, Tuple[int, ...]]]] if fmt is not None: self.fmt = [ tuple(map(int, x.split("-"))) if "-" in x else x for x in fmt.split(".") ] self.depth = depth self.idnum = idnum def _command_args(self): # type: () -> str ret = [] if self.fmt: ret.append("fmt=%r" % self.ori_fmt) if not isinstance(self.depth, VolatileValue): ret.append("depth=%r" % self.depth) elif not isinstance(self.depth, RandNumExpo) or \ self.depth.lambd != 0.1 or self.depth.base != 0: ret.append("depth=%s" % self.depth.command()) if not isinstance(self.idnum, VolatileValue): ret.append("idnum=%r" % self.idnum) elif not isinstance(self.idnum, RandNumExpo) or \ self.idnum.lambd != 0.01 or self.idnum.base != 0: ret.append("idnum=%s" % self.idnum.command()) return ", ".join(ret) def __repr__(self): # type: () -> str if self.ori_fmt is None: return "<%s>" % self.__class__.__name__ else: return "<%s [%s]>" % (self.__class__.__name__, self.ori_fmt) def _fix(self): # type: () -> str if self.fmt is None: return ".".join(str(self.idnum) for _ in range(1 + self.depth)) else: oid = [] for i in self.fmt: if i == "*": oid.append(str(self.idnum)) elif i == "**": oid += [str(self.idnum) for i in range(1 + self.depth)] elif isinstance(i, tuple): oid.append(str(random.randrange(*i))) else: oid.append(i) return ".".join(oid) class RandRegExp(RandField[str]): def __init__(self, regexp, lambda_=0.3): # type: (str, float) -> None self._regexp = regexp self._lambda = lambda_ def _command_args(self): # type: () -> str ret = "regexp=%r" % self._regexp if self._lambda != 0.3: ret += ", lambda_=%r" % self._lambda return ret special_sets = { "[:alnum:]": "[a-zA-Z0-9]", "[:alpha:]": "[a-zA-Z]", "[:ascii:]": "[\x00-\x7F]", "[:blank:]": "[ \t]", "[:cntrl:]": "[\x00-\x1F\x7F]", "[:digit:]": "[0-9]", "[:graph:]": "[\x21-\x7E]", "[:lower:]": "[a-z]", "[:print:]": "[\x20-\x7E]", "[:punct:]": "[!\"\\#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^_{|}~]", "[:space:]": "[ \t\r\n\v\f]", "[:upper:]": "[A-Z]", "[:word:]": "[A-Za-z0-9_]", "[:xdigit:]": "[A-Fa-f0-9]", } @staticmethod def choice_expand(s): # type: (str) -> str m = "" invert = s and s[0] == "^" while True: p = s.find("-") if p < 0: break if p == 0 or p == len(s) - 1: m = "-" if p: s = s[:-1] else: s = s[1:] else: c1 = s[p - 1] c2 = s[p + 1] rng = "".join(map(chr, range(ord(c1), ord(c2) + 1))) s = s[:p - 1] + rng + s[p + 1:] res = m + s if invert: res = "".join(chr(x) for x in range(256) if chr(x) not in res) return res @staticmethod def stack_fix(lst, index): # type: (List[Any], List[Any]) -> str r = "" mul = 1 for e in lst: if isinstance(e, list): if mul != 1: mul = mul - 1 r += RandRegExp.stack_fix(e[1:] * mul, index) # only the last iteration should be kept for back reference f = RandRegExp.stack_fix(e[1:], index) for i, idx in enumerate(index): if e is idx: index[i] = f r += f mul = 1 elif isinstance(e, tuple): kind, val = e if kind == "cite": r += index[val - 1] elif kind == "repeat": mul = val elif kind == "choice": if mul == 1: c = random.choice(val) r += RandRegExp.stack_fix(c[1:], index) else: r += RandRegExp.stack_fix([e] * mul, index) mul = 1 else: if mul != 1: r += RandRegExp.stack_fix([e] * mul, index) mul = 1 else: r += str(e) return r def _fix(self): # type: () -> str stack = [None] index = [] # Give up on typing this current = stack # type: Any i = 0 regexp = self._regexp for k, v in self.special_sets.items(): regexp = regexp.replace(k, v) ln = len(regexp) interp = True while i < ln: c = regexp[i] i += 1 if c == '(': current = [current] current[0].append(current) elif c == '|': p = current[0] ch = p[-1] if not isinstance(ch, tuple): ch = ("choice", [current]) p[-1] = ch else: ch[1].append(current) current = [p] elif c == ')': ch = current[0][-1] if isinstance(ch, tuple): ch[1].append(current) index.append(current) current = current[0] elif c == '[' or c == '{': current = [current] current[0].append(current) interp = False elif c == ']': current = current[0] choice = RandRegExp.choice_expand("".join(current.pop()[1:])) current.append(RandChoice(*list(choice))) interp = True elif c == '}': current = current[0] num = "".join(current.pop()[1:]) e = current.pop() if "," not in num: current.append([current] + [e] * int(num)) else: num_min, num_max = num.split(",") if not num_min: num_min = "0" if num_max: n = RandNum(int(num_min), int(num_max)) else: n = RandNumExpo(self._lambda, base=int(num_min)) current.append(("repeat", n)) current.append(e) interp = True elif c == '\\': c = regexp[i] if c == "s": current.append(RandChoice(" ", "\t")) elif c in "0123456789": current.append("cite", ord(c) - 0x30) i += 1 elif not interp: current.append(c) elif c == '+': e = current.pop() current.append([current] + [e] * (int(random.expovariate(self._lambda)) + 1)) # noqa: E501 elif c == '*': e = current.pop() current.append([current] + [e] * int(random.expovariate(self._lambda))) # noqa: E501 elif c == '?': if random.randint(0, 1): current.pop() elif c == '.': current.append(RandChoice(*[chr(x) for x in range(256)])) elif c == '$' or c == '^': pass else: current.append(c) return RandRegExp.stack_fix(stack[1:], index) def __repr__(self): # type: () -> str return "<%s [%r]>" % (self.__class__.__name__, self._regexp) class RandSingularity(RandChoice): pass class RandSingNum(RandSingularity): @staticmethod def make_power_of_two(end): # type: (int) -> Set[int] sign = 1 if end == 0: end = 1 if end < 0: end = -end sign = -1 end_n = int(math.log(end) / math.log(2)) + 1 return {sign * 2**i for i in range(end_n)} def __init__(self, mn, mx): # type: (int, int) -> None self._mn = mn self._mx = mx sing = {0, mn, mx, int((mn + mx) / 2)} sing |= self.make_power_of_two(mn) sing |= self.make_power_of_two(mx) for i in sing.copy(): sing.add(i + 1) sing.add(i - 1) for i in sing.copy(): if not mn <= i <= mx: sing.remove(i) super(RandSingNum, self).__init__(*sing) self._choice.sort() def _command_args(self): # type: () -> str if self.__class__.__name__ == 'RandSingNum': return "mn=%r, mx=%r" % (self._mn, self._mx) return super(RandSingNum, self)._command_args() class RandSingByte(RandSingNum): def __init__(self): # type: () -> None RandSingNum.__init__(self, 0, 2**8 - 1) class RandSingSByte(RandSingNum): def __init__(self): # type: () -> None RandSingNum.__init__(self, -2**7, 2**7 - 1) class RandSingShort(RandSingNum): def __init__(self): # type: () -> None RandSingNum.__init__(self, 0, 2**16 - 1) class RandSingSShort(RandSingNum): def __init__(self): # type: () -> None RandSingNum.__init__(self, -2**15, 2**15 - 1) class RandSingInt(RandSingNum): def __init__(self): # type: () -> None RandSingNum.__init__(self, 0, 2**32 - 1) class RandSingSInt(RandSingNum): def __init__(self): # type: () -> None RandSingNum.__init__(self, -2**31, 2**31 - 1) class RandSingLong(RandSingNum): def __init__(self): # type: () -> None RandSingNum.__init__(self, 0, 2**64 - 1) class RandSingSLong(RandSingNum): def __init__(self): # type: () -> None RandSingNum.__init__(self, -2**63, 2**63 - 1) class RandSingString(RandSingularity): def __init__(self): # type: () -> None choices_list = ["", "%x", "%%", "%s", "%i", "%n", "%x%x%x%x%x%x%x%x%x", "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", "%", "%%%", "A" * 4096, b"\x00" * 4096, b"\xff" * 4096, b"\x7f" * 4096, b"\x80" * 4096, " " * 4096, "\\" * 4096, "(" * 4096, "../" * 1024, "/" * 1024, "${HOME}" * 512, " or 1=1 --", "' or 1=1 --", '" or 1=1 --', " or 1=1; #", "' or 1=1; #", '" or 1=1; #', ";reboot;", "$(reboot)", "`reboot`", "index.php%00", b"\x00", "%00", "\\", "../../../../../../../../../../../../../../../../../etc/passwd", # noqa: E501 "%2e%2e%2f" * 20 + "etc/passwd", "%252e%252e%252f" * 20 + "boot.ini", "..%c0%af" * 20 + "etc/passwd", "..%c0%af" * 20 + "boot.ini", "//etc/passwd", r"..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\boot.ini", # noqa: E501 "AUX:", "CLOCK$", "COM:", "CON:", "LPT:", "LST:", "NUL:", "CON:", r"C:\CON\CON", r"C:\boot.ini", r"\\myserver\share", "foo.exe:", "foo.exe\\", ] super(RandSingString, self).__init__(*choices_list) def _command_args(self): # type: () -> str return "" def __str__(self): # type: () -> str return str(self._fix()) def __bytes__(self): # type: () -> bytes return bytes_encode(self._fix()) class RandPool(RandField[VolatileValue[Any]]): def __init__(self, *args): # type: (*Tuple[VolatileValue[Any], int]) -> None """Each parameter is a volatile object or a couple (volatile object, weight)""" # noqa: E501 self._args = args pool = [] # type: List[VolatileValue[Any]] for p in args: w = 1 if isinstance(p, tuple): p, w = p # type: ignore pool += [cast(VolatileValue[Any], p)] * w self._pool = pool def _command_args(self): # type: () -> str ret = [] for p in self._args: if isinstance(p, tuple): ret.append("(%s, %r)" % (p[0].command(), p[1])) else: ret.append(p.command()) return ", ".join(ret) def _fix(self): # type: () -> Any r = random.choice(self._pool) return r._fix() class RandUUID(RandField[uuid.UUID]): """Generates a random UUID. By default, this generates a RFC 4122 version 4 UUID (totally random). See Python's ``uuid`` module documentation for more information. Args: template (optional): A template to build the UUID from. Not valid with any other option. node (optional): A 48-bit Host ID. Only valid for version 1 (where it is optional). clock_seq (optional): An integer of up to 14-bits for the sequence number. Only valid for version 1 (where it is optional). namespace: A namespace identifier, which is also a UUID. Required for versions 3 and 5, must be omitted otherwise. name: string, required for versions 3 and 5, must be omitted otherwise. version: Version of UUID to use (1, 3, 4 or 5). If omitted, attempts to guess which version to generate, defaulting to version 4 (totally random). Raises: ValueError: on invalid constructor arguments """ # This was originally scapy.contrib.dce_rpc.RandUUID. _BASE = "([0-9a-f]{{{0}}}|\\*|[0-9a-f]{{{0}}}:[0-9a-f]{{{0}}})" _REG = re.compile( r"^{0}-?{1}-?{1}-?{2}{2}-?{2}{2}{2}{2}{2}{2}$".format( _BASE.format(8), _BASE.format(4), _BASE.format(2) ), re.I ) VERSIONS = [1, 3, 4, 5] def __init__(self, template=None, # type: Optional[Any] node=None, # type: Optional[int] clock_seq=None, # type: Optional[int] namespace=None, # type: Optional[uuid.UUID] name=None, # type: Optional[str] version=None, # type: Optional[Any] ): # type: (...) -> None self._template = template self._ori_version = version self.uuid_template = None self.clock_seq = None self.namespace = None self.name = None self.node = None self.version = None if template: if node or clock_seq or namespace or name or version: raise ValueError("UUID template must be the only parameter, " "if specified") tmp = RandUUID._REG.match(template) if tmp: template = tmp.groups() else: # Invalid template raise ValueError("UUID template is invalid") rnd_f = [RandInt] + [RandShort] * 2 + [RandByte] * 8 uuid_template = [] # type: List[Union[int, RandNum]] for i, t in enumerate(template): if t == "*": uuid_template.append(rnd_f[i]()) elif ":" in t: mini, maxi = t.split(":") uuid_template.append( RandNum(int(mini, 16), int(maxi, 16)) ) else: uuid_template.append(int(t, 16)) self.uuid_template = tuple(uuid_template) else: if version: if version not in RandUUID.VERSIONS: raise ValueError("version is not supported") else: self.version = version else: # No version specified, try to guess... # This could be wrong, and cause an error later! if node or clock_seq: self.version = 1 elif namespace and name: self.version = 5 else: # Don't know, random! self.version = 4 # We have a version, now do things... if self.version == 1: if namespace or name: raise ValueError("namespace and name may not be used with " "version 1") self.node = node self.clock_seq = clock_seq elif self.version in (3, 5): if node or clock_seq: raise ValueError("node and clock_seq may not be used with " "version {}".format(self.version)) self.namespace = namespace self.name = name elif self.version == 4: if namespace or name or node or clock_seq: raise ValueError("node, clock_seq, node and clock_seq may " "not be used with version 4. If you " "did not specify version, you need to " "specify it explicitly.") def _command_args(self): # type: () -> str ret = [] if self._template: ret.append("template=%r" % self._template) if self.node: ret.append("node=%r" % self.node) if self.clock_seq: ret.append("clock_seq=%r" % self.clock_seq) if self.namespace: ret.append("namespace=%r" % self.namespace) if self.name: ret.append("name=%r" % self.name) if self._ori_version: ret.append("version=%r" % self._ori_version) return ", ".join(ret) def _fix(self): # type: () -> uuid.UUID if self.uuid_template: return uuid.UUID(("%08x%04x%04x" + ("%02x" * 8)) % self.uuid_template) elif self.version == 1: return uuid.uuid1(self.node, self.clock_seq) elif self.version == 3: if not self.namespace or not self.name: raise ValueError("Missing namespace or name") return uuid.uuid3(self.namespace, self.name) elif self.version == 4: return uuid.uuid4() elif self.version == 5: if not self.namespace or not self.name: raise ValueError("Missing namespace or name") return uuid.uuid5(self.namespace, self.name) else: raise ValueError("Unhandled version") # Automatic timestamp class _AutoTime(_RandNumeral[_T], # type: ignore Generic[_T]): def __init__(self, base=None, diff=None): # type: (Optional[int], Optional[float]) -> None self._base = base self._ori_diff = diff if diff is not None: self.diff = diff elif base is None: self.diff = 0. else: self.diff = time.time() - base def _command_args(self): # type: () -> str ret = [] if self._base: ret.append("base=%r" % self._base) if self._ori_diff: ret.append("diff=%r" % self._ori_diff) return ", ".join(ret) class AutoTime(_AutoTime[float]): def _fix(self): # type: () -> float return time.time() - self.diff class IntAutoTime(_AutoTime[int]): def _fix(self): # type: () -> int return int(time.time() - self.diff) class ZuluTime(_AutoTime[str]): def __init__(self, diff=0): # type: (int) -> None super(ZuluTime, self).__init__(diff=diff) def _fix(self): # type: () -> str return time.strftime("%y%m%d%H%M%SZ", time.gmtime(time.time() + self.diff)) class GeneralizedTime(_AutoTime[str]): def __init__(self, diff=0): # type: (int) -> None super(GeneralizedTime, self).__init__(diff=diff) def _fix(self): # type: () -> str return time.strftime("%Y%m%d%H%M%SZ", time.gmtime(time.time() + self.diff)) class DelayedEval(VolatileValue[Any]): """ Example of usage: DelayedEval("time.time()") """ def __init__(self, expr): # type: (str) -> None self.expr = expr def _command_args(self): # type: () -> str return "expr=%r" % self.expr def _fix(self): # type: () -> Any return eval(self.expr) class IncrementalValue(VolatileValue[int]): def __init__(self, start=0, step=1, restart=-1): # type: (int, int, int) -> None self.start = self.val = start self.step = step self.restart = restart def _command_args(self): # type: () -> str ret = [] if self.start: ret.append("start=%r" % self.start) if self.step != 1: ret.append("step=%r" % self.step) if self.restart != -1: ret.append("restart=%r" % self.restart) return ", ".join(ret) def _fix(self): # type: () -> int v = self.val if self.val == self.restart: self.val = self.start else: self.val += self.step return v class CorruptedBytes(VolatileValue[bytes]): def __init__(self, s, p=0.01, n=None): # type: (str, float, Optional[Any]) -> None self.s = s self.p = p self.n = n def _command_args(self): # type: () -> str ret = [] ret.append("s=%r" % self.s) if self.p != 0.01: ret.append("p=%r" % self.p) if self.n: ret.append("n=%r" % self.n) return ", ".join(ret) def _fix(self): # type: () -> bytes return corrupt_bytes(self.s, self.p, self.n) class CorruptedBits(CorruptedBytes): def _fix(self): # type: () -> bytes return corrupt_bits(self.s, self.p, self.n)