1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <phil@secdev.org>
4## This program is published under a GPLv2 license
5
6"""
7Fields that hold random numbers.
8"""
9
10from __future__ import absolute_import
11import random,time,math
12from scapy.base_classes import Net
13from scapy.compat import *
14from scapy.utils import corrupt_bits,corrupt_bytes
15from scapy.modules.six.moves import range
16
17####################
18## Random numbers ##
19####################
20
21
22class RandomEnumeration:
23    """iterate through a sequence in random order.
24       When all the values have been drawn, if forever=1, the drawing is done again.
25       If renewkeys=0, the draw will be in the same order, guaranteeing that the same
26       number will be drawn in not less than the number of integers of the sequence"""
27    def __init__(self, inf, sup, seed=None, forever=1, renewkeys=0):
28        self.forever = forever
29        self.renewkeys = renewkeys
30        self.inf = inf
31        self.rnd = random.Random(seed)
32        self.sbox_size = 256
33
34        self.top = sup-inf+1
35
36        n=0
37        while (1<<n) < self.top:
38            n += 1
39        self.n =n
40
41        self.fs = min(3,(n+1)//2)
42        self.fsmask = 2**self.fs-1
43        self.rounds = max(self.n,3)
44        self.turns = 0
45        self.i = 0
46
47    def __iter__(self):
48        return self
49    def next(self):
50        while True:
51            if self.turns == 0 or (self.i == 0 and self.renewkeys):
52                self.cnt_key = self.rnd.randint(0,2**self.n-1)
53                self.sbox = [self.rnd.randint(0, self.fsmask)
54                             for _ in range(self.sbox_size)]
55            self.turns += 1
56            while self.i < 2**self.n:
57                ct = self.i^self.cnt_key
58                self.i += 1
59                for _ in range(self.rounds): # Unbalanced Feistel Network
60                    lsb = ct & self.fsmask
61                    ct >>= self.fs
62                    lsb ^= self.sbox[ct%self.sbox_size]
63                    ct |= lsb << (self.n-self.fs)
64
65                if ct < self.top:
66                    return self.inf+ct
67            self.i = 0
68            if not self.forever:
69                raise StopIteration
70    __next__ = next
71
72
73class VolatileValue:
74    def __repr__(self):
75        return "<%s>" % self.__class__.__name__
76    def __eq__(self, other):
77        x = self._fix()
78        y = other._fix() if isinstance(other, VolatileValue) else other
79        if not isinstance(x, type(y)):
80            return False
81        return x == y
82    def __getattr__(self, attr):
83        if attr in ["__setstate__", "__getstate__"]:
84            raise AttributeError(attr)
85        return getattr(self._fix(),attr)
86    def __str__(self):
87        return str(self._fix())
88    def __bytes__(self):
89        return raw(self._fix())
90    def __len__(self):
91        return len(self._fix())
92
93    def _fix(self):
94        return None
95
96
97class RandField(VolatileValue):
98    pass
99
100class RandNum(RandField):
101    """Instances evaluate to random integers in selected range"""
102    min = 0
103    max = 0
104    def __init__(self, min, max):
105        self.min = min
106        self.max = max
107    def _fix(self):
108        return random.randrange(self.min, self.max+1)
109
110    def __int__(self):
111        return int(self._fix())
112    def __index__(self):
113        return int(self)
114    def __add__(self, other):
115        return self._fix() + other
116    def __radd__(self, other):
117        return other + self._fix()
118    def __sub__(self, other):
119        return self._fix() - other
120    def __rsub__(self, other):
121        return other - self._fix()
122    def __mul__(self, other):
123        return self._fix() * other
124    def __rmul__(self, other):
125        return other * self._fix()
126    def __floordiv__(self, other):
127        return self._fix() / other
128    __div__ = __floordiv__
129
130class RandNumGamma(RandNum):
131    def __init__(self, alpha, beta):
132        self.alpha = alpha
133        self.beta = beta
134    def _fix(self):
135        return int(round(random.gammavariate(self.alpha, self.beta)))
136
137class RandNumGauss(RandNum):
138    def __init__(self, mu, sigma):
139        self.mu = mu
140        self.sigma = sigma
141    def _fix(self):
142        return int(round(random.gauss(self.mu, self.sigma)))
143
144class RandNumExpo(RandNum):
145    def __init__(self, lambd, base=0):
146        self.lambd = lambd
147        self.base = base
148    def _fix(self):
149        return self.base+int(round(random.expovariate(self.lambd)))
150
151class RandEnum(RandNum):
152    """Instances evaluate to integer sampling without replacement from the given interval"""
153    def __init__(self, min, max, seed=None):
154        self.seq = RandomEnumeration(min,max,seed)
155    def _fix(self):
156        return next(self.seq)
157
158class RandByte(RandNum):
159    def __init__(self):
160        RandNum.__init__(self, 0, 2**8-1)
161
162class RandSByte(RandNum):
163    def __init__(self):
164        RandNum.__init__(self, -2**7, 2**7-1)
165
166class RandShort(RandNum):
167    def __init__(self):
168        RandNum.__init__(self, 0, 2**16-1)
169
170class RandSShort(RandNum):
171    def __init__(self):
172        RandNum.__init__(self, -2**15, 2**15-1)
173
174class RandInt(RandNum):
175    def __init__(self):
176        RandNum.__init__(self, 0, 2**32-1)
177
178class RandSInt(RandNum):
179    def __init__(self):
180        RandNum.__init__(self, -2**31, 2**31-1)
181
182class RandLong(RandNum):
183    def __init__(self):
184        RandNum.__init__(self, 0, 2**64-1)
185
186class RandSLong(RandNum):
187    def __init__(self):
188        RandNum.__init__(self, -2**63, 2**63-1)
189
190class RandEnumByte(RandEnum):
191    def __init__(self):
192        RandEnum.__init__(self, 0, 2**8-1)
193
194class RandEnumSByte(RandEnum):
195    def __init__(self):
196        RandEnum.__init__(self, -2**7, 2**7-1)
197
198class RandEnumShort(RandEnum):
199    def __init__(self):
200        RandEnum.__init__(self, 0, 2**16-1)
201
202class RandEnumSShort(RandEnum):
203    def __init__(self):
204        RandEnum.__init__(self, -2**15, 2**15-1)
205
206class RandEnumInt(RandEnum):
207    def __init__(self):
208        RandEnum.__init__(self, 0, 2**32-1)
209
210class RandEnumSInt(RandEnum):
211    def __init__(self):
212        RandEnum.__init__(self, -2**31, 2**31-1)
213
214class RandEnumLong(RandEnum):
215    def __init__(self):
216        RandEnum.__init__(self, 0, 2**64-1)
217
218class RandEnumSLong(RandEnum):
219    def __init__(self):
220        RandEnum.__init__(self, -2**63, 2**63-1)
221
222class RandEnumKeys(RandEnum):
223    """Picks a random value from dict keys list. """
224    def __init__(self, enum, seed=None):
225        self.enum = list(enum)
226        self.seq = RandomEnumeration(0, len(self.enum) - 1, seed)
227
228    def _fix(self):
229        return self.enum[next(self.seq)]
230
231class RandChoice(RandField):
232    def __init__(self, *args):
233        if not args:
234            raise TypeError("RandChoice needs at least one choice")
235        self._choice = args
236    def _fix(self):
237        return random.choice(self._choice)
238
239class RandString(RandField):
240    def __init__(self, size=None, chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"):
241        if size is None:
242            size = RandNumExpo(0.01)
243        self.size = size
244        self.chars = chars
245    def _fix(self):
246        s = ""
247        for _ in range(self.size):
248            s += random.choice(self.chars)
249        return s
250    def __mul__(self, n):
251        return self._fix()*n
252
253class RandBin(RandString):
254    def __init__(self, size=None):
255        RandString.__init__(self, size, "".join(map(chr, range(256))))
256
257
258class RandTermString(RandString):
259    def __init__(self, size, term):
260        RandString.__init__(self, size, "".join(map(chr, range(1,256))))
261        self.term = term
262    def _fix(self):
263        return RandString._fix(self)+self.term
264
265    def __str__(self):
266        return str(self._fix())
267
268    def __bytes__(self):
269        return raw(self._fix())
270
271
272class RandIP(RandString):
273    def __init__(self, iptemplate="0.0.0.0/0"):
274        self.ip = Net(iptemplate)
275    def _fix(self):
276        return self.ip.choice()
277
278class RandMAC(RandString):
279    def __init__(self, template="*"):
280        template += ":*:*:*:*:*"
281        template = template.split(":")
282        self.mac = ()
283        for i in range(6):
284            if template[i] == "*":
285                v = RandByte()
286            elif "-" in template[i]:
287                x,y = template[i].split("-")
288                v = RandNum(int(x,16), int(y,16))
289            else:
290                v = int(template[i],16)
291            self.mac += (v,)
292    def _fix(self):
293        return "%02x:%02x:%02x:%02x:%02x:%02x" % self.mac
294
295class RandIP6(RandString):
296    def __init__(self, ip6template="**"):
297        self.tmpl = ip6template
298        self.sp = self.tmpl.split(":")
299        for i,v in enumerate(self.sp):
300            if not v or v == "**":
301                continue
302            if "-" in v:
303                a,b = v.split("-")
304            elif v == "*":
305                a=b=""
306            else:
307                a=b=v
308
309            if not a:
310                a = "0"
311            if not b:
312                b = "ffff"
313            if a==b:
314                self.sp[i] = int(a,16)
315            else:
316                self.sp[i] = RandNum(int(a,16), int(b,16))
317        self.variable = "" in self.sp
318        self.multi = self.sp.count("**")
319    def _fix(self):
320        done = 0
321        nbm = self.multi
322        ip = []
323        for i,n in enumerate(self.sp):
324            if n == "**":
325                nbm -= 1
326                remain = 8-(len(self.sp)-i-1)-len(ip)+nbm
327                if "" in self.sp:
328                    remain += 1
329                if nbm or self.variable:
330                    remain = random.randint(0,remain)
331                for j in range(remain):
332                    ip.append("%04x" % random.randint(0,65535))
333            elif isinstance(n, RandNum):
334                ip.append("%04x" % n)
335            elif n == 0:
336              ip.append("0")
337            elif not n:
338                ip.append("")
339            else:
340                ip.append("%04x" % n)
341        if len(ip) == 9:
342            ip.remove("")
343        if ip[-1] == "":
344          ip[-1] = "0"
345        return ":".join(ip)
346
347class RandOID(RandString):
348    def __init__(self, fmt=None, depth=RandNumExpo(0.1), idnum=RandNumExpo(0.01)):
349        self.ori_fmt = fmt
350        if fmt is not None:
351            fmt = fmt.split(".")
352            for i in range(len(fmt)):
353                if "-" in fmt[i]:
354                    fmt[i] = tuple(map(int, fmt[i].split("-")))
355        self.fmt = fmt
356        self.depth = depth
357        self.idnum = idnum
358    def __repr__(self):
359        if self.ori_fmt is None:
360            return "<%s>" % self.__class__.__name__
361        else:
362            return "<%s [%s]>" % (self.__class__.__name__, self.ori_fmt)
363    def _fix(self):
364        if self.fmt is None:
365            return ".".join(str(self.idnum) for _ in range(1 + self.depth))
366        else:
367            oid = []
368            for i in self.fmt:
369                if i == "*":
370                    oid.append(str(self.idnum))
371                elif i == "**":
372                    oid += [str(self.idnum) for i in range(1 + self.depth)]
373                elif isinstance(i, tuple):
374                    oid.append(str(random.randrange(*i)))
375                else:
376                    oid.append(i)
377            return ".".join(oid)
378
379
380class RandRegExp(RandField):
381    def __init__(self, regexp, lambda_=0.3,):
382        self._regexp = regexp
383        self._lambda = lambda_
384
385    @staticmethod
386    def choice_expand(s): #XXX does not support special sets like (ex ':alnum:')
387        m = ""
388        invert = s and s[0] == "^"
389        while True:
390            p = s.find("-")
391            if p < 0:
392                break
393            if p == 0 or p == len(s)-1:
394                m = "-"
395                if p:
396                    s = s[:-1]
397                else:
398                    s = s[1:]
399            else:
400                c1 = s[p-1]
401                c2 = s[p+1]
402                rng = "".join(map(chr, range(ord(c1), ord(c2)+1)))
403                s = s[:p-1]+rng+s[p+1:]
404        res = m+s
405        if invert:
406            res = "".join(chr(x) for x in range(256) if chr(x) not in res)
407        return res
408
409    @staticmethod
410    def stack_fix(lst, index):
411        r = ""
412        mul = 1
413        for e in lst:
414            if isinstance(e, list):
415                if mul != 1:
416                    mul = mul-1
417                    r += RandRegExp.stack_fix(e[1:]*mul, index)
418                # only the last iteration should be kept for back reference
419                f = RandRegExp.stack_fix(e[1:], index)
420                for i,idx in enumerate(index):
421                    if e is idx:
422                        index[i] = f
423                r += f
424                mul = 1
425            elif isinstance(e, tuple):
426                kind,val = e
427                if kind == "cite":
428                    r += index[val-1]
429                elif kind == "repeat":
430                    mul = val
431
432                elif kind == "choice":
433                    if mul == 1:
434                        c = random.choice(val)
435                        r += RandRegExp.stack_fix(c[1:], index)
436                    else:
437                        r += RandRegExp.stack_fix([e]*mul, index)
438                        mul = 1
439            else:
440                if mul != 1:
441                    r += RandRegExp.stack_fix([e]*mul, index)
442                    mul = 1
443                else:
444                    r += str(e)
445        return r
446
447    def _fix(self):
448        stack = [None]
449        index = []
450        current = stack
451        i = 0
452        ln = len(self._regexp)
453        interp = True
454        while i < ln:
455            c = self._regexp[i]
456            i+=1
457
458            if c == '(':
459                current = [current]
460                current[0].append(current)
461            elif c == '|':
462                p = current[0]
463                ch = p[-1]
464                if not isinstance(ch, tuple):
465                    ch = ("choice",[current])
466                    p[-1] = ch
467                else:
468                    ch[1].append(current)
469                current = [p]
470            elif c == ')':
471                ch = current[0][-1]
472                if isinstance(ch, tuple):
473                    ch[1].append(current)
474                index.append(current)
475                current = current[0]
476            elif c == '[' or c == '{':
477                current = [current]
478                current[0].append(current)
479                interp = False
480            elif c == ']':
481                current = current[0]
482                choice = RandRegExp.choice_expand("".join(current.pop()[1:]))
483                current.append(RandChoice(*list(choice)))
484                interp = True
485            elif c == '}':
486                current = current[0]
487                num = "".join(current.pop()[1:])
488                e = current.pop()
489                if "," not in num:
490                    n = int(num)
491                    current.append([current]+[e]*n)
492                else:
493                    num_min,num_max = num.split(",")
494                    if not num_min:
495                        num_min = "0"
496                    if num_max:
497                        n = RandNum(int(num_min),int(num_max))
498                    else:
499                        n = RandNumExpo(self._lambda,base=int(num_min))
500                    current.append(("repeat",n))
501                    current.append(e)
502                interp = True
503            elif c == '\\':
504                c = self._regexp[i]
505                if c == "s":
506                    c = RandChoice(" ","\t")
507                elif c in "0123456789":
508                    c = ("cite",ord(c)-0x30)
509                current.append(c)
510                i += 1
511            elif not interp:
512                current.append(c)
513            elif c == '+':
514                e = current.pop()
515                current.append([current]+[e]*(int(random.expovariate(self._lambda))+1))
516            elif c == '*':
517                e = current.pop()
518                current.append([current]+[e]*int(random.expovariate(self._lambda)))
519            elif c == '?':
520                if random.randint(0,1):
521                    current.pop()
522            elif c == '.':
523                current.append(RandChoice(*[chr(x) for x in range(256)]))
524            elif c == '$' or c == '^':
525                pass
526            else:
527                current.append(c)
528
529        return RandRegExp.stack_fix(stack[1:], index)
530    def __repr__(self):
531        return "<%s [%r]>" % (self.__class__.__name__, self._regexp)
532
533class RandSingularity(RandChoice):
534    pass
535
536class RandSingNum(RandSingularity):
537    @staticmethod
538    def make_power_of_two(end):
539        sign = 1
540        if end == 0:
541            end = 1
542        if end < 0:
543            end = -end
544            sign = -1
545        end_n = int(math.log(end)/math.log(2))+1
546        return {sign*2**i for i in range(end_n)}
547
548    def __init__(self, mn, mx):
549        sing = {0, mn, mx, int((mn+mx)/2)}
550        sing |= self.make_power_of_two(mn)
551        sing |= self.make_power_of_two(mx)
552        for i in sing.copy():
553            sing.add(i+1)
554            sing.add(i-1)
555        for i in sing.copy():
556            if not mn <= i <= mx:
557                sing.remove(i)
558        self._choice = list(sing)
559        self._choice.sort()
560
561
562class RandSingByte(RandSingNum):
563    def __init__(self):
564        RandSingNum.__init__(self, 0, 2**8-1)
565
566class RandSingSByte(RandSingNum):
567    def __init__(self):
568        RandSingNum.__init__(self, -2**7, 2**7-1)
569
570class RandSingShort(RandSingNum):
571    def __init__(self):
572        RandSingNum.__init__(self, 0, 2**16-1)
573
574class RandSingSShort(RandSingNum):
575    def __init__(self):
576        RandSingNum.__init__(self, -2**15, 2**15-1)
577
578class RandSingInt(RandSingNum):
579    def __init__(self):
580        RandSingNum.__init__(self, 0, 2**32-1)
581
582class RandSingSInt(RandSingNum):
583    def __init__(self):
584        RandSingNum.__init__(self, -2**31, 2**31-1)
585
586class RandSingLong(RandSingNum):
587    def __init__(self):
588        RandSingNum.__init__(self, 0, 2**64-1)
589
590class RandSingSLong(RandSingNum):
591    def __init__(self):
592        RandSingNum.__init__(self, -2**63, 2**63-1)
593
594class RandSingString(RandSingularity):
595    def __init__(self):
596        self._choice = [ "",
597                         "%x",
598                         "%%",
599                         "%s",
600                         "%i",
601                         "%n",
602                         "%x%x%x%x%x%x%x%x%x",
603                         "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
604                         "%",
605                         "%%%",
606                         "A"*4096,
607                         b"\x00"*4096,
608                         b"\xff"*4096,
609                         b"\x7f"*4096,
610                         b"\x80"*4096,
611                         " "*4096,
612                         "\\"*4096,
613                         "("*4096,
614                         "../"*1024,
615                         "/"*1024,
616                         "${HOME}"*512,
617                         " or 1=1 --",
618                         "' or 1=1 --",
619                         '" or 1=1 --',
620                         " or 1=1; #",
621                         "' or 1=1; #",
622                         '" or 1=1; #',
623                         ";reboot;",
624                         "$(reboot)",
625                         "`reboot`",
626                         "index.php%00",
627                         b"\x00",
628                         "%00",
629                         "\\",
630                         "../../../../../../../../../../../../../../../../../etc/passwd",
631                         "%2e%2e%2f" * 20 + "etc/passwd",
632                         "%252e%252e%252f" * 20 + "boot.ini",
633                         "..%c0%af" * 20 + "etc/passwd",
634                         "..%c0%af" * 20 + "boot.ini",
635                         "//etc/passwd",
636                         r"..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\boot.ini",
637                         "AUX:",
638                         "CLOCK$",
639                         "COM:",
640                         "CON:",
641                         "LPT:",
642                         "LST:",
643                         "NUL:",
644                         "CON:",
645                         r"C:\CON\CON",
646                         r"C:\boot.ini",
647                         r"\\myserver\share",
648                         "foo.exe:",
649                         "foo.exe\\", ]
650
651    def __str__(self):
652        return str(self._fix())
653    def __bytes__(self):
654        return raw(self._fix())
655
656
657class RandPool(RandField):
658    def __init__(self, *args):
659        """Each parameter is a volatile object or a couple (volatile object, weight)"""
660        pool = []
661        for p in args:
662            w = 1
663            if isinstance(p, tuple):
664                p,w = p
665            pool += [p]*w
666        self._pool = pool
667    def _fix(self):
668        r = random.choice(self._pool)
669        return r._fix()
670
671# Automatic timestamp
672
673class AutoTime(VolatileValue):
674    def __init__(self, base=None):
675        if base == None:
676            self.diff = 0
677        else:
678            self.diff = time.time()-base
679    def _fix(self):
680        return time.time()-self.diff
681
682class IntAutoTime(AutoTime):
683    def _fix(self):
684        return int(time.time()-self.diff)
685
686
687class ZuluTime(AutoTime):
688    def __init__(self, diff=0):
689        self.diff = diff
690    def _fix(self):
691        return time.strftime("%y%m%d%H%M%SZ",
692                             time.gmtime(time.time() + self.diff))
693
694
695class GeneralizedTime(AutoTime):
696    def __init__(self, diff=0):
697        self.diff = diff
698    def _fix(self):
699        return time.strftime("%Y%m%d%H%M%SZ",
700                             time.gmtime(time.time() + self.diff))
701
702
703class DelayedEval(VolatileValue):
704    """ Example of usage: DelayedEval("time.time()") """
705    def __init__(self, expr):
706        self.expr = expr
707    def _fix(self):
708        return eval(self.expr)
709
710
711class IncrementalValue(VolatileValue):
712    def __init__(self, start=0, step=1, restart=-1):
713        self.start = self.val = start
714        self.step = step
715        self.restart = restart
716    def _fix(self):
717        v = self.val
718        if self.val == self.restart :
719            self.val = self.start
720        else:
721            self.val += self.step
722        return v
723
724class CorruptedBytes(VolatileValue):
725    def __init__(self, s, p=0.01, n=None):
726        self.s = s
727        self.p = p
728        self.n = n
729    def _fix(self):
730        return corrupt_bytes(self.s, self.p, self.n)
731
732class CorruptedBits(CorruptedBytes):
733    def _fix(self):
734        return corrupt_bits(self.s, self.p, self.n)
735
736