├── LICENSE.md ├── README.md ├── dizzy ├── __init__.py ├── config.py ├── dizz.py ├── dizz_iterator.py ├── dizz_state.py ├── encodings │ ├── __init__.py │ ├── der.py │ └── encoding.py ├── functions │ ├── __init__.py │ ├── call.py │ ├── checksum.py │ ├── encryption.py │ ├── length.py │ ├── link.py │ ├── padding.py │ ├── rand.py │ ├── run_cmd.py │ └── time.py ├── interaction.py ├── interaction_iterator.py ├── interaction_state.py ├── job.py ├── library.py ├── log.py ├── module.py ├── objects │ ├── __init__.py │ ├── field.py │ ├── list.py │ ├── rand.py │ └── regex.py ├── pcap.py ├── probe │ ├── __init__.py │ ├── http.py │ ├── icmp.py │ └── tcp.py ├── profile.py ├── session │ ├── __init__.py │ ├── eth.py │ ├── http.py │ ├── sctp.py │ ├── ssl.py │ ├── stdout-hex.py │ ├── stdout.py │ ├── tcp.py │ └── udp.py ├── state.py ├── tests │ ├── __init__.py │ ├── probe │ │ ├── __init__.py │ │ ├── test_icmp.py │ │ └── test_tcp.py │ ├── test.py │ ├── test_dizzy.py │ ├── test_field.py │ ├── test_interaction.py │ ├── test_list.py │ ├── test_regex.py │ └── test_value.py ├── tools.py └── value.py ├── dizzy_cmd ├── lib └── std_string_lib.txt ├── modules_src ├── Makefile ├── Makefile_Module └── demo │ ├── __init__.py │ ├── config.py │ └── demo │ ├── act │ └── demo.act │ ├── dizz │ └── demo.dizz │ └── job │ └── demo.conf ├── setup.cfg ├── setup.py └── wireshark └── dizzy.lua /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2018, Daniel Mende 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. Neither the name of the project nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /dizzy/__init__.py: -------------------------------------------------------------------------------- 1 | # __init__.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | 33 | class DizzyParseException(Exception): 34 | pass 35 | 36 | 37 | class DizzyRuntimeException(Exception): 38 | pass 39 | 40 | 41 | class InteractParseException(Exception): 42 | pass 43 | 44 | 45 | class JobParseException(Exception): 46 | pass 47 | -------------------------------------------------------------------------------- /dizzy/dizz_state.py: -------------------------------------------------------------------------------- 1 | # dizz_state.py 2 | # 3 | # Copyright 2018 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from types import GeneratorType 33 | from dizzy.state import State 34 | 35 | class DizzState(State): 36 | def __init__(self, obj): 37 | State.__init__(self, obj) 38 | 39 | def next(self): 40 | if not self.is_dizz(): 41 | # Mutate 42 | self.bak = next(self.iter) 43 | self.cur = self.bak 44 | else: 45 | # Mutate the dizz object before the dizz functions is call 46 | self.bak = self.iter.mutate() 47 | # Call the dizz functions and return the current state 48 | self.cur = self.iter.call_functions() 49 | 50 | def is_dizz(self): 51 | from dizzy.dizz_iterator import DizzIterator 52 | return isinstance(self.iter, DizzIterator) 53 | 54 | def reset(self): 55 | # To reset the state to the value before a function is call, it have to be checked 56 | # if the self.iter is DizzIterator (nested Dizz object). 57 | # If self.iter is DizzIterator, the fields of the dizz object have to be reset, too. 58 | if self.is_dizz(): 59 | self.iter.reset() 60 | 61 | self.cur = self.bak 62 | -------------------------------------------------------------------------------- /dizzy/encodings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ernw/dizzy/c44b8ff0ac3f1acb577e848303a6b74de89d011a/dizzy/encodings/__init__.py -------------------------------------------------------------------------------- /dizzy/encodings/der.py: -------------------------------------------------------------------------------- 1 | # der.py 2 | # 3 | # Copyright 2018 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from dizzy import DizzyRuntimeException 32 | from dizzy.value import Value 33 | from struct import pack 34 | 35 | class Tree(object): 36 | def __init__(self, up, data): 37 | self.up = up 38 | self.down = [] 39 | self.data = data 40 | 41 | def __repr__(self): 42 | return "%s: Data: %s Up: %s Down: %s" % (id(self), self.data.obj.name, id(self.up), [ i.data.obj.name for i in self.down ]) 43 | 44 | def encode(dizz_state): 45 | tree = None 46 | cur_depth = 0 47 | cur_node = None 48 | 49 | for obj in dizz_state: 50 | enc = obj.obj.extra_encoding 51 | if enc is "DER": 52 | (_, depth) = obj.obj.extra_encoding_data 53 | if tree is None: 54 | if not depth is 0: 55 | raise DizzyRuntimeException("DER encoding: First element needs depth 0.") 56 | else: 57 | tree = Tree(None, obj) 58 | cur_node = tree 59 | else: 60 | if depth is cur_depth + 1: 61 | new_node = Tree(cur_node, obj) 62 | cur_node.down.append(new_node) 63 | cur_node = new_node 64 | cur_depth = depth 65 | elif depth > cur_depth: 66 | raise DizzyRuntimeException("DER encoding: Can only increment depth by one.") 67 | elif depth is cur_depth: 68 | new_node = Tree(cur_node.up, obj) 69 | cur_node.up.down.append(new_node) 70 | cur_node = new_node 71 | else: 72 | lam = cur_depth - depth 73 | #print(cur_depth, lam) 74 | for _ in range(lam): 75 | #print (".") 76 | if not cur_node.up is None: 77 | #print (",") 78 | cur_node = cur_node.up 79 | #print(cur_node) 80 | new_node = Tree(cur_node.up, obj) 81 | cur_node.up.down.append(new_node) 82 | cur_node = new_node 83 | cur_depth = depth 84 | 85 | #from pprint import pprint 86 | #pprint(tree) 87 | 88 | return enc_tree(tree) 89 | 90 | def calc_len(node): 91 | length = len(node.data.cur.byte) 92 | for i in node.down: 93 | length += calc_len(i) 94 | return length 95 | 96 | def enc_tree(node): 97 | for i in node.down: 98 | enc_tree(i) 99 | (tag, _) = node.data.obj.extra_encoding_data 100 | length = calc_len(node) 101 | if length < 128: 102 | len_octets = length.to_bytes(1, byteorder='big') 103 | else: 104 | len_bytes = length.bit_length() // 8 105 | if length.bit_length() % 8 > 0: 106 | len_bytes += 1 107 | len_octets = (0x80 | len_bytes).to_bytes(1, byteorder='big') + length.to_bytes(len_bytes, byteorder='big') 108 | #print("tag: %s len: %s, len_octets: %s" % (tag.hex(), length, len_octets.hex())) 109 | #print("old_cur: %s" % node.data.cur.byte.hex()) 110 | new_cur = Value(tag) + Value(len_octets) + node.data.cur 111 | #print("new_cur: %s" % new_cur.byte.hex()) 112 | node.data.cur = new_cur 113 | -------------------------------------------------------------------------------- /dizzy/encodings/encoding.py: -------------------------------------------------------------------------------- 1 | # encoding.py 2 | # 3 | # Copyright 2018 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from dizzy.encodings.der import encode as encode_der 33 | 34 | encodings = { 35 | "DER" : encode_der 36 | } 37 | 38 | def apply_extra_encoding(dizz_state): 39 | enclist = [] 40 | 41 | for obj in dizz_state: 42 | if not obj.obj.extra_encoding is None: 43 | enc = obj.obj.extra_encoding 44 | if not enc in enclist: 45 | enclist.append(enc) 46 | 47 | for i in enclist: 48 | encodings[i](dizz_state) 49 | 50 | 51 | -------------------------------------------------------------------------------- /dizzy/functions/__init__.py: -------------------------------------------------------------------------------- 1 | # __init__.py 2 | # 3 | # Copyright 2018 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | PRE = "_PRE_" 33 | POST = "_POST_" 34 | BOTH = "_BOTH_" 35 | -------------------------------------------------------------------------------- /dizzy/functions/call.py: -------------------------------------------------------------------------------- 1 | # call.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from . import BOTH 33 | 34 | def call(index, func, when=BOTH): 35 | if hasattr(func, "index"): 36 | func.index.append(index) 37 | else: 38 | func.index = [index] 39 | 40 | return (func, when) 41 | -------------------------------------------------------------------------------- /dizzy/functions/checksum.py: -------------------------------------------------------------------------------- 1 | # checksum.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from dizzy.value import Value 32 | from . import BOTH 33 | from hashlib import md5, sha1, sha224, sha256, sha384, sha512 34 | from struct import pack 35 | 36 | def checksum(target, start, stop, algorithm, callback=None, when=BOTH): 37 | def func(dizzy_iterator): 38 | if algorithm == "md5": 39 | hash_algorithm = md5() 40 | elif algorithm == "sha1": 41 | hash_algorithm = sha1() 42 | elif algorithm == "sha224": 43 | hash_algorithm = sha224() 44 | elif algorithm == "sha256": 45 | hash_algorithm = sha256() 46 | elif algorithm == "sha384": 47 | hash_algorithm = sha384() 48 | elif algorithm == "sha512": 49 | hash_algorithm = sha512() 50 | else: 51 | if callback is None: 52 | raise Exception("Hash algorithm not found") 53 | else: 54 | hash_algorithm = None 55 | 56 | size = dizzy_iterator[target].size 57 | size_bytes = (size + 7) // 8 58 | 59 | dizzy_iterator[target] = Value(b'\x00' * size_bytes, size) 60 | if not hash_algorithm is None: 61 | hash_algorithm.update(dizzy_iterator[start:stop].byte) 62 | digest = hash_algorithm.digest() 63 | else: 64 | digest = callback(dizzy_iterator[start:stop].byte) 65 | dizzy_iterator[target] = Value(digest[:size_bytes], size) 66 | 67 | return (func, when) 68 | 69 | def checksum_inet(target, start, stop, when=BOTH): 70 | def func(dizzy_iterator): 71 | data = dizzy_iterator[start:stop].byte 72 | checksum = 0 73 | for i in range(0, len(data),2): 74 | if i + 1 >= len(data): 75 | checksum += data[i] & 0xFF 76 | else: 77 | w = ((data[i] << 8) & 0xFF00) + (data[i+1] & 0xFF) 78 | checksum += w 79 | while (checksum >> 16) > 0: 80 | checksum = (checksum & 0xFFFF) + (checksum >> 16) 81 | checksum = ~checksum 82 | dizzy_iterator[target] = pack("!H", checksum & 0xFFFF) 83 | 84 | return (func, when) 85 | -------------------------------------------------------------------------------- /dizzy/functions/encryption.py: -------------------------------------------------------------------------------- 1 | # encryption.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from dizzy.config import CONFIG 33 | 34 | if CONFIG["DEPS"]["Crypto"]: 35 | from Crypto.Cipher import AES 36 | 37 | from . import BOTH 38 | 39 | def aes_encrypt(start, stop, key, mode=AES.MODE_CBC, mode_param=None, when=BOTH): 40 | def func(dizzy_iterator): 41 | enc = AES.new(key, mode, mode_param) 42 | dizzy_iterator[start:stop] = enc.encrypt(dizzy_iterator[start:stop].byte) 43 | 44 | return (func, when) 45 | 46 | 47 | def aes_decrypt(start, stop, key, mode=AES.MODE_CBC, mode_param=None, when=BOTH): 48 | def func(dizzy_iterator): 49 | enc = AES.new(key, mode, mode_param) 50 | dizzy_iterator[start:stop] = enc.decrypt(dizzy_iterator[start:stop].byte) 51 | 52 | return (func, when) 53 | else: 54 | def aes_encrypt(start, stop, key, mode=AES.MODE_CBC, mode_param=None, when=BOTH): 55 | raise DizzyParseException("python Crypto module not available.") 56 | 57 | def aes_decrypt(start, stop, key, mode=AES.MODE_CBC, mode_param=None, when=BOTH): 58 | raise DizzyParseException("python Crypto module not available.") -------------------------------------------------------------------------------- /dizzy/functions/length.py: -------------------------------------------------------------------------------- 1 | # length.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from dizzy.config import CONFIG 32 | from dizzy.value import Value 33 | from dizzy.tools import pack_with_length 34 | from dizzy.objects import START, END 35 | from dizzy.log import print_dizzy, DEBUG 36 | from . import BOTH 37 | 38 | def length(target, start=START, stop=END, endian="!", when=BOTH): 39 | def func(dizzy_iterator): 40 | if target not in dizzy_iterator.current_mutated_objects: 41 | size = dizzy_iterator[target].size 42 | value = Value(pack_with_length(dizzy_iterator[start:stop].size, size, endian), size) 43 | print_dizzy("length/%s: seting to %s." % (target, value), DEBUG) 44 | dizzy_iterator[target] = value 45 | else: 46 | print_dizzy("length/%s: is not updated." % (target,), DEBUG) 47 | 48 | return (func, when) 49 | 50 | def length_bytes(target, start=START, stop=END, endian="!", when=BOTH): 51 | def func(dizzy_iterator): 52 | if target not in dizzy_iterator.current_mutated_objects: 53 | size = dizzy_iterator[target].size 54 | value = Value(pack_with_length((dizzy_iterator[start:stop].size + 7) // 8, size, endian), size) 55 | print_dizzy("length_bytes/%s: seting to %s." % (target, value), DEBUG) 56 | dizzy_iterator[target] = value 57 | else: 58 | print_dizzy("length_bytes/%s: is not updated." % (target,), DEBUG) 59 | 60 | return (func, when) 61 | 62 | def length_string_bytes(target, start=START, stop=END, encoding=CONFIG["GLOBALS"]["CODEC"], when=BOTH): 63 | def func(dizzy_iterator): 64 | if target not in dizzy_iterator.current_mutated_objects: 65 | size = str((dizzy_iterator[start:stop].size + 7) // 8) 66 | value = Value(bytes(size, encoding), len(size) * 8) 67 | print_dizzy("length_string_bytes/%s: seting to %s." % (target, value), DEBUG) 68 | dizzy_iterator[target] = value 69 | else: 70 | print_dizzy("length_string_bytes/%s: is not updated." % (target,), DEBUG) 71 | 72 | return (func, when) 73 | 74 | def length_lambda(target, start=START, stop=END, lam=lambda x: x, endian="!", when=BOTH): 75 | def func(dizzy_iterator): 76 | if target not in dizzy_iterator.current_mutated_objects: 77 | size = dizzy_iterator[target].size 78 | value = Value(pack_with_length(lam(dizzy_iterator[start:stop].size), size, endian), size) 79 | print_dizzy("length_lambda/%s: seting to %s." % (target, value), DEBUG) 80 | dizzy_iterator[target] = value 81 | else: 82 | print_dizzy("length_lambda/%s: is not updated." % (target,), DEBUG) 83 | 84 | return (func, when) 85 | 86 | def length_lambda2(target, start=START, stop=END, lam=lambda x: x, lam2=lambda x: x, endian="!", when=BOTH): 87 | def func(dizzy_iterator): 88 | if target not in dizzy_iterator.current_mutated_objects: 89 | size = lam(dizzy_iterator[target].size) 90 | value = Value(pack_with_length(lam2(dizzy_iterator[start:stop].size), size, endian), size) 91 | print_dizzy("length_lambda2/%s: seting to %s." % (target, value), DEBUG) 92 | dizzy_iterator[target] = value 93 | else: 94 | print_dizzy("length_lambda2/%s: is not updated." % (target,), DEBUG) 95 | 96 | 97 | return (func, when) 98 | -------------------------------------------------------------------------------- /dizzy/functions/link.py: -------------------------------------------------------------------------------- 1 | # link.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from . import BOTH 33 | 34 | def link(source, target, when=BOTH): 35 | def func(dizzy_iterator): 36 | dizzy_iterator[target] = dizzy_iterator[source] 37 | 38 | return (func, when) 39 | -------------------------------------------------------------------------------- /dizzy/functions/padding.py: -------------------------------------------------------------------------------- 1 | # padding.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from dizzy.value import Value 32 | from . import BOTH 33 | from os import urandom 34 | 35 | 36 | def padding(target, start, stop, modulo, pattern=b"\x00", when=BOTH): 37 | def func(dizzy_iterator): 38 | if target not in dizzy_iterator.current_mutated_objects: 39 | mod = (dizzy_iterator[start:stop].size % modulo) 40 | if mod > 0: 41 | size_in_bits = modulo - mod 42 | dizzy_iterator[target] = Value(pattern * ((size_in_bits + 7) // 8), size_in_bits) 43 | else: 44 | dizzy_iterator[target] = "" 45 | return (func, when) 46 | 47 | 48 | # Source: https://en.wikipedia.org/wiki/Padding_(cryptography) 49 | 50 | 51 | def padding_zero(target, start, stop, modulo, when=BOTH): 52 | return padding(target, start, stop, modulo, pattern=b"\x00", when=when) 53 | 54 | 55 | def padding_pkcs7(target, start, stop, modulo, when=BOTH): 56 | def func(dizzy_iterator): 57 | if target not in dizzy_iterator.current_mutated_objects: 58 | size_in_bits = modulo - (dizzy_iterator[start:stop].size % modulo) 59 | size_in_bytes = ((size_in_bits + 7) // 8) 60 | dizzy_iterator[target] = Value(size_in_bytes.to_bytes(1, 'big') * size_in_bytes, size_in_bits) 61 | 62 | return (func, when) 63 | 64 | 65 | def padding_ansi_x923(target, start, stop, modulo, when=BOTH): 66 | def func(dizzy_iterator): 67 | if target not in dizzy_iterator.current_mutated_objects: 68 | size_in_bits = modulo - (dizzy_iterator[start:stop].size % modulo) 69 | size_in_bytes = ((size_in_bits + 7) // 8) 70 | dizzy_iterator[target] = Value((size_in_bytes - 1) * b"\x00" + size_in_bytes.to_bytes(1, 'big'), 71 | size_in_bits) 72 | 73 | return (func, when) 74 | 75 | 76 | def padding_iso_10126(target, start, stop, modulo, when=BOTH): 77 | def func(dizzy_iterator): 78 | if target not in dizzy_iterator.current_mutated_objects: 79 | size_in_bits = modulo - (dizzy_iterator[start:stop].size % modulo) 80 | size_in_bytes = ((size_in_bits + 7) // 8) 81 | dizzy_iterator[target] = Value(urandom(size_in_bytes - 1) + size_in_bytes.to_bytes(1, 'big'), size_in_bits) 82 | 83 | return (func, when) 84 | 85 | 86 | def padding_iso_iec_7816_4(target, start, stop, modulo, when=BOTH): 87 | def func(dizzy_iterator): 88 | if target not in dizzy_iterator.current_mutated_objects: 89 | size_in_bits = modulo - (dizzy_iterator[start:stop].size % modulo) 90 | size_in_bytes = ((size_in_bits + 7) // 8) 91 | dizzy_iterator[target] = Value(b"\80" + (size_in_bytes - 1) * b"\x00", size_in_bits) 92 | 93 | return (func, when) -------------------------------------------------------------------------------- /dizzy/functions/rand.py: -------------------------------------------------------------------------------- 1 | # rand.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from os import urandom 32 | from dizzy.value import Value 33 | from . import BOTH 34 | 35 | def rand(target, when=BOTH): 36 | def func(dizzy_iterator): 37 | size_in_bits = dizzy_iterator[target].size 38 | dizzy_iterator[target] = Value(urandom((size_in_bits + 7) // 8), size_in_bits) 39 | 40 | return (func, when) 41 | -------------------------------------------------------------------------------- /dizzy/functions/run_cmd.py: -------------------------------------------------------------------------------- 1 | # run_cmd.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import BOTH 32 | from os import system 33 | 34 | def run_cmd(cmd, when=BOTH): 35 | def func(dizzy_iterator): 36 | system(cmd) 37 | 38 | return (func, when) 39 | -------------------------------------------------------------------------------- /dizzy/functions/time.py: -------------------------------------------------------------------------------- 1 | # time.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import BOTH 32 | from time import time 33 | 34 | 35 | def time_no_fracs(target, offset=0, when=BOTH): 36 | def func(dizzy_iterator): 37 | dizzy_iterator[target] = int(time() + offset) 38 | 39 | return (func, when) 40 | 41 | 42 | def time(target, offset=0, when=BOTH): 43 | def func(dizzy_iterator): 44 | now = time.time() + offset 45 | secs = int(now) 46 | fracs = int((now - secs) * 65536) 47 | # secs is packed in 48 bits(2 ** 48 - 1 = 281474976710655) and fracs packed in 18 bits(2 ** 18 - 1 = 262143) 48 | dizzy_iterator[target] = ((secs & 281474976710655) << 18) | (fracs & 262143) 49 | 50 | return (func, when) 51 | -------------------------------------------------------------------------------- /dizzy/interaction.py: -------------------------------------------------------------------------------- 1 | # interaction.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from os.path import exists 32 | 33 | from . import InteractParseException, DizzyParseException 34 | from dizzy.interaction_iterator import InteractionIterator 35 | from dizzy.functions.call import call 36 | from dizzy.dizz import load_dizz, null_dizz, Dizz 37 | from dizzy.config import CONFIG 38 | from dizzy.log import print_dizzy, DEBUG 39 | 40 | def length_std(self): 41 | length = 0 42 | for obj in self.objects: 43 | length += obj.length() 44 | return (length - (len(self.objects) - 1)) * (len(self.objects) + 1) - 1 45 | 46 | def iterations_std(self): 47 | iterations = 0 48 | for obj in self.objects: 49 | iterations += obj.length() 50 | return iterations - (len(self.objects) - 1) 51 | 52 | def length_full(self): 53 | length = 1 54 | for obj in self.objects: 55 | length *= obj.length() 56 | return length * (len(self.objects) + 1) - 1 57 | 58 | def iterations_full(self): 59 | iterations = 1 60 | for obj in self.objects: 61 | iterations *= obj.length() 62 | return iterations 63 | 64 | def length_none(self): 65 | return len(self.objects) 66 | 67 | def iterations_none(_): 68 | return 1 69 | 70 | class Interaction(object): 71 | # TODO: start_object 72 | def __init__(self, name, objects=[], functions={}, fuzz='std', start_at=0): 73 | self.name = name 74 | self.objects = objects 75 | self.functions = functions 76 | self.fuzz = fuzz 77 | self.start_at = start_at 78 | self.response = b'' 79 | 80 | if self.fuzz == 'std': 81 | self.len = length_std 82 | self.iter = iterations_std 83 | elif self.fuzz == 'full': 84 | self.len = length_full 85 | self.iter = iterations_full 86 | elif self.fuzz == "none": 87 | self.len = length_none 88 | self.iter = iterations_none 89 | else: 90 | raise DizzyParseException("fuzz mode is unknown.") 91 | 92 | def __repr__(self): 93 | return "Interaction '%s' %d objects, %d functions" % (self.name, len(self.objects), len(self.functions)) 94 | 95 | def __iter__(self): 96 | return InteractionIterator(self) 97 | 98 | def length(self): 99 | return self.len(self) 100 | 101 | def iterations(self): 102 | return self.iter(self) 103 | 104 | def dump(self): 105 | return "Name: '%s'\nObjects: '[%s]'\nFunktions: '%s'\n" % (self.name, ", ".join(map(Dizz.dump, self.objects)), self.functions) 106 | 107 | def load_interaction(filename, fuzz="std", start_at=0, config_values={}): 108 | if exists(filename): 109 | with open(filename) as f: 110 | return parse_interaction(f.read(), filename, fuzz, start_at, config_values) 111 | else: 112 | if filename in CONFIG["ACT"]: 113 | return parse_interaction(CONFIG["ACT"][filename], filename, fuzz, start_at, config_values) 114 | 115 | def parse_interaction(file, filename, fuzz="std", start_at=0, config_values={}): 116 | def not_impl(*args, **kwargs): 117 | raise InteractParseException("Not implemented") 118 | 119 | def load_dizz_conf(*args, **kwargs): 120 | if not 'config_values' in kwargs: 121 | kwargs['config_values'] = config_values 122 | return load_dizz(*args, **kwargs) 123 | def config_value(name): 124 | return config_values[name] 125 | 126 | ns = {"Dizzy": load_dizz_conf, 127 | "NullDizzy": null_dizz, 128 | "copy": not_impl, 129 | "call": call, 130 | "f": not_impl, 131 | "print_dizz": not_impl, 132 | "print_field": not_impl, 133 | "global": CONFIG["GLOBALS"]["INTERACTION_GLOBALS"], 134 | "get_session": lambda : CONFIG["GLOBALS"]["SESSION"], 135 | "config_value": config_value} 136 | 137 | exec(compile(file, filename, 'exec'), ns) 138 | act = Interaction(ns["name"], ns["objects"], ns["functions"], fuzz, start_at) 139 | print_dizzy("interaction/%s: '%s'." % (act.name, act.dump()), DEBUG) 140 | return act 141 | -------------------------------------------------------------------------------- /dizzy/interaction_iterator.py: -------------------------------------------------------------------------------- 1 | # interaction_iterator.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from dizzy.interaction_state import InteractionState 32 | from dizzy import DizzyRuntimeException, DizzyParseException 33 | from dizzy.log import print_dizzy 34 | from dizzy.functions import POST 35 | 36 | class InteractionIterator: 37 | def __init__(self, interaction): 38 | from dizzy.interaction import Interaction 39 | if not isinstance(interaction, Interaction): 40 | raise DizzyParseException('interaction must be Interaction object.') 41 | 42 | self.interaction = interaction 43 | self.state = [] 44 | self.objects = {} 45 | for obj in interaction.objects: 46 | state = InteractionState(obj) 47 | if obj.name in self.objects: 48 | raise DizzyParseException("duplicate names: %s" % obj.name) 49 | else: 50 | self.objects.update({obj.name: state}) 51 | self.state.append(state) 52 | 53 | self.current_iterator = enumerate(self.state) 54 | 55 | if self.interaction.fuzz == 'std': 56 | self.mutate = self.mutate_std 57 | for cur in self.state: 58 | cur.done = False 59 | elif self.interaction.fuzz == 'full': 60 | self.mutate = self.mutate_full 61 | else: 62 | self.mutate = self.mutate_none 63 | 64 | if interaction.start_at != 0: 65 | length = interaction.length() 66 | if length <= interaction.start_at: 67 | raise DizzyRuntimeException("start_at out of bounds: %s" % obj.name) 68 | 69 | if interaction.fuzz == 'std': 70 | self.start_at_std(interaction.start_at) 71 | elif interaction.fuzz == 'full': 72 | self.start_at_full(interaction.start_at) 73 | 74 | def __next__(self): 75 | try: 76 | # Get the next packet in the iteration 77 | self.index, self.current_object = next(self.current_iterator) 78 | # Call the act functions and then the dizz functions and return the current state of the packet 79 | return self.call_functions() 80 | except StopIteration: 81 | # The iteration is done 82 | # Set all packets to value before the dizz and act functions was called 83 | # This have to be done, because the act functions can change the value in all packets 84 | self.reset_packets() 85 | 86 | # Set the first object to the current object 87 | self.current_iterator = enumerate(self.state) 88 | 89 | # Mutate the next packet 90 | # The function behind self.mutate() depends on the fuzzing mode: 91 | # none: mutate_none() 92 | # std: mutate_std() 93 | # full: mutate_full() 94 | # This function return always None to mark the next iteration 95 | return self.mutate() 96 | 97 | def mutate_std(self): 98 | # Mutate the next packet 99 | self.current_iterator = enumerate(self.state) 100 | for current in self.state: 101 | if not current.done: 102 | try: 103 | # Found a packet to mutate, if next() is not raise a StopIteration 104 | # next(current) call State.__next__() which call DizzIterator.__next__() 105 | next(current) 106 | # Return None to mark the next iteration 107 | return None 108 | except StopIteration: 109 | # current Dizz Object 110 | current.done = True 111 | raise StopIteration 112 | 113 | def start_at_std(self, start_at): 114 | for state in self.state: 115 | obj_length = state.obj.length() 116 | 117 | if start_at < obj_length: 118 | state.obj.start_at = (state.obj.start_at + start_at) % obj_length 119 | state.iter = iter(state.obj) 120 | state.cur = next(state.iter) 121 | state.obj.start_at = 0 122 | break 123 | else: 124 | start_at -= obj_length - 1 125 | state.done = True 126 | 127 | if start_at == 0: 128 | break 129 | 130 | def mutate_full(self): 131 | # Mutate the next packet 132 | for current in self.state: 133 | try: 134 | # Found a packet to mutate, if next() is not raise a StopIteration 135 | # next(current) call State.__next__() which call DizzIterator.__next__() 136 | next(current) 137 | # Return None to mark the next iteration 138 | return None 139 | except StopIteration: 140 | pass 141 | raise StopIteration 142 | 143 | def start_at_full(self, start_at): 144 | for state in self.state: 145 | obj_length = state.obj.length() 146 | start_at, length = divmod(start_at, obj_length) 147 | 148 | if length > 0: 149 | state.obj.start_at = (state.obj.start_at + length) % obj_length 150 | state.iter = iter(state.obj) 151 | state.cur = next(state.iter) 152 | state.obj.start_at = 0 153 | 154 | if start_at == 0: 155 | break 156 | 157 | def mutate_none(self): 158 | raise StopIteration 159 | 160 | def __getitem__(self, item): 161 | return self.objects[item].iter 162 | 163 | def call_functions(self): 164 | if self.current_object is None: 165 | return None 166 | 167 | # Call the act functions 168 | if self.index in self.interaction.functions: 169 | # There is a function list to call 170 | for func in self.interaction.functions[self.index]: 171 | try: 172 | func(self, self.current_object.iter, self.interaction.response) 173 | except Exception as e: 174 | print_dizzy("%s: Exception in function" % self.interaction) 175 | print_dizzy(e) 176 | 177 | # Call the dizz functions(for example to correct the length fields) and return the current state 178 | return self.current_object.call_functions(POST) 179 | 180 | def __setitem__(self, key, value): 181 | if not isinstance(key, str): 182 | raise RuntimeError('Error') 183 | 184 | for c in self.state: 185 | if c.obj.name == key: 186 | c.cur = value 187 | return 188 | 189 | raise RuntimeError('Not found') 190 | 191 | def reset_packets(self): 192 | for a in self.state: 193 | a.reset() 194 | -------------------------------------------------------------------------------- /dizzy/interaction_state.py: -------------------------------------------------------------------------------- 1 | # interaction_state.py 2 | # 3 | # Copyright 2018 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from dizzy.state import State 33 | 34 | class InteractionState(State): 35 | def __init__(self, obj): 36 | State.__init__(self, obj) 37 | 38 | def next(self): 39 | # Mutate the dizz object before the dizz functions is call 40 | self.bak = self.iter.mutate() 41 | # Call the dizz functions and return the current state 42 | self.cur = self.iter.call_functions() 43 | 44 | def reset(self): 45 | self.iter.reset() 46 | self.cur = self.bak 47 | -------------------------------------------------------------------------------- /dizzy/library.py: -------------------------------------------------------------------------------- 1 | # library.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | #import math 33 | 34 | #from dizzy.config import CONFIG 35 | from dizzy import tools 36 | from dizzy.value import Value 37 | 38 | class DizzyLibrary(object): 39 | def __init__(self): 40 | self.lib = {} 41 | 42 | def load_file(self, filename, listname=None): 43 | if listname is None: 44 | listname = filename 45 | if listname in self.lib: 46 | return self.lib[listname] 47 | self.lib[listname] = [] 48 | with open(filename, 'r+b') as f: 49 | for l in f: 50 | if l.rstrip(b'\n') in self.lib[listname]: 51 | pass 52 | v = Value(l.rstrip(b'\n')) 53 | self.lib[listname].append(v) 54 | return self.lib[listname] 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | class dizz_library(object): 64 | def __init__(self): 65 | self.lib = {} 66 | self.load_strings(CONFIG["GLOBALS"]["DEFAULT_STR_LIB"]) 67 | 68 | def get_next(self, obj): 69 | libidx = obj["length"] 70 | if libidx is None: 71 | if not obj["encoding"] is None: 72 | cur = obj["cur"].decode(obj["encoding"]) 73 | else: 74 | cur = obj["cur"].decode(CONFIG["GLOBALS"]["CODEC"]) 75 | else: 76 | cur = obj["cur"] 77 | if obj["_type"] == "list": 78 | libidx = obj["listname"] 79 | if not libidx in self.lib: 80 | self.gen_entries(libidx) 81 | if cur not in self.lib[libidx]: 82 | if libidx == None: 83 | return self.lib[libidx][0] 84 | return None 85 | return self.lib[libidx][self.lib[libidx].index(cur) + 1] 86 | 87 | def _get_next(self, index, cur): 88 | if not index in self.lib: 89 | self.gen_entries(index) 90 | if cur is None: 91 | return self.lib[index][0] 92 | return self.lib[index][self.lib[index].index(cur) + 1] 93 | 94 | def gen_entries(self, length): 95 | bytelen = length // 8 96 | if length % 8 > 0: 97 | bytelen += 1 98 | if length >= 4: 99 | entr = [] 100 | entr += [tools.pack_with_length(0, length)] 101 | entr += [tools.pack_with_length(1, length)] 102 | entr += [tools.pack_with_length(2, length)] 103 | entr += [tools.pack_with_length(3, length)] 104 | entr += [tools.pack_with_length(4, length)] 105 | if length > 8: 106 | entr += [tools.pack_with_length(1, length, endian="<")] 107 | entr += [tools.pack_with_length(2, length, endian="<")] 108 | entr += [tools.pack_with_length(3, length, endian="<")] 109 | entr += [tools.pack_with_length(4, length, endian="<")] 110 | max = int(math.pow(2, length)) - 1 111 | if length > 8: 112 | entr += [tools.pack_with_length(max - 4, length, endian="<")] 113 | entr += [tools.pack_with_length(max - 3, length, endian="<")] 114 | entr += [tools.pack_with_length(max - 2, length, endian="<")] 115 | entr += [tools.pack_with_length(max - 1, length, endian="<")] 116 | entr += [tools.pack_with_length((max / 2), length, endian="<")] 117 | entr += [tools.pack_with_length((max / 2) + 1, length, endian="<")] 118 | entr += [tools.pack_with_length(((max / 2) + 1) / 2, length, endian="<")] 119 | entr += [tools.pack_with_length(max - 4, length)] 120 | entr += [tools.pack_with_length(max - 3, length)] 121 | entr += [tools.pack_with_length(max - 2, length)] 122 | entr += [tools.pack_with_length(max - 1, length)] 123 | entr += [tools.pack_with_length(max, length)] 124 | entr += [tools.pack_with_length((max / 2), length)] 125 | entr += [tools.pack_with_length((max / 2) + 1, length)] 126 | entr += [tools.pack_with_length(((max / 2) + 1) / 2, length)] 127 | entr += [None] 128 | elif length == 3: 129 | entr = [b"\x00", b"\x01", b"\x02", b"\x03", b"\x04", b"\x05", b"\x06", b"\x07", None] 130 | elif length == 2: 131 | entr = [b"\x00", b"\x01", b"\x02", b"\x03", None] 132 | elif length == 1: 133 | entr = [b"\x00", b"\x01", None] 134 | self.lib[length] = tools.unique(entr) 135 | 136 | def load_strings(self, filename, listname=None): 137 | if listname in self.lib: 138 | return 139 | self.lib[listname] = [ b"", None ] 140 | with open(filename, 'r+b') as f: 141 | for l in f: 142 | if l.rstrip(b'\n') in self.lib[listname]: 143 | pass 144 | b = l.rstrip(b'\n') 145 | self.lib[listname].insert(-1, b) 146 | -------------------------------------------------------------------------------- /dizzy/log.py: -------------------------------------------------------------------------------- 1 | # log.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from datetime import datetime 33 | from traceback import print_tb 34 | from threading import Lock 35 | from sys import exc_info 36 | from pprint import pprint 37 | 38 | DEBUG = 5 39 | VERBOSE_2 = 4 40 | VERBOSE_1 = 3 41 | NORMAL = 2 42 | REDUCE = 1 43 | NONE = 0 44 | 45 | print_level = NORMAL 46 | 47 | print_levels = {"DEBUG": DEBUG, 48 | "VERBOSE_2": VERBOSE_2, 49 | "VERBOSE_1": VERBOSE_1, 50 | "NORMAL": NORMAL, 51 | "REDUCE": REDUCE, 52 | "NONE": NONE} 53 | 54 | print_colors = { DEBUG: '\033[91m', 55 | VERBOSE_2: '\033[94m', 56 | VERBOSE_1: '\033[96m', 57 | NORMAL: '\033[97m', 58 | REDUCE: '\033[90m', 59 | NONE: ''} 60 | ENDC = '\033[0m' 61 | 62 | print_lock = Lock() 63 | 64 | def set_print_level(level): 65 | global print_level 66 | print_level = level 67 | 68 | def print_dizzy(value, level=NORMAL): 69 | if print_level >= level: 70 | with print_lock: 71 | print(print_colors[level], end='') 72 | print(value) 73 | if isinstance(value, Exception): 74 | ex_type, ex, tb = exc_info() 75 | print_tb(tb) 76 | print(ENDC, end='') 77 | 78 | def pprint_dizzy(value, level=NORMAL): 79 | if print_level >= level: 80 | with print_lock: 81 | print(print_colors[level], end='') 82 | pprint(value, width=1000, compact=True) 83 | print(ENDC, end='') 84 | 85 | class Logger(object): 86 | def __init__(self, stream, logfile, buffered=False): 87 | self.stream = stream 88 | self.logfile = open(logfile, "a") 89 | self.buffered = buffered 90 | 91 | def write(self, data): 92 | self.stream.write(data) 93 | self.logfile.write(datetime.now().isoformat() + ": " + data + "\n") 94 | if not self.buffered: 95 | self.flush() 96 | 97 | def flush(self): 98 | self.stream.flush() 99 | self.logfile.flush() 100 | -------------------------------------------------------------------------------- /dizzy/module.py: -------------------------------------------------------------------------------- 1 | # module.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from zipimport import zipimporter 33 | import sys 34 | 35 | from dizzy.log import print_dizzy, VERBOSE_1, VERBOSE_2, DEBUG 36 | 37 | class DizzyModule(object): 38 | def __init__(self, path, config): 39 | self.path = path 40 | self.global_config = config 41 | self.zipimport = zipimporter(path) 42 | self.config = self.zipimport.load_module("config") 43 | 44 | def load(self): 45 | inventory = self.zipimport.load_module(self.name).__all__ 46 | 47 | if "deps" in inventory: 48 | depspath = self.path + "/" + self.name + "/deps" 49 | sys.path.insert(0, depspath) 50 | print_dizzy("mod:%s/load: added dependency path '%s'" % (self.name, depspath), VERBOSE_1) 51 | 52 | sys.path.insert(0, self.path) 53 | if "dizz" in inventory: 54 | dizz = __import__(self.name + ".dizz").dizz 55 | self.dizz = [] 56 | for i in dizz.__all__: 57 | name = self.name + "/dizz/" + i 58 | obj = dizz.__loader__.get_data(name) 59 | self.dizz.append(name) 60 | self.global_config["DIZZ"][name] = obj.decode(self.global_config["GLOBALS"]["CODEC"]) 61 | 62 | print_dizzy("mod:%s/load: Loaded %d dizz files." % (self.name, len(self.dizz)), VERBOSE_1) 63 | print_dizzy("mod:%s/load: %s" % (self.name, self.dizz), DEBUG) 64 | 65 | if "act" in inventory: 66 | act = __import__(self.name + ".act").act 67 | self.act = [] 68 | for i in act.__all__: 69 | name = self.name + "/act/" + i 70 | obj = act.__loader__.get_data(name) 71 | self.act.append(name) 72 | self.global_config["ACT"][name] = obj.decode(self.global_config["GLOBALS"]["CODEC"]) 73 | 74 | print_dizzy("mod:%s/load: Loaded %d act files." % (self.name, len(self.act)), VERBOSE_1) 75 | print_dizzy("mod:%s/load: %s" % (self.name, self.act), DEBUG) 76 | 77 | if "job" in inventory: 78 | job = __import__(self.name + ".job").job 79 | self.job = [] 80 | for i in job.__all__: 81 | name = self.name + "/job/" + i 82 | obj = job.__loader__.get_data(name) 83 | self.job.append(name) 84 | self.global_config["JOB"][name] = obj.decode(self.global_config["GLOBALS"]["CODEC"]) 85 | 86 | print_dizzy("mod:%s/load: Loaded %d job files." % (self.name, len(self.job)), VERBOSE_1) 87 | print_dizzy("mod:%s/load: %s" % (self.name, self.job), DEBUG) 88 | 89 | if "probe" in inventory: 90 | probe = __import__(self.name + ".probe").probe 91 | self.probe = [] 92 | for i in probe.__all__: 93 | name = self.name + ".probe." + i 94 | obj = getattr(__import__(name).probe, i) 95 | self.probe.append(name) 96 | self.global_config["PROBE"][name] = obj 97 | 98 | print_dizzy("mod:%s/load: Loaded %d target probes." % (self.name, len(self.probe)), VERBOSE_1) 99 | print_dizzy("mod:%s/load: %s" % (self.name, self.probe), DEBUG) 100 | 101 | if "session" in inventory: 102 | session = __import__(self.name + ".session").session 103 | self.session = [] 104 | for i in session.__all__: 105 | name = self.name + ".session." + i 106 | obj = getattr(__import__(name).session, i) 107 | self.session.append(name) 108 | self.global_config["SESSION"][name] = obj 109 | 110 | print_dizzy("mod:%s/load: Loaded %d sessions." % (self.name, len(self.session)), VERBOSE_1) 111 | print_dizzy("mod:%s/load: %s" % (self.name, self.session), DEBUG) 112 | 113 | sys.path.remove(self.path) 114 | 115 | @property 116 | def name(self): 117 | return self.config.name 118 | 119 | @property 120 | def dependencies(self): 121 | return self.config.dependencies 122 | 123 | @property 124 | def version(self): 125 | return self.config.version -------------------------------------------------------------------------------- /dizzy/objects/__init__.py: -------------------------------------------------------------------------------- 1 | # __init__.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | START = "_START_" 33 | END = "_END_" 34 | -------------------------------------------------------------------------------- /dizzy/objects/field.py: -------------------------------------------------------------------------------- 1 | # field.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from dizzy.config import CONFIG 32 | from dizzy.tools import pack_with_length 33 | from dizzy.value import Value 34 | from dizzy import DizzyParseException 35 | from sys import maxsize 36 | 37 | class Field: 38 | def __init__(self, name, default=b'', size=None, fuzz='none', endian="!", encoding=CONFIG["GLOBALS"]["CODEC"], extra_encoding=None, extra_encoding_data=None): 39 | if isinstance(name, str) and name: 40 | self.name = name 41 | else: 42 | raise DizzyParseException("Name must be str and not empty.") 43 | self.encoding = encoding 44 | self.extra_encoding = extra_encoding 45 | self.extra_encoding_data = extra_encoding_data 46 | 47 | if isinstance(default, str): 48 | self.default = bytes(default, self.encoding) 49 | elif isinstance(default, bytes): 50 | self.default = default 51 | elif isinstance(default, int): 52 | # TODO: check size type 53 | self.default = pack_with_length(default, size, endian) 54 | else: 55 | raise DizzyParseException('Default must be str, bytes or int: %s' % name) 56 | 57 | if isinstance(size, int): 58 | self.size = slice(size, size + 1, 1) 59 | elif size is None: 60 | self.size = len(self.default) * 8 61 | self.size = slice(self.size, self.size + 1, 1) 62 | elif isinstance(size, slice): 63 | self.size = size 64 | else: 65 | raise DizzyParseException("Unknown fuzzing mode: %s" % name) 66 | 67 | if self.size.start < 0: 68 | raise DizzyParseException('Length less than 0 are not allowed: %s' % name) 69 | elif self.size.stop <= self.size.start: 70 | raise DizzyParseException('Length less than 0 are not allowed: %s' % name) 71 | elif self.size.step is None: 72 | self.size = slice(self.size.start, self.size.stop, 1) 73 | elif self.size.step <= 0: 74 | raise DizzyParseException('Step must not be 0: %s' % name) 75 | 76 | self.fuzz = fuzz 77 | 78 | if self.fuzz == "full": 79 | if self.size.start > maxsize or self.size.stop > maxsize: 80 | raise DizzyParseException("Cannot make dizzy with length '%d' full fuzzable, " 81 | "this would take ages: %s" % (size, name)) 82 | else: 83 | self.iter = self.iter_full 84 | self.len = 1 85 | for r in range(self.size.start, self.size.stop, self.size.step): 86 | self.len += 2 ** r 87 | elif self.fuzz == 'std': 88 | self.iter = self.iter_std 89 | self.len = 1 90 | for r in range(self.size.start, self.size.stop, self.size.step): 91 | if r > 2: 92 | self.len += 5 93 | if r > 4: 94 | self.len += 5 95 | if r > 7: 96 | self.len += 4 97 | if r > 8: 98 | self.len += 4 99 | elif self.fuzz is 'none': 100 | self.iter = self.iter_none 101 | self.len = 1 102 | else: 103 | raise DizzyParseException("Unknown fuzzing mode: %s" % name) 104 | 105 | self.default = Value(self.default, self.size.start) 106 | self.endian = endian 107 | 108 | def __iter__(self): 109 | return self.iter() 110 | 111 | def __repr__(self): 112 | return "Field(name=%s, default=%s, size=%s, fuzz=%s, endian=%s, encoding=%s)" % (self.name, self.default, self.size, self.fuzz, self.endian, self.encoding) 113 | 114 | def length(self): 115 | return self.len 116 | 117 | def iter_full(self): 118 | yield self.default 119 | for r in range(self.size.start, self.size.stop, self.size.step): 120 | i = 0 121 | byte_length = (r + 7) // 8 122 | max_value = 2 ** r 123 | while i < max_value: 124 | yield Value(i.to_bytes(byte_length, 'big'), r) 125 | i += 1 126 | 127 | def iter_std(self): 128 | yield self.default 129 | for r in range(self.size.start, self.size.stop, self.size.step): 130 | if r > 2: 131 | byte_length = (r + 7) // 8 132 | for i in range(0, 5): 133 | yield Value(i.to_bytes(byte_length, 'big'), r) 134 | if r > 4: 135 | buf = (2 ** r) - 1 136 | for i in range(0, 5): 137 | yield Value((buf - (4 - i)).to_bytes(byte_length, 'big'), r) 138 | if r > 7: 139 | buf = 2 ** (r - 1) 140 | for i in range(0, 4): 141 | yield Value((buf - (4 - i)).to_bytes(byte_length, 'big'), r) 142 | if r > 8: 143 | buf = 2 ** ((byte_length - 1) * 8) 144 | for i in range(1, 5): 145 | yield Value((i * buf).to_bytes(byte_length, 'big'), r) 146 | 147 | def iter_none(self): 148 | yield self.default 149 | -------------------------------------------------------------------------------- /dizzy/objects/list.py: -------------------------------------------------------------------------------- 1 | # list.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from dizzy import DizzyParseException 32 | from dizzy.config import CONFIG 33 | from dizzy.value import Value 34 | 35 | class List: 36 | def __init__(self, name, default=b'', path=CONFIG["GLOBALS"]["DEFAULT_STR_LIB"], encoding=CONFIG["GLOBALS"]["CODEC"], extra_encoding=None, extra_encoding_data=None): 37 | if isinstance(name, str) and name: 38 | self.name = name 39 | else: 40 | raise DizzyParseException("Name must be str and not empty.") 41 | 42 | self.default = default 43 | if isinstance(default, Value): 44 | self.list_ = [default] 45 | elif isinstance(default, str): 46 | self.list_ = [Value(default.encode(encoding))] 47 | else: 48 | self.list_ = [Value(default)] 49 | 50 | self.path = path 51 | self.flist_ = CONFIG["GLOBALS"]["GLOBAL_LIBRARY"].load_file(path) #encoding? 52 | 53 | self.extra_encoding = extra_encoding 54 | self.extra_encoding_data = extra_encoding_data 55 | 56 | def __repr__(self): 57 | return "List(name=%s, default=%s, path=%s)" % (self.name, self.default, self.path) 58 | 59 | def __iter__(self): 60 | return iter(self.list_ + self.flist_) 61 | 62 | def length(self): 63 | return len(self.list_ + self.flist_) 64 | -------------------------------------------------------------------------------- /dizzy/objects/rand.py: -------------------------------------------------------------------------------- 1 | # rand.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from dizzy import DizzyParseException 32 | from dizzy.value import Value 33 | from os import urandom 34 | 35 | class Rand: 36 | def __init__(self, name, size, count=1): 37 | if isinstance(name, str) and name: 38 | self.name = name 39 | else: 40 | raise DizzyParseException("Name must be str and not empty.") 41 | 42 | self.list_ = list() 43 | if isinstance(size, int): 44 | size = slice(size, size + 1, 1) 45 | 46 | for size_in_bits in range(size.start, size.stop, size.step): 47 | size_in_bytes = (size_in_bits + 7) // 8 48 | for y in range(count): 49 | self.list_.append(Value(urandom(size_in_bytes), size_in_bits)) 50 | 51 | def __iter__(self): 52 | return iter(self.list_) 53 | 54 | def length(self): 55 | return len(self.list_) 56 | -------------------------------------------------------------------------------- /dizzy/objects/regex.py: -------------------------------------------------------------------------------- 1 | # regex.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from dizzy.value import Value 32 | from dizzy.config import CONFIG 33 | from dizzy import DizzyParseException 34 | 35 | if CONFIG["DEPS"]["exrex"]: 36 | from exrex import generate, count 37 | 38 | class Regex: 39 | def __init__(self, name, regex, limit=20): 40 | if isinstance(name, str) and name: 41 | self.name = name 42 | else: 43 | raise DizzyParseException("Name must be str and not empty.") 44 | 45 | if isinstance(regex, str): 46 | self.regex = regex 47 | else: 48 | raise DizzyParseException("regex must be str.") 49 | 50 | if isinstance(limit, int): 51 | self.limit = limit 52 | else: 53 | raise DizzyParseException("limit must be int.") 54 | 55 | self.len = count(self.regex, self.limit) 56 | 57 | def __iter__(self): 58 | for string in generate(self.regex, self.limit): 59 | value = bytes(string, encoding=CONFIG["GLOBALS"]["CODEC"]) 60 | yield Value(value, len(value) * 8) 61 | 62 | def length(self): 63 | return self.len 64 | else: 65 | class Regex: 66 | def __init__(self, _1, _2, _3=None): 67 | raise DizzyParseException("python egrex module not available.") 68 | -------------------------------------------------------------------------------- /dizzy/pcap.py: -------------------------------------------------------------------------------- 1 | # pcap.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from threading import Thread 33 | from time import sleep 34 | from dizzy.tools import check_root 35 | 36 | from dizzy.log import print_dizzy, VERBOSE_1, VERBOSE_2 37 | 38 | class Pcap(Thread): 39 | def __init__(self, config, filename): 40 | Thread.__init__(self) 41 | try: 42 | import pcapy 43 | self.pcap = pcapy 44 | except: 45 | print_dizzy("No usable pcap library found. Be sure you have pcapy installed!") 46 | print_dizzy("Pcap recording disabled!") 47 | self.pcap = None 48 | return 49 | self.interface = config.get("interface", "any") 50 | check_root("use the PCAP feature") 51 | print_dizzy("pcap/init: listening on interface '%s'." % self.interface, VERBOSE_1) 52 | if not self.interface is "any": 53 | if not self.interface in self.pcap.findalldevs(): 54 | print_dizzy("Device '%s' not found, recording on _all_ interfaces.") 55 | self.interface = "any" 56 | self.filter = config.get("filter", "") 57 | if not self.filter is "": 58 | print_dizzy("pcap/init: using bpf '%s'." % self.filter, VERBOSE_1) 59 | self.snaplen = config.getint("snaplen", 8192) 60 | self.promisc = config.getboolean("promisc", True) 61 | self.to_ms = config.getint("to_ms", 10) 62 | self.cooldown = config.getint("cooldown", 0) 63 | self.filename = filename 64 | self.is_open = False 65 | 66 | def run(self): 67 | if self.pcap is None: 68 | return 69 | self.run = True 70 | self.pcap_object = self.pcap.open_live(self.interface, self.snaplen, self.promisc, self.to_ms) 71 | print_dizzy("pcap/run: pcap object opened.", VERBOSE_2) 72 | if not self.filter is "": 73 | self.pcap_object.setfilter(self.filter) 74 | self.pcap_dumper = self.pcap_object.dump_open(self.filename) 75 | print_dizzy("pcap/run: pcap dumper opened for file '%s'." % self.filename, VERBOSE_2) 76 | self.is_open = True 77 | while self.run: 78 | hdr, data = self.pcap_object.next() 79 | if not hdr is None: 80 | if self.is_open: 81 | self.pcap_dumper.dump(hdr, data) 82 | 83 | def stop(self): 84 | if self.pcap is None: 85 | return 86 | if self.cooldown > 0: 87 | print_dizzy("pcap/stop: cooling down for %d seconds." % self.cooldown, VERBOSE_1) 88 | sleep(self.cooldown) 89 | self.run = False 90 | self.is_open = False 91 | self.pcap_dumper.close() 92 | print_dizzy("pcap/stop: pcap dumper closed for file '%s'." % self.filename, VERBOSE_2) 93 | -------------------------------------------------------------------------------- /dizzy/probe/__init__.py: -------------------------------------------------------------------------------- 1 | # __init__.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | __all__ = [ "http", "icmp", "tcp" ] 33 | 34 | class ProbeParseException(Exception): 35 | pass 36 | 37 | class ProbeException(Exception): 38 | pass 39 | -------------------------------------------------------------------------------- /dizzy/probe/http.py: -------------------------------------------------------------------------------- 1 | # http.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import ProbeParseException, ProbeException 32 | from dizzy.log import print_dizzy, VERBOSE_1, DEBUG 33 | 34 | from http.client import HTTPConnection 35 | 36 | class DizzyProbe(object): 37 | def __init__(self, section_proxy): 38 | self.dest = section_proxy.get('target_host') 39 | self.dport = section_proxy.getint('target_port', 80) 40 | self.src = section_proxy.get('source_host', '') 41 | if self.src == '': 42 | self.src = None 43 | self.method = section_proxy.get('method', 'GET') 44 | self.url = section_proxy.get('url') 45 | self.body = section_proxy.get('body', '') 46 | if self.body == '': 47 | self.body = None 48 | self.headers = {} 49 | headers = section_proxy.get('headers', '') 50 | if not headers == '': 51 | for l in headers.split("\n"): 52 | r = l.split(":") 53 | self.headers[r[0]] = ":".join(r[1:]) 54 | self.timeout = section_proxy.getfloat('timeout', 1) 55 | self.retry = section_proxy.getint('retry', 3) 56 | self.is_open = False 57 | self.res = None 58 | 59 | def open(self): 60 | try: 61 | self.connection = HTTPConnection(self.dest, self.dport, timeout=self.timeout, source_address=self.src) 62 | except Exception as e: 63 | raise SessionException("probe/http: cant open session: %s" % e) 64 | else: 65 | self.is_open = True 66 | 67 | def close(self): 68 | if not self.is_open: 69 | return 70 | self.connection.close() 71 | self.res = None 72 | self.is_open = False 73 | 74 | def probe(self): 75 | if not self.is_open: 76 | print_dizzy("probe/http: not opened.", DEBUG) 77 | return False 78 | for attempt in range(1, self.retry + 1): 79 | print_dizzy("probe/http: probe attempt: %d" % attempt, VERBOSE_1) 80 | try: 81 | self.connection.request(self.method, self.url, body=self.body, headers=self.headers) 82 | self.res = self.connection.getresponse() 83 | self.connection.close() 84 | except Exception as e: 85 | print_dizzy("probe/http: probe error: '%s'" % type(e)) 86 | print_dizzy(e, DEBUG) 87 | else: 88 | return True 89 | return False -------------------------------------------------------------------------------- /dizzy/probe/icmp.py: -------------------------------------------------------------------------------- 1 | # icmp.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import ProbeParseException, ProbeException 32 | from dizzy.log import print_dizzy, VERBOSE_1, DEBUG 33 | from dizzy.tools import csum_inet, check_root 34 | from os import getpid, geteuid 35 | from socket import socket, inet_aton, inet_pton, getprotobyname, AF_INET, AF_INET6, SOCK_RAW, error 36 | from struct import pack, unpack 37 | 38 | class DizzyProbe(object): 39 | ICMP_ECHO_REPLY = 0 40 | ICMP_ECHO = 8 41 | ICMP6_ECHO = 128 42 | ICMP6_ECHO_REPLY = 129 43 | 44 | def __init__(self, section_proxy): 45 | check_root("use the ICMP probe") 46 | 47 | self.target_host = section_proxy.get('target_host') 48 | self.timeout = section_proxy.getfloat('timeout', 1) 49 | self.pkg_size = section_proxy.getint('pkg_size', 64) 50 | self.retry = section_proxy.getint('retry', 2) 51 | self.socket = None 52 | self.is_open = False 53 | 54 | try: 55 | inet_aton(self.target_host) 56 | self.af = AF_INET 57 | self.proto = getprotobyname("icmp") 58 | echo = self.ICMP_ECHO 59 | except Exception as e: 60 | try: 61 | inet_pton(AF_INET6, self.target_host) 62 | self.af = AF_INET6 63 | self.proto = getprotobyname("ipv6-icmp") 64 | echo = self.ICMP6_ECHO 65 | except Exception as f: 66 | raise ProbeParseException("probe/icmp: unknown address family: %s: %s, %s" % 67 | (self.target_host, e, f)) 68 | self.pid = getpid() & 0xFFFF 69 | self.header = pack("!BBHHH", echo, 0, 0, self.pid, 0) 70 | 71 | pad = list() 72 | for i in range(0x41, 0x41 + self.pkg_size): 73 | pad += [(i & 0xff)] 74 | self.data = bytearray(pad) 75 | 76 | checksum = csum_inet(self.header + self.data) 77 | self.header = self.header[0:2] + checksum + self.header[4:] 78 | 79 | def open(self): 80 | try: 81 | self.socket = socket(self.af, SOCK_RAW, self.proto) 82 | except error as e: 83 | if not self.socket is None: 84 | self.socket.close() 85 | print_dizzy("probe/icmp: open error: '%s'" % e) 86 | print_dizzy(e, DEBUG) 87 | self.is_open = True 88 | 89 | def probe(self): 90 | if not self.is_open: 91 | print_dizzy("probe/tcp: not opened.", DEBUG) 92 | return False 93 | for attempt in range(1, self.retry + 1): 94 | print_dizzy("probe/icmp: probe attempt: %d" % attempt, VERBOSE_1) 95 | 96 | try: 97 | if self.af == AF_INET6: 98 | self.socket.sendto(self.header + self.data, (self.target_host, 0, 0, 0)) 99 | else: 100 | self.socket.sendto(self.header + self.data, (self.target_host, 0)) 101 | 102 | if self.af == AF_INET6: 103 | (data, (address, _, _, _)) = self.socket.recvfrom(2048) 104 | if address == self.target_host: 105 | pid, = unpack("!H", data[4:6]) 106 | if data[0] == self.ICMP6_ECHO_REPLY and pid == self.pid: 107 | return True 108 | else: 109 | (data, (address, _)) = self.socket.recvfrom(2048) 110 | if address == self.target_host: 111 | hl = (data[0] & 0x0f) * 4 112 | pid, = unpack("!H", data[hl + 4:hl + 6]) 113 | if data[hl] == self.ICMP_ECHO_REPLY and pid == self.pid: 114 | return True 115 | except error as e: 116 | print_dizzy("probe/icmp: reopening: '%s'" % e) 117 | print_dizzy(e, DEBUG) 118 | self.close() 119 | self.open() 120 | 121 | return False 122 | 123 | def close(self): 124 | if not self.is_open: 125 | return 126 | if self.socket is not None: 127 | self.socket.close() 128 | self.socket = None 129 | self.is_open = False 130 | -------------------------------------------------------------------------------- /dizzy/probe/tcp.py: -------------------------------------------------------------------------------- 1 | # tcp.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import ProbeParseException, ProbeException 32 | from dizzy.log import print_dizzy, VERBOSE_1, DEBUG 33 | from dizzy.tools import check_root 34 | from socket import inet_aton, inet_pton, AF_INET, AF_INET6, socket, SOCK_STREAM, SOL_SOCKET, SO_BROADCAST, \ 35 | SO_REUSEADDR, SHUT_RDWR 36 | from binascii import unhexlify 37 | 38 | 39 | class DizzyProbe(object): 40 | def __init__(self, section_proxy): 41 | self.target_host = section_proxy.get('target_host') 42 | self.target_port = section_proxy.getint('target_port') 43 | self.source_host = section_proxy.get('source_host', None) 44 | self.source_port = section_proxy.getint('source_port', None) 45 | if not self.source_host is None and self.source_port <= 1024: 46 | check_root("use a source port <= 1024") 47 | self.timeout = section_proxy.getfloat('timeout', 1) 48 | self.retry = section_proxy.getint('retry', 2) 49 | self.is_open = False 50 | self.socket = None 51 | 52 | try: 53 | inet_aton(self.target_host) 54 | self.af = AF_INET 55 | except Exception as e: 56 | try: 57 | inet_pton(AF_INET6, self.target_host) 58 | self.af = AF_INET6 59 | except Exception as f: 60 | raise ProbeParseException("probe/tcp: unknown address family: %s: %s, %s" % 61 | (self.target_host, e, f)) 62 | if not self.source_host is None: 63 | try: 64 | inet_aton(self.source_host) 65 | except Exception as e: 66 | try: 67 | inet_pton(AF_INET6, self.source_host) 68 | except Exception as f: 69 | raise ProbeParseException("probe/tcp: unknown address family: %s: %s, %s" % 70 | (self.source_host, e, f)) 71 | else: 72 | if not self.af == AF_INET6: 73 | raise ProbeParseException("probe/tcp: address family mismatch: %s - %s" % 74 | (self.target_host, self.source_host)) 75 | else: 76 | if not self.af == AF_INET: 77 | raise ProbeParseException("probe/tcp: address family mismatch: %s - %s" % 78 | (self.target_host, self.source_host)) 79 | 80 | def open(self): 81 | self.is_open = True 82 | 83 | def probe(self): 84 | try: 85 | self.socket = socket(self.af, SOCK_STREAM) 86 | if self.target_host == "255.255.255.255": 87 | self.socket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) 88 | self.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 89 | self.socket.settimeout(self.timeout) 90 | if not self.source_host is None and not self.source_port is None: 91 | self.socket.bind((self.source_host, self.source_port)) 92 | except Exception as e: 93 | if not self.socket is None: 94 | self.socket.close() 95 | print_dizzy("probe/tcp: open error: %s" % e) 96 | print_dizzy(e, DEBUG) 97 | 98 | for attempt in range(1, self.retry + 1): 99 | print_dizzy("probe/tcp: probe attempt: %d" % attempt, VERBOSE_1) 100 | try: 101 | self.socket.connect((self.target_host, self.target_port)) 102 | except (ConnectionAbortedError, ConnectionRefusedError) as e: 103 | pass 104 | except Exception as e: 105 | print_dizzy("probe/tcp: probe error: '%s'" % type(e)) 106 | print_dizzy(e, DEBUG) 107 | else: 108 | self.socket.close() 109 | return True 110 | return False 111 | 112 | def close(self): 113 | if not self.is_open: 114 | return 115 | self.is_open = False 116 | -------------------------------------------------------------------------------- /dizzy/profile.py: -------------------------------------------------------------------------------- 1 | # profile.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from time import time 32 | import threading 33 | import sys 34 | from collections import deque 35 | try: 36 | from resource import getrusage, RUSAGE_SELF 37 | except ImportError: 38 | RUSAGE_SELF = 0 39 | def getrusage(who=0): 40 | return [0.0, 0.0] # on non-UNIX platforms cpu_time always 0.0 41 | 42 | p_stats = None 43 | p_start_time = None 44 | 45 | def profiler(frame, event, arg): 46 | if event not in ('call','return'): return profiler 47 | #### gather stats #### 48 | rusage = getrusage(RUSAGE_SELF) 49 | t_cpu = rusage[0] + rusage[1] # user time + system time 50 | code = frame.f_code 51 | fun = (code.co_name, code.co_filename, code.co_firstlineno) 52 | #### get stack with functions entry stats #### 53 | ct = threading.currentThread() 54 | try: 55 | p_stack = ct.p_stack 56 | except AttributeError: 57 | ct.p_stack = deque() 58 | p_stack = ct.p_stack 59 | #### handle call and return #### 60 | if event == 'call': 61 | p_stack.append((time(), t_cpu, fun)) 62 | elif event == 'return': 63 | try: 64 | t,t_cpu_prev,f = p_stack.pop() 65 | assert f == fun 66 | except IndexError: # TODO investigate 67 | t,t_cpu_prev,f = p_start_time, 0.0, None 68 | call_cnt, t_sum, t_cpu_sum = p_stats.get(fun, (0, 0.0, 0.0)) 69 | p_stats[fun] = (call_cnt+1, t_sum+time()-t, t_cpu_sum+t_cpu-t_cpu_prev) 70 | return profiler 71 | 72 | 73 | def profile_on(): 74 | global p_stats, p_start_time 75 | p_stats = {} 76 | p_start_time = time() 77 | threading.setprofile(profiler) 78 | sys.setprofile(profiler) 79 | 80 | 81 | def profile_off(): 82 | threading.setprofile(None) 83 | sys.setprofile(None) 84 | 85 | def get_profile_stats(): 86 | """ 87 | returns dict[function_tuple] -> stats_tuple 88 | where 89 | function_tuple = (function_name, filename, lineno) 90 | stats_tuple = (call_cnt, real_time, cpu_time) 91 | """ 92 | return p_stats 93 | -------------------------------------------------------------------------------- /dizzy/session/__init__.py: -------------------------------------------------------------------------------- 1 | # __init__.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | __all__ = [ "eth", "http", "sctp", "ssl", "stdout", "stdout-hex", "tcp", "udp" ] 33 | 34 | class SessionParseException(Exception): 35 | pass 36 | 37 | class SessionException(Exception): 38 | pass 39 | -------------------------------------------------------------------------------- /dizzy/session/eth.py: -------------------------------------------------------------------------------- 1 | # eth.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import SessionException, SessionParseException 32 | from socket import AF_INET, IPPROTO_IP, gethostname, PF_PACKET, SOCK_RAW, socket, gethostbyname 33 | from ctypes import Structure, c_char, c_short, create_string_buffer 34 | from dizzy.config import CONFIG 35 | from dizzy.log import print_dizzy, DEBUG 36 | from dizzy.tools import check_root 37 | 38 | class Ifreq(Structure): 39 | _fields_ = [("ifr_ifrn", c_char * 16), ("ifr_flags", c_short)] 40 | 41 | class DizzySession(object): 42 | IFF_PROMISC = 0x100 43 | SIOCGIFFLAGS = 0x8913 44 | SIOCSIFFLAGS = 0x8914 45 | ETH_P_ALL = 0x0003 46 | 47 | def __init__(self, section_proxy): 48 | check_root("use the ETH session") 49 | 50 | self.interface = section_proxy.get('target_interface') 51 | self.timeout = section_proxy.getfloat('timeout', 1) 52 | self.recv_buffer = section_proxy.getfloat('recv_buffer', 4096) 53 | self.auto_reopen = section_proxy.getboolean('auto_reopen', True) 54 | self.server_side = section_proxy.getboolean('server', False) 55 | self.read_first = self.server_side 56 | self.read_first = section_proxy.getboolean('read_first', self.read_first) 57 | self.is_open = False 58 | 59 | def open(self): 60 | try: 61 | if CONFIG["GLOBALS"]["PLATFORM"] == "Windows": 62 | from socket import SIO_RCVALL, RCVALL_ON 63 | self.s = socket(AF_INET, SOCK_RAW, IPPROTO_IP) 64 | host = gethostbyname(gethostname()) 65 | self.s.bind((host, 0)) 66 | # enable promisc 67 | self.s.ioctl(SIO_RCVALL, RCVALL_ON) 68 | else: 69 | self.s = socket(PF_PACKET, SOCK_RAW, self.ETH_P_ALL) 70 | # set interface 71 | self.s.bind((self.interface, self.ETH_P_ALL)) 72 | # enable promisc 73 | import fcntl 74 | self.ifr = Ifreq() 75 | ifname = create_string_buffer(self.interface.encode(CONFIG["GLOBALS"]["CODEC"])) 76 | self.ifr.ifr_ifrn = ifname.value 77 | fcntl.ioctl(self.s.fileno(), self.SIOCGIFFLAGS, self.ifr) # G for Get 78 | self.ifr.ifr_flags |= self.IFF_PROMISC 79 | fcntl.ioctl(self.s.fileno(), self.SIOCSIFFLAGS, self.ifr) # S for Set 80 | self.maxsize = 1500 81 | except Exception as e: 82 | raise SessionException("session/eth: cant open session: %s" % e) 83 | else: 84 | self.is_open = True 85 | 86 | def close(self): 87 | if CONFIG["GLOBALS"]["PLATFORM"] == "Windows": 88 | self.s.ioctl(SIO_RCVALL, RCVALL_OFF) 89 | else: 90 | self.ifr.ifr_flags &= ~self.IFF_PROMISC 91 | import fcntl 92 | fcntl.ioctl(self.s.fileno(), self.SIOCSIFFLAGS, self.ifr) 93 | self.is_open = False 94 | 95 | def send(self, data): 96 | try: 97 | if not self.maxsize is None and len(data) > self.maxsize: 98 | data = data[:self.maxsize - 1] 99 | print_dizzy("session/eth: Truncated data to %d byte." % self.maxsize, DEBUG) 100 | self.s.send(data) 101 | except Exception as e: 102 | if self.auto_reopen: 103 | print_dizzy("session/eth: session got closed '%s', autoreopening..." % e, DEBUG) 104 | print_dizzy(e, DEBUG) 105 | self.close() 106 | self.open() 107 | else: 108 | self.close() 109 | raise SessionException("error on sending '%s', connection closed." % e) 110 | 111 | def recv(self): 112 | return self.s.recv(2048) 113 | -------------------------------------------------------------------------------- /dizzy/session/http.py: -------------------------------------------------------------------------------- 1 | # http.py 2 | # 3 | # Copyright 2018 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, frWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from . import SessionException, SessionParseException 33 | from http.client import HTTPConnection 34 | from http.server import HTTPServer, BaseHTTPRequestHandler 35 | from threading import Thread, Lock 36 | from time import sleep 37 | from dizzy.log import print_dizzy, DEBUG, VERBOSE_1, VERBOSE_2 38 | 39 | class DizzyHTTPServerThread(Thread): 40 | def __init__(self, server): 41 | Thread.__init__(self) 42 | self.server = server 43 | 44 | def run(self): 45 | self.server.serve_forever() 46 | 47 | class DizzySession(object): 48 | def __init__(self, section_proxy): 49 | self.dest = section_proxy.get('target_host') 50 | self.dport = section_proxy.getint('target_port', 80) 51 | self.src = section_proxy.get('source_host', '') 52 | self.src_str = self.src 53 | if self.src == '': 54 | self.src = None 55 | self.sport = section_proxy.getint('source_port', 80) 56 | self.method = section_proxy.get('method', 'GET') 57 | self.url = section_proxy.get('url') 58 | self.headers = {} 59 | headers = section_proxy.get('headers', '') 60 | if not headers == '': 61 | for l in headers.split(";"): 62 | r = l.split(":") 63 | self.headers[r[0]] = ":".join(r[1:]) 64 | self.cookies = {} 65 | self.timeout = section_proxy.getfloat('timeout', 1) 66 | self.auto_reopen = section_proxy.getboolean('auto_reopen', True) 67 | self.retry = section_proxy.getint('retry', 3) 68 | self.server_side = section_proxy.getboolean('server', False) 69 | self.read_first = self.server_side 70 | self.read_first = section_proxy.getboolean('read_first', self.read_first) 71 | self.is_open = False 72 | self.res = None 73 | self.thread = None 74 | self.send_lock = Lock() 75 | self.recv_lock = Lock() 76 | self.rlist = [] 77 | self.slist = [] 78 | 79 | class DizzyHTTPRequestHandler(BaseHTTPRequestHandler): 80 | dest = self.dest 81 | dport = self.dport 82 | send_lock = self.send_lock 83 | recv_lock = self.recv_lock 84 | rlist = self.rlist 85 | slist = self.slist 86 | set_headers = self.headers 87 | protocol_version = "HTTP/1.1" 88 | 89 | def all_methods(self): 90 | (addr, port) = self.client_address 91 | if addr == self.dest: 92 | print_dizzy("http/request_handler: handling request from %s" % addr, VERBOSE_2) 93 | while True and not self.server._BaseServer__shutdown_request: 94 | with self.recv_lock: 95 | if len(self.rlist) == 0: 96 | break 97 | print_dizzy("http/request_handler: rlist not empty", DEBUG) 98 | sleep(0.1) 99 | with self.recv_lock: 100 | length = int(self.headers['content-length']) 101 | self.rlist.append(self.rfile.read(length)) 102 | while True and not self.server._BaseServer__shutdown_request: 103 | with self.send_lock: 104 | if len(self.slist) == 1: 105 | break 106 | print_dizzy("http/request_handler: slist empty", DEBUG) 107 | sleep(0.1) 108 | with self.send_lock: 109 | data = self.slist.pop() 110 | self.send_response(200) 111 | for i in self.set_headers: 112 | self.send_header(i, self.set_headers[i]) 113 | self.send_header('Content-Length', len(data)) 114 | self.end_headers() 115 | self.wfile.write(data) 116 | else: 117 | print_dizzy("http/request_handler: denied access for %s" % addr, VERBOSE_2) 118 | 119 | def log_message(self, format, *args): 120 | return 121 | 122 | do_GET = all_methods 123 | do_HEAD = all_methods 124 | do_POST = all_methods 125 | do_PUT = all_methods 126 | do_DELETE = all_methods 127 | do_CONNECT = all_methods 128 | do_OPTIONS = all_methods 129 | do_TRACE = all_methods 130 | do_PATCH = all_methods 131 | 132 | self.request_handler = DizzyHTTPRequestHandler 133 | 134 | def open(self): 135 | try: 136 | if not self.server_side: 137 | self.connection = HTTPConnection(self.dest, self.dport, timeout=self.timeout, source_address=self.src) 138 | else: 139 | attempt = 0 140 | while attempt < self.retry: 141 | try: 142 | self.connection = HTTPServer((self.src_str, self.sport), self.request_handler) 143 | except OSError: 144 | attempt += 1 145 | sleep(1) 146 | continue 147 | else: 148 | break 149 | 150 | self.thread = DizzyHTTPServerThread(self.connection) 151 | self.thread.start() 152 | except Exception as e: 153 | raise SessionException("http/open: cant open session: %s" % e) 154 | else: 155 | self.is_open = True 156 | 157 | def close(self): 158 | if not self.is_open: 159 | return 160 | if not self.server_side: 161 | self.connection.close() 162 | self.res = None 163 | else: 164 | self.connection.shutdown() 165 | self.is_open = False 166 | 167 | def send(self, data): 168 | try: 169 | if not self.server_side: 170 | headers = self.headers 171 | cookies = ";".join(["%s=%s" % (n, v) for (n, v) in self.cookies.items()]) 172 | if len(cookies) > 0: 173 | headers.update({"Cookie" : cookies}) 174 | self.connection.request(self.method, self.url, body=data, headers=headers) 175 | self.res = self.connection.getresponse() 176 | headers = dict(self.res.getheaders()) 177 | for h in headers: 178 | if h == "Set-Cookie": 179 | try: 180 | nv = headers[h].split(";")[0].split("=") 181 | self.cookies[nv[0]] = nv[1] 182 | except: 183 | print_dizzy("http/send: failed to parse set-cookie: %s" % headers[h], VERBOSE_1) 184 | else: 185 | while True: 186 | with self.send_lock: 187 | if len(self.slist) == 0: 188 | break 189 | print_dizzy("http/send: slist not empty", DEBUG) 190 | sleep(0.1) 191 | with self.send_lock: 192 | self.slist.append(data) 193 | print_dizzy("http/send: pushed %s" % data, DEBUG) 194 | except Exception as e: 195 | if self.auto_reopen: 196 | print_dizzy("http/send: session got closed '%s', auto reopening..." % e, DEBUG) 197 | print_dizzy(e, DEBUG) 198 | self.close() 199 | self.open() 200 | else: 201 | self.close() 202 | raise SessionException("http/send: error on sending '%s', connection closed." % e) 203 | 204 | def recv(self): 205 | #from traceback import print_stack 206 | #print_stack() 207 | 208 | if not self.server_side: 209 | if not self.res is None: 210 | return self.res.read() 211 | else: 212 | while True: 213 | with self.recv_lock: 214 | if len(self.rlist) == 1: 215 | break 216 | print_dizzy("http/recv: rlist empty", DEBUG) 217 | sleep(0.1) 218 | with self.recv_lock: 219 | data = self.rlist.pop() 220 | print_dizzy("http/recv: poped %s" % data, DEBUG) 221 | return data 222 | -------------------------------------------------------------------------------- /dizzy/session/sctp.py: -------------------------------------------------------------------------------- 1 | # sctp.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import SessionException, SessionParseException 32 | from socket import inet_aton, AF_INET, inet_pton, AF_INET6, htonl, SOCK_SEQPACKET, SOL_SOCKET, SO_BROADCAST, SO_SNDBUF,\ 33 | SO_REUSEADDR, socket 34 | from traceback import print_exc 35 | from ctypes import Structure, c_uint16, c_uint32, c_int 36 | from dizzy.log import print_dizzy, DEBUG 37 | 38 | class sctp_sndrcvinfo(Structure): 39 | _fields_ = [("sinfo_stream", c_uint16), 40 | ("sinfo_ssn", c_uint16), 41 | ("sinfo_flags", c_uint16), 42 | ("sinfo_ppid", c_uint32), 43 | ("sinfo_context", c_uint32), 44 | ("sinfo_timetolive", c_uint32), 45 | ("sinfo_tsn", c_uint32), 46 | ("sinfo_cumtsn", c_uint32), 47 | ("sinfo_assoc_id", c_int)] 48 | 49 | class DizzySession(object): 50 | SCTP_STREAM = 1 51 | SCTP_PPID = 1 52 | SCTP_FLAGS = 0 #MSG_ADDR_OVER ? 53 | SOL_SCTP = 0x84 54 | IPPROTO_SCTP = 0x84 55 | SCTP_DEFAULT_SEND_PARAM = 0xa 56 | SCTP_SNDRCV = 1 57 | 58 | def __init__(self, section_proxy): 59 | self.dest = section_proxy.get('target_host') 60 | self.dport = section_proxy.getint('target_port') 61 | self.src = section_proxy.get('source_host', '') 62 | self.sport = section_proxy.getint('source_port') 63 | self.timeout = section_proxy.getfloat('timeout', 1) 64 | self.recv_buffer = section_proxy.getfloat('recv_buffer', 4096) 65 | self.auto_reopen = section_proxy.getboolean('auto_reopen', True) 66 | self.server_side = section_proxy.getboolean('server', False) 67 | self.read_first = self.server_side 68 | self.read_first = section_proxy.getboolean('read_first', self.read_first) 69 | self.connect_retry = section_proxy.getint('retry', 3) 70 | self.sid = section_proxy.getint('streamid', self.SCTP_STREAM) 71 | self.ppid = section_proxy.getint('ppid', self.SCTP_PPID) 72 | self.is_open = False 73 | 74 | try: 75 | inet_aton(self.dest) 76 | self.af = AF_INET 77 | except Exception as e: 78 | try: 79 | inet_pton(AF_INET6, self.dest) 80 | self.af = AF_INET6 81 | except Exception as f: 82 | raise SessionParseException("unknown address family: %s: %s, %s" % (self.dest, e, f)) 83 | if self.src != '': 84 | try: 85 | inet_aton(self.src) 86 | except Exception as e: 87 | try: 88 | inet_pton(AF_INET6, self.src) 89 | except Exception as f: 90 | raise SessionParseException("unknown address family: %s: %s, %s" % (self.src, e, f)) 91 | else: 92 | if not self.af == AF_INET6: 93 | raise SessionParseException("address family missmatch: %s - %s" % (self.dest, self.src)) 94 | else: 95 | if not self.af == AF_INET: 96 | raise SessionParseException("address family missmatch: %s - %s" % (self.dest, self.src)) 97 | 98 | self.sndrcvinfo = sctp_sndrcvinfo() 99 | self.sndrcvinfo.sinfo_stream = self.sid 100 | self.sndrcvinfo.sinfo_ppid = htonl(self.ppid) 101 | self.cs = None 102 | self.maxsize = 65534 103 | 104 | def open(self): 105 | try: 106 | self.s = socket(self.af, SOCK_SEQPACKET) 107 | if self.dest == "255.255.255.255": 108 | self.s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) 109 | self.s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 110 | self.s.settimeout(self.timeout) 111 | sendbuf = self.s.getsockopt(SOL_SOCKET, SO_SNDBUF) 112 | if sendbuf < self.maxsize: 113 | self.maxsize = sendbuf 114 | self.s.setsockopt(self.SOL_SCTP, self.SCTP_DEFAULT_SEND_PARAM, self.sndrcvinfo) 115 | if not self.sport is None: 116 | self.s.bind((self.src, self.sport)) 117 | except Exception as e: 118 | raise SessionException("cant open session: %s" % str(e)) 119 | else: 120 | self.is_open = True 121 | 122 | def close(self): 123 | if not self.is_open: 124 | return 125 | if not self.s is None: 126 | self.s.close() 127 | self.s = None 128 | if not self.cs is None: 129 | self.cs.close() 130 | self.cs = None 131 | self.is_open = False 132 | 133 | def send(self, data, streamid=None): 134 | try: 135 | if not self.maxsize is None and len(data) > self.maxsize: 136 | data = data[:self.maxsize - 1] 137 | print_dizzy("session/sctp: Truncated data to %d byte." % self.maxsize, DEBUG) 138 | if not streamid is None: 139 | sndrcvinfo = sctp_sndrcvinfo() 140 | sndrcvinfo.sinfo_stream = streamid 141 | sndrcvinfo.sinfo_ppid = htonl(self.ppid) 142 | self.s.sendmsg([data], [(self.IPPROTO_SCTP, self.SCTP_SNDRCV, sndrcvinfo)], 0, (self.dest, self.dport)) 143 | else: 144 | self.s.sendto(data, (self.dest, self.dport)) 145 | except Exception as e: 146 | if self.auto_reopen: 147 | print_dizzy("session/sctp: session got closed '%s', autoreopening..." % e, DEBUG) 148 | self.close() 149 | self.open() 150 | else: 151 | self.close() 152 | raise SessionException("error on sending '%s', connection closed." % e) 153 | 154 | def recv(self): 155 | if self.server_side: 156 | return self.cs.recv(self.recv_buffer) 157 | else: 158 | return self.s.recv(self.recv_buffer) 159 | -------------------------------------------------------------------------------- /dizzy/session/ssl.py: -------------------------------------------------------------------------------- 1 | # ssl.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import SessionException, SessionParseException 32 | from socket import inet_aton, AF_INET, inet_pton, AF_INET6, SOCK_STREAM, SOL_SOCKET, SO_BROADCAST, SO_REUSEADDR, \ 33 | SO_SNDBUF, timeout, socket 34 | from traceback import print_exc 35 | from ssl import SSLSocket, SSLError 36 | from select import select 37 | from dizzy.config import CONFIG 38 | from dizzy.log import print_dizzy, DEBUG 39 | 40 | class DizzySession(object): 41 | def __init__(self, section_proxy): 42 | self.dest = section_proxy.get('target_host') 43 | self.dport = section_proxy.getint('target_port') 44 | self.src = section_proxy.get('source_host', '') 45 | self.sport = section_proxy.getint('source_port') 46 | self.client_cert = section_proxy.get('certfile') 47 | self.client_key = section_proxy.get('keyfile') 48 | self.timeout = section_proxy.getfloat('timeout', 1) 49 | self.recv_buffer = section_proxy.getfloat('recv_buffer', 4096) 50 | self.auto_reopen = section_proxy.getboolean('auto_reopen', True) 51 | self.server_side = section_proxy.getboolean('server', False) 52 | self.read_first = self.server_side 53 | self.read_first = section_proxy.getboolean('read_first', self.read_first) 54 | self.connect_retry = section_proxy.getint('retry', 3) 55 | self.is_open = False 56 | 57 | try: 58 | inet_aton(self.dest) 59 | self.af = AF_INET 60 | except Exception as e: 61 | try: 62 | inet_pton(AF_INET6, self.dest) 63 | self.af = AF_INET6 64 | except Exception as f: 65 | raise SessionParseException("unknown address family: %s: %s, %s" % (self.dest, e, f)) 66 | if self.src != '': 67 | try: 68 | inet_aton(self.src) 69 | except Exception as e: 70 | try: 71 | inet_pton(AF_INET6, self.src) 72 | except Exception as f: 73 | raise SessionParseException("unknown address family: %s: %s, %s" % (self.src, e, f)) 74 | else: 75 | if not self.af == AF_INET6: 76 | raise SessionParseException("address family missmatch: %s - %s" % (self.dest, self.src)) 77 | else: 78 | if not self.af == AF_INET: 79 | raise SessionParseException("address family missmatch: %s - %s" % (self.dest, self.src)) 80 | 81 | self.cs = None 82 | self.maxsize = 65534 83 | 84 | def open(self): 85 | try: 86 | self.s = socket(self.af, SOCK_STREAM) 87 | if self.dest == "255.255.255.255": 88 | self.s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) 89 | self.s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 90 | self.s.settimeout(self.timeout) 91 | sendbuf = self.s.getsockopt(SOL_SOCKET, SO_SNDBUF) 92 | if sendbuf < self.maxsize: 93 | self.maxsize = sendbuf 94 | if not self.sport is None: 95 | if self.af == AF_INET: 96 | self.s.bind((self.src, self.sport)) 97 | elif self.af == AF_INET6: 98 | try: 99 | self.s.bind((self.src, self.port, 0, 0)) 100 | except: 101 | if not self.interface is '': 102 | (_, _, _, _, addri) = getaddrinfo("%s%%%s" % (self.src, self.interface), self.sport, family=AF_INET6, proto=IPPROTO_UDP)[0] 103 | self.s.bind(addri) 104 | else: 105 | raise SessionException("cant bind to ipv6 LL without interface!") 106 | self.s = SSLSocket(self.s, self.client_key, self.client_cert, ssl_version=3) 107 | if self.server_side: 108 | self.s.listen(1) 109 | (self.cs, (rip, rport)) = self.s.accept() 110 | if self.dport: 111 | while self.dport != rport or self.src != rip: 112 | if self.dport != rport: 113 | print_dizzy("session/ssl: remote port %i not destination port %i" % (rport, self.dport), DEBUG) 114 | if self.src != rip: 115 | print_dizzy("session/ssl: remote ip %s not destination ip %i" % (rip, self.dst), DEBUG) 116 | (self.cs, (sip, rport)) = self.s.accept() 117 | self.cs.settimeout(self.timeout) 118 | else: 119 | connected = False 120 | attempt = 1 121 | try: 122 | self.s.connect((self.dest, self.dport)) 123 | connected = True 124 | except (timeout, SSLError): 125 | print_dizzy("session/ssl: Connection attempt %d timed out." % attempt) 126 | while not connected and attempt <= self.connect_retry: 127 | try: 128 | (r, w, x) = select([], [self.s], [], self.timeout) 129 | if self.s in w: 130 | connected = True 131 | except: 132 | pass 133 | attempt += 1 134 | if not connected: 135 | raise SessionException("too much connection attempts") 136 | except Exception as e: 137 | raise SessionException("cant open session: %s" % str(e)) 138 | else: 139 | self.is_open = True 140 | 141 | def close(self): 142 | self.s.close() 143 | self.s = None 144 | if self.cs: 145 | self.cs.close() 146 | self.cs = None 147 | self.is_open = False 148 | 149 | def send(self, data): 150 | try: 151 | if not self.maxsize is None and len(data) > self.maxsize: 152 | data = data[:self.maxsize - 1] 153 | print_dizzy("Truncated data to %d byte." % self.maxsize, DEBUG) 154 | if self.server_side: 155 | if not self.cs: 156 | raise SessionException("no client connection, cant send") 157 | self.cs.send(data) 158 | else: 159 | self.s.send(data) 160 | except Exception as e: 161 | if self.auto_reopen: 162 | print_dizzy("session got closed '%s', autoreopening..." % e, DEBUG) 163 | self.close() 164 | self.open() 165 | else: 166 | self.close() 167 | raise SessionException("error on sending '%s', connection closed." % e) 168 | 169 | def recv(self): 170 | if self.server_side: 171 | return self.cs.recv(self.recv_buffer) 172 | else: 173 | return self.s.recv(self.recv_buffer) 174 | -------------------------------------------------------------------------------- /dizzy/session/stdout-hex.py: -------------------------------------------------------------------------------- 1 | # stdout-hex.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import SessionException 32 | from traceback import print_exc 33 | from sys import stdout, stdin 34 | from binascii import hexlify 35 | from dizzy.log import print_dizzy, DEBUG 36 | from dizzy.config import CONFIG 37 | 38 | class DizzySession(object): 39 | def __init__(self, section_proxy): 40 | self.auto_reopen = section_proxy.getboolean('auto_reopen', True) 41 | self.read_first = section_proxy.getboolean('read_first', False) 42 | self.is_open = False 43 | 44 | def open(self): 45 | try: 46 | self.f = stdout.buffer 47 | except Exception as e: 48 | raise SessionException("cant open session: %s" % e) 49 | else: 50 | self.is_open = True 51 | 52 | def close(self): 53 | self.is_open = False 54 | 55 | def send(self, data): 56 | try: 57 | self.f.write(hexlify(data)) 58 | except Exception as e: 59 | if self.auto_reopen: 60 | print_dizzy("session/stdout-hex: session got closed '%s', autoreopening..." % e, DEBUG) 61 | self.close() 62 | self.open() 63 | else: 64 | self.close() 65 | raise SessionException("error on sending '%s', connection closed." % e) 66 | 67 | def recv(self): 68 | line = stdin.readline() 69 | if line == ".\n": 70 | return None 71 | else: 72 | return line.encode(CONFIG["GLOBALS"]["CODEC"]) 73 | -------------------------------------------------------------------------------- /dizzy/session/stdout.py: -------------------------------------------------------------------------------- 1 | # stdout.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import SessionException 32 | from sys import stdout, stdin 33 | from traceback import print_exc 34 | from dizzy.log import print_dizzy, DEBUG 35 | from dizzy.config import CONFIG 36 | 37 | class DizzySession(object): 38 | def __init__(self, section_proxy): 39 | self.auto_reopen = section_proxy.getboolean('auto_reopen', True) 40 | self.read_first = section_proxy.getboolean('read_first', False) 41 | self.send_lf = section_proxy.getboolean('send_lf', True) 42 | self.is_open = False 43 | 44 | def open(self): 45 | try: 46 | self.f = stdout.buffer 47 | except Exception as e: 48 | raise SessionException("cant open session: %s" % e) 49 | else: 50 | self.is_open = True 51 | 52 | def close(self): 53 | self.is_open = False 54 | 55 | def send(self, data): 56 | try: 57 | self.f.write(data) 58 | if self.send_lf: 59 | self.f.write(b"\n") 60 | self.f.flush() 61 | except Exception as e: 62 | if self.auto_reopen: 63 | print_dizzy("session/stdout: session got closed '%s', autoreopening..." % e, DEBUG) 64 | self.close() 65 | self.open() 66 | else: 67 | self.close() 68 | raise SessionException("error on sending '%s', connection closed." % e) 69 | 70 | def recv(self): 71 | line = bytes(stdin.readline(), CONFIG["GLOBALS"]["CODEC"]) 72 | if line == b".\n": 73 | return None 74 | else: 75 | return line 76 | -------------------------------------------------------------------------------- /dizzy/session/tcp.py: -------------------------------------------------------------------------------- 1 | # tcp.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, frWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import SessionException, SessionParseException 32 | from socket import inet_aton, inet_pton, AF_INET, AF_INET6, socket, SOCK_STREAM, SOL_SOCKET, SO_BROADCAST, \ 33 | SO_REUSEADDR, SO_SNDBUF, timeout, IPPROTO_TCP 34 | from select import select 35 | from traceback import print_exc 36 | from dizzy.log import print_dizzy, DEBUG 37 | 38 | class DizzySession(object): 39 | def __init__(self, section_proxy): 40 | self.dest = section_proxy.get('target_host') 41 | self.dport = section_proxy.getint('target_port') 42 | self.src = section_proxy.get('source_host', '') 43 | self.sport = section_proxy.getint('source_port') 44 | self.interface = section_proxy.get('interface', '') 45 | self.timeout = section_proxy.getfloat('timeout', 1) 46 | self.recv_buffer = section_proxy.getfloat('recv_buffer', 4096) 47 | self.auto_reopen = section_proxy.getboolean('auto_reopen', True) 48 | self.server_side = section_proxy.getboolean('server', False) 49 | self.read_first = self.server_side 50 | self.read_first = section_proxy.getboolean('read_first', self.read_first) 51 | self.connect_retry = section_proxy.getint('retry', 3) 52 | self.is_open = False 53 | self.client_socket = None 54 | self.maxsize = 65534 55 | 56 | try: 57 | inet_aton(self.dest) 58 | self.af = AF_INET 59 | except Exception as e: 60 | try: 61 | inet_pton(AF_INET6, self.dest) 62 | self.af = AF_INET6 63 | except Exception as f: 64 | raise SessionParseException("unknown address family: %s: %s, %s" % (self.dest, e, f)) 65 | if self.src != '': 66 | try: 67 | inet_aton(self.src) 68 | except Exception as e: 69 | try: 70 | inet_pton(AF_INET6, self.src) 71 | except Exception as f: 72 | raise SessionParseException("unknown address family: %s: %s, %s" % (self.src, e, f)) 73 | else: 74 | if not self.af == AF_INET6: 75 | raise SessionParseException("address family missmatch: %s - %s" % (self.dest, self.src)) 76 | else: 77 | if not self.af == AF_INET: 78 | raise SessionParseException("address family missmatch: %s - %s" % (self.dest, self.src)) 79 | 80 | def open(self): 81 | try: 82 | self.socket = socket(self.af, SOCK_STREAM) 83 | if self.dest == "255.255.255.255": 84 | self.socket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) 85 | self.socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 86 | self.socket.settimeout(self.timeout) 87 | sendbuf = self.socket.getsockopt(SOL_SOCKET, SO_SNDBUF) 88 | if sendbuf < self.maxsize: 89 | self.maxsize = sendbuf 90 | if not self.sport is None and not self.src is None: 91 | if self.af == AF_INET: 92 | self.socket.bind((self.src, self.sport)) 93 | elif self.af == AF_INET6: 94 | try: 95 | #this should be the way for v6 addr, for some reason failed on me ): 96 | #self.socket.bind((self.src, self.sport, 0, 0)) 97 | self.socket.bind((self.src, self.sport)) 98 | except: 99 | if not self.interface is '': 100 | (_, _, _, _, addri) = getaddrinfo("%s%%%s" % (self.src, self.interface), self.sport, family=AF_INET6, proto=IPPROTO_TCP)[0] 101 | self.socket.bind(addri) 102 | else: 103 | raise SessionException("cant bind to ipv6 LL without interface!") 104 | if self.server_side: 105 | self.socket.listen(1) 106 | (self.client_socket, (rip, rport)) = self.socket.accept() 107 | if self.dport: 108 | while self.dport != rport or self.src != rip: 109 | if self.dport != rport: 110 | print_dizzy("session/tcp: remote port %i not destination port %i" % (rport, self.dport), DEBUG) 111 | if self.src != rip: 112 | print_dizzy("session/tcp: remote ip %s not destination ip %i" % (rip, self.dst)) 113 | (self.client_socket, (sip, rport)) = self.socket.accept() 114 | self.client_socket.settimeout(self.timeout) 115 | else: 116 | connected = False 117 | attempt = 1 118 | try: 119 | self.socket.connect((self.dest, self.dport)) 120 | connected = True 121 | except timeout: 122 | print_dizzy("session/tcp: Connection attempt %d timed out." % (attempt)) 123 | while not connected and attempt <= self.connect_retry: 124 | try: 125 | (r, w, x) = select([], [self.socket], [], self.timeout) 126 | if self.socket in w: 127 | connected = True 128 | except: 129 | pass 130 | attempt += 1 131 | if not connected: 132 | raise SessionException("too much connection attempts") 133 | except Exception as e: 134 | raise SessionException("cant open session: %s" % e) 135 | else: 136 | self.is_open = True 137 | 138 | def close(self): 139 | if not self.is_open: 140 | return 141 | if not self.socket is None: 142 | self.socket.close() 143 | self.socket = None 144 | if not self.client_socket is None: 145 | self.client_socket.close() 146 | self.client_socket = None 147 | self.is_open = False 148 | 149 | def send(self, data): 150 | try: 151 | if not self.maxsize is None and len(data) > self.maxsize: 152 | data = data[:self.maxsize - 1] 153 | print_dizzy("session/tcp: Truncated data to %d byte." % self.maxsize, DEBUG) 154 | 155 | if self.server_side: 156 | if not self.client_socket: 157 | raise SessionException("no client connection, cant send") 158 | self.client_socket.send(data) 159 | else: 160 | self.socket.send(data) 161 | except Exception as e: 162 | if self.auto_reopen: 163 | print_dizzy("session/tcp: session got closed '%s', auto reopening..." % e, DEBUG) 164 | print_dizzy(e, DEBUG) 165 | self.close() 166 | self.open() 167 | else: 168 | self.close() 169 | raise SessionException("error on sending '%s', connection closed." % e) 170 | 171 | def recv(self): 172 | if self.server_side: 173 | return self.client_socket.recv(self.recv_buffer) 174 | else: 175 | return self.socket.recv(self.recv_buffer) 176 | -------------------------------------------------------------------------------- /dizzy/session/udp.py: -------------------------------------------------------------------------------- 1 | # udp.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from . import SessionException, SessionParseException 32 | from socket import inet_aton, AF_INET, inet_pton, AF_INET6, SOCK_DGRAM, SOL_SOCKET, SO_BROADCAST, SO_REUSEADDR, \ 33 | SO_SNDBUF, socket, getaddrinfo, IPPROTO_UDP 34 | from traceback import print_exc 35 | from dizzy.log import print_dizzy, DEBUG 36 | 37 | class DizzySession(object): 38 | def __init__(self, section_proxy): 39 | self.dest = section_proxy.get('target_host') 40 | self.dport = section_proxy.getint('target_port') 41 | self.src = section_proxy.get('source_host', '') 42 | self.sport = section_proxy.getint('source_port') 43 | self.timeout = section_proxy.getfloat('timeout', 1) 44 | self.recv_buffer = section_proxy.getfloat('recv_buffer', 4096) 45 | self.auto_reopen = section_proxy.getboolean('auto_reopen', True) 46 | self.server_side = section_proxy.getboolean('server', False) 47 | self.read_first = self.server_side 48 | self.read_first = section_proxy.getboolean('read_first', self.read_first) 49 | self.connect_retry = section_proxy.getint('retry', 3) 50 | self.interface = section_proxy.get('interface', "") 51 | self.is_open = False 52 | 53 | try: 54 | inet_aton(self.dest) 55 | self.af = AF_INET 56 | except Exception as e: 57 | try: 58 | inet_pton(AF_INET6, self.dest) 59 | self.af = AF_INET6 60 | except Exception as f: 61 | raise SessionParseException("unknown address family: %s: %s, %s" % (self.dest, e, f)) 62 | if self.src != '': 63 | try: 64 | inet_aton(self.src) 65 | except Exception as e: 66 | try: 67 | inet_pton(AF_INET6, self.src) 68 | except Exception as f: 69 | raise SessionParseException("unknown address family: %s: %s, %s" % (self.src, e, f)) 70 | else: 71 | if not self.af == AF_INET6: 72 | raise SessionParseException("address family missmatch: %s - %s" % (self.dest, self.src)) 73 | else: 74 | if not self.af == AF_INET: 75 | raise SessionParseException("address family missmatch: %s - %s" % (self.dest, self.src)) 76 | 77 | self.cs = None 78 | self.maxsize = 65534 79 | 80 | def open(self): 81 | try: 82 | self.s = socket(self.af, SOCK_DGRAM) 83 | if self.dest == "255.255.255.255": 84 | self.s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) 85 | self.s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 86 | self.s.settimeout(self.timeout) 87 | sendbuf = self.s.getsockopt(SOL_SOCKET, SO_SNDBUF) 88 | if sendbuf < self.maxsize: 89 | self.maxsize = sendbuf 90 | if not self.sport is None: 91 | if self.af == AF_INET: 92 | self.s.bind((self.src, self.sport)) 93 | elif self.af == AF_INET6: 94 | try: 95 | self.s.bind((self.src, self.port, 0, 0)) 96 | except: 97 | if not self.interface is '': 98 | (_, _, _, _, addri) = getaddrinfo("%s%%%s" % (self.src, self.interface), self.sport, family=AF_INET6, proto=IPPROTO_UDP)[0] 99 | self.s.bind(addri) 100 | else: 101 | raise SessionException("cant bind to ipv6 LL without interface!") 102 | except Exception as e: 103 | raise SessionException("cant open session: %s" % e) 104 | else: 105 | self.is_open = True 106 | 107 | def close(self): 108 | if not self.is_open: 109 | return 110 | if not self.s is None: 111 | self.s.close() 112 | self.s = None 113 | if not self.cs is None: 114 | self.cs.close() 115 | self.cs = None 116 | self.is_open = False 117 | 118 | def send(self, data): 119 | try: 120 | self.s.sendto(data, (self.dest, self.dport)) 121 | except Exception as e: 122 | if self.auto_reopen: 123 | print_dizzy("session/udp: session got closed '%s', autoreopening..." % e, DEBUG) 124 | self.close() 125 | self.open() 126 | else: 127 | self.close() 128 | raise SessionException("error on sending '%s', connection closed." % e) 129 | 130 | def recv(self): 131 | if self.server_side: 132 | return self.cs.recv(self.recv_buffer) 133 | else: 134 | return self.s.recv(self.recv_buffer) 135 | -------------------------------------------------------------------------------- /dizzy/state.py: -------------------------------------------------------------------------------- 1 | # state.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | class State: 33 | def __init__(self, obj): 34 | self.obj = obj 35 | self.iter = iter(obj) 36 | self.bak = None 37 | self.cur = None 38 | 39 | try: 40 | # Get the first value(it is the default value) and save it in self.cur 41 | self.next() 42 | except StopIteration: 43 | raise Exception("At least one element") 44 | 45 | def __next__(self): 46 | try: 47 | # Mutate and call the functions(if self.iter is a DizzIterator) and 48 | # save the current state in self.cur 49 | self.next() 50 | except StopIteration: 51 | # Reset the iterator and raise a StopIteration to signal that this field or dizz object is done 52 | self.iter = iter(self.obj) 53 | self.next() 54 | raise StopIteration 55 | 56 | def call_functions(self, when): 57 | self.cur = self.iter.call_functions(when) 58 | return self.cur 59 | 60 | def next(self): 61 | raise NotImplementedError("Overload Me !!!") -------------------------------------------------------------------------------- /dizzy/tests/__init__.py: -------------------------------------------------------------------------------- 1 | def first(iterable): 2 | return next(iter(iterable)) 3 | -------------------------------------------------------------------------------- /dizzy/tests/probe/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ernw/dizzy/c44b8ff0ac3f1acb577e848303a6b74de89d011a/dizzy/tests/probe/__init__.py -------------------------------------------------------------------------------- /dizzy/tests/probe/test_icmp.py: -------------------------------------------------------------------------------- 1 | # test_icmp.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from configparser import ConfigParser 32 | from unittest import TestCase, main 33 | from dizzy.probe.icmp import DizzyProbe 34 | 35 | 36 | class TestIcmp(TestCase): 37 | def test_probe(self): 38 | config_parser = ConfigParser() 39 | config_parser.add_section("probe") 40 | config_parser.set("probe", "type", "icmp") 41 | config_parser.set("probe", "target.host", "127.0.0.1") 42 | 43 | self.section_proxy = config_parser['probe'] 44 | 45 | probe = DizzyProbe(self.section_proxy) 46 | probe.open() 47 | result = probe.probe() 48 | probe.close() 49 | self.assertTrue(result) 50 | 51 | if __name__ == '__main__': 52 | main() 53 | -------------------------------------------------------------------------------- /dizzy/tests/probe/test_tcp.py: -------------------------------------------------------------------------------- 1 | # test_tcp.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from configparser import ConfigParser 32 | from unittest import TestCase, main 33 | from dizzy.probe.tcp import DizzyProbe 34 | 35 | 36 | class TestTcp(TestCase): 37 | def test_probe(self): 38 | config_parser = ConfigParser() 39 | config_parser.add_section("probe") 40 | config_parser.set("probe", "type", "tcp") 41 | config_parser.set("probe", "target.host", "127.0.0.1") 42 | config_parser.set("probe", "target.port", "12345") 43 | 44 | self.section_proxy = config_parser['probe'] 45 | 46 | probe = DizzyProbe(self.section_proxy) 47 | probe.open() 48 | result = probe.probe() 49 | probe.close() 50 | self.assertTrue(result) 51 | 52 | if __name__ == '__main__': 53 | main() 54 | -------------------------------------------------------------------------------- /dizzy/tests/test.py: -------------------------------------------------------------------------------- 1 | from dizzy.interaction import Interaction 2 | from dizzy.objects.field import Field 3 | from dizzy.functions.length import length 4 | from dizzy.dizz import Dizz, load_dizz 5 | from dizzy.tests import first 6 | from dizzy.functions.padding import padding 7 | from dizzy.functions.run_cmd import run_cmd 8 | from dizzy.functions.checksum import checksum 9 | from dizzy.value import Value 10 | from dizzy.objects.list import List 11 | from dizzy.log import print_dizzy, DEBUG, VERBOSE_2, VERBOSE_1, NORMAL, REDUCE, NONE, print_level, set_print_level 12 | from timeit import timeit 13 | from binascii import hexlify 14 | 15 | #print(timeit("print_dizzy('TEST', )", "from dizzy.log import print_dizzy, NORMAL", number=100000)) 16 | 17 | objects = [Field("test0", b"\x11\x11", fuzz="std"), Field("test1", b"\x22", fuzz="std"), 18 | Field("test2", b"\x33\x33", slice(9, 17), fuzz="std"), Field("length", b"\x00\x00", fuzz="std")] 19 | functions = [length("length", "test0", "test2")] 20 | d0 = Dizz("test0", objects, functions, fuzz="std") 21 | 22 | objects = [Field("test0", b"\xff", fuzz="full"), Field("test1", b"\xaa", 10, fuzz="std"), 23 | Field("test2", b"\x00\x00"), Field("checksum", b"\x00\x00")] 24 | functions = [checksum("checksum", "test0", "test2", "sha1")] 25 | d1 = Dizz("test1", objects, functions, fuzz="std") 26 | 27 | 28 | def inc(interaction_iterator, dizzy_iterator, response): 29 | i = int.from_bytes(dizzy_iterator["test2"].byte, "big") + 1 30 | dizzy_iterator["test2"] = Value(i.to_bytes(2, "big")) 31 | 32 | 33 | act = Interaction("Test", [d0, d1], {1: [inc]}) 34 | 35 | 36 | 37 | for i in List("List"): 38 | print(i) 39 | 40 | 41 | """ 42 | 43 | def checksum_inet(target, start, stop): 44 | def func(dizzy_iterator): 45 | dizzy_iterator[""] 46 | dizzy_iterator["NAME"]["ELEE"] = b"TTTTTT" 47 | 48 | return func 49 | 50 | objects = [Field("test0", b"\x11\x11", fuzz="std"), Field("test1", b"\x22", fuzz="std"), 51 | Field("test2", b"\x33\x33", slice(9, 17), fuzz="std"), Field("length", b"\x00\x00", fuzz="std"), 52 | Field("padding"), "dizzes/bgp/keepalive.dizz", Dizzy("NAME", "PFAD")] 53 | functions = [length("length", "test0", "test2"), padding("padding", "test0", "length", 8),] 54 | d0 = Dizzy("test0", objects, functions, fuzz="std", start_at=0) 55 | 56 | for i in d0: 57 | print(i) 58 | 59 | run_cmd('echo "AAA"') 60 | objects = [Field("test0", b"\xff", fuzz="full"), Field("test1", b"\xaa", 10, fuzz="std"), 61 | Field("test2", b"\x00\x00"), Field("checksum", b"\x00\x00")] 62 | functions = [checksum("checksum", "test0", "test2", "sha1")] 63 | d1 = Dizzy("test1", objects, functions, fuzz="std") 64 | 65 | def inc(interaction_iterator, dizzy_iterator, response): 66 | i = int.from_bytes(dizzy_iterator["test2"].byte, "big") + 1 67 | dizzy_iterator["test2"] = Value(i.to_bytes(2, "big")) 68 | 69 | 70 | for x in Interaction("Test", [d0, d1], {1: [inc]}): 71 | print(x) 72 | 73 | 74 | for i in List("WWW"): 75 | print(i) 76 | 77 | """ 78 | -------------------------------------------------------------------------------- /dizzy/tests/test_field.py: -------------------------------------------------------------------------------- 1 | # test_field.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from unittest import TestCase, main 32 | from dizzy.tests import first 33 | from dizzy.objects.field import Field 34 | from dizzy.value import Value 35 | 36 | 37 | class TestField(TestCase): 38 | def test_init(self): 39 | f = Field("test", b"\x01\x23", slice(10, 12), "std") 40 | self.assertEqual(f.name, "test") 41 | self.assertEqual(f.size, slice(10, 12, 1)) 42 | self.assertEqual(f.default, Value(b"\x01\x23", 10)) 43 | self.assertEqual(f.fuzz, "std") 44 | 45 | def test_add_aligned(self): 46 | chars1 = b"This is a test " 47 | chars2 = b"of adding adding aligned" 48 | chars3 = b" values." 49 | 50 | f1 = Field("test1", chars1) 51 | f2 = Field("test2", chars2) 52 | f3 = Field("test3", chars3) 53 | 54 | v1 = first(f1) + first(f2) + first(f3) 55 | 56 | self.assertTrue(isinstance(v1, Value)) 57 | self.assertEqual(v1.byte, chars1 + chars2 + chars3) 58 | self.assertEqual(v1.size // 8, len(chars1) + len(chars2) + len(chars3)) 59 | 60 | def test_add_unaligned(self): 61 | pass 62 | 63 | def test_iter(self): 64 | expected = [Value(b'\x01#', 10), Value(b'\x00\x00', 10), Value(b'\x00\x01', 10), Value(b'\x00\x02', 10), 65 | Value(b'\x00\x03', 10), Value(b'\x00\x04', 10), Value(b'\x03\xfb', 10), Value(b'\x03\xfc', 10), 66 | Value(b'\x03\xfd', 10), Value(b'\x03\xfe', 10), Value(b'\x03\xff', 10), Value(b'\x01\xfc', 10), 67 | Value(b'\x01\xfd', 10), Value(b'\x01\xfe', 10), Value(b'\x01\xff', 10), Value(b'\x01\x00', 10), 68 | Value(b'\x02\x00', 10), Value(b'\x03\x00', 10), Value(b'\x04\x00', 10)] 69 | f = Field("test", b"\x01\x23", 10, "std") 70 | self.assertEqual([i for i in f], expected) 71 | 72 | def test_size(self): 73 | f = Field("test", b"\x01\x23", 10, "std") 74 | self.assertEqual(f.length(), 19) 75 | self.assertEqual(len(list(f)), f.length()) 76 | 77 | 78 | if __name__ == '__main__': 79 | main() 80 | -------------------------------------------------------------------------------- /dizzy/tests/test_value.py: -------------------------------------------------------------------------------- 1 | # test_value.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from unittest import TestCase, main 32 | from dizzy.value import Value 33 | 34 | 35 | class TestValue(TestCase): 36 | def test_init(self): 37 | v = Value(b"\x01\x23", 10) 38 | self.assertEqual(v.byte, b"\x01\x23") 39 | self.assertEqual(v.size, 10) 40 | 41 | def test_eq(self): 42 | v1 = Value(b"\x01\x23", 10) 43 | v2 = Value(b"\x01\x23", 10) 44 | v3 = Value(b"\x02", 2) 45 | self.assertEqual(v1, v2) 46 | self.assertNotEqual(v2, v3) 47 | 48 | def test_add_aligned(self): 49 | chars1 = b"This is a test " 50 | chars2 = b"of adding adding aligned" 51 | chars3 = b" values." 52 | v1 = Value(chars1, len(chars1) * 8) 53 | v2 = Value(chars2, len(chars2) * 8) 54 | v3 = Value(chars3, len(chars3) * 8) 55 | v4 = v1 + v2 + v3 56 | self.assertEqual(v4.byte, chars1 + chars2 + chars3) 57 | self.assertEqual(v4.size // 8, len(chars1) + len(chars2) + len(chars3)) 58 | 59 | def test_add_unaligned(self): 60 | v1 = Value(b"\x01\x23", 10) 61 | v2 = Value(b"\x02", 2) 62 | v3 = v1 + v2 63 | self.assertEqual(v3.byte, b"\x04\x8e") 64 | self.assertEqual(v3.size, 12) 65 | v3 = v2 + v1 66 | self.assertEqual(v3.byte, b"\x09\x23") 67 | self.assertEqual(v3.size, 12) 68 | 69 | v1 = Value(b'\x00\x00', 10) 70 | v2 = Value(b'\x00\x1f\xf0\xff', 30) 71 | v3 = v1 + v2 72 | self.assertEqual(v3.byte, b'\x00\x00\x1f\xf0\xff') 73 | self.assertEqual(v3.size, 40) 74 | 75 | def test_add_empty(self): 76 | v1 = Value(b"", 0) 77 | v2 = Value(b"\x01\x23", 10) 78 | v3 = v1 + v2 79 | self.assertEqual(v3.byte, b"\x01\x23") 80 | self.assertEqual(v3.size, 10) 81 | v3 = v2 + v1 82 | self.assertEqual(v3.byte, b"\x01\x23") 83 | self.assertEqual(v3.size, 10) 84 | 85 | 86 | if __name__ == '__main__': 87 | main() 88 | -------------------------------------------------------------------------------- /dizzy/tools.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # tools.py 4 | # 5 | # Copyright 2013 Daniel Mende 6 | # 7 | 8 | # Redistribution and use in source and binary forms, with or without 9 | # modification, are permitted provided that the following conditions are 10 | # met: 11 | # 12 | # * Redistributions of source code must retain the above copyright 13 | # notice, this list of conditions and the following disclaimer. 14 | # * Redistributions in binary form must reproduce the above 15 | # copyright notice, this list of conditions and the following disclaimer 16 | # in the documentation and/or other materials provided with the 17 | # distribution. 18 | # * Neither the name of the nor the names of its 19 | # contributors may be used to endorse or promote products derived from 20 | # this software without specific prior written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | from . import DizzyRuntimeException 34 | from dizzy.log import print_dizzy, DEBUG 35 | from struct import unpack, pack 36 | import ctypes 37 | import os 38 | 39 | endian_format = {"<": "little", 40 | ">": "big", 41 | "!": "big"} 42 | 43 | def check_root(text): 44 | if hasattr(os, 'geteuid'): 45 | if os.geteuid() != 0: 46 | print_dizzy("You must be root to " + text + ".") 47 | exit(1) 48 | else: 49 | if ctypes.windll.shell32.IsUserAnAdmin() != 1: 50 | print_dizzy("You must be Admin to " + text + ".") 51 | exit(1) 52 | 53 | 54 | def unique(seq, idfun=None): 55 | # order preserving 56 | if idfun is None: 57 | def idfun(x): return x 58 | seen = {} 59 | result = [] 60 | for item in seq: 61 | marker = idfun(item) 62 | if marker in seen: continue 63 | seen[marker] = 1 64 | result.append(item) 65 | return result 66 | 67 | 68 | def read_with_length(data, length, endian='!'): 69 | try: 70 | out = None 71 | if length <= 8: 72 | (out,) = unpack("%sB" % endian, data[0]) 73 | elif length <= 16: 74 | (out,) = unpack("%sH" % endian, data[0:1]) 75 | elif length <= 32: 76 | (out,) = unpack("%sI" % endian, data[0:3]) 77 | elif length <= 64: 78 | (out,) = unpack("%sQ" % endian, data[0:7]) 79 | else: 80 | raise DizzyRuntimeException("cant read with len >64") 81 | except Exception as e: 82 | print_dizzy("Can't unpack %s: %s" % (data, str(e)), DEBUG) 83 | raise e 84 | 85 | 86 | def pack_with_length(value, size, endian='!'): 87 | return (value & ((1 << size) - 1)).to_bytes((size + 7) // 8, endian_format[endian]) 88 | 89 | 90 | def chr_to_bin(c): 91 | out = "" 92 | for i in range(0, 8): 93 | if i == 4: 94 | out += " " 95 | out += "%d" % (((ord(c) << i) & 0x80) >> 7) 96 | return out 97 | 98 | 99 | def str_to_bin(s): 100 | out = "" 101 | c = 1 102 | for i in s: 103 | out += chr_to_bin(i) 104 | if c % 8 == 0: 105 | out += "\n" 106 | else: 107 | out += " " 108 | c += 1 109 | return out[:-2] 110 | 111 | 112 | def shift_left(inp, by, out=None): 113 | by = by % 8 114 | if inp == b"": 115 | return out 116 | l = len(inp) 117 | if isinstance(out, bytes) and len(out) == 1: 118 | out = bytes([out[0] | inp[0] >> 8 - by]) 119 | else: 120 | out = b"" 121 | for i in range(0, l): 122 | if i == l - 1: 123 | out += bytes([(inp[i] << by) & 0xff]) 124 | else: 125 | out += bytes([((inp[i] << by) & 0xff) | (inp[i + 1] >> 8 - by)]) 126 | return out 127 | 128 | 129 | def shift_right(inp, by, out=None): 130 | by = by % 8 131 | l = len(inp) 132 | if isinstance(out, bytes) and len(out) == 1: 133 | if inp == b"": 134 | return out 135 | out = bytes([out[0] | ((inp[-1] << 8 - by) & 0xff)]) 136 | else: 137 | out = b"" 138 | for i in range(1, l + 1): 139 | if i == l: 140 | out = bytes([inp[-i] >> by]) + out 141 | else: 142 | out = bytes([inp[-i] >> by | ((inp[-i - 1] << 8 - by) & 0xff)]) + out 143 | return out 144 | 145 | 146 | def csum_inet(data, csum=0): 147 | for i in range(0, len(data), 2): 148 | if i + 1 >= len(data): 149 | csum += data[i] & 0xFF 150 | else: 151 | w = ((data[i] << 8) & 0xFF00) + (data[i + 1] & 0xFF) 152 | csum += w 153 | while (csum >> 16) > 0: 154 | csum = (csum & 0xFFFF) + (csum >> 16) 155 | csum = ~csum 156 | return pack("!H", csum & 0xFFFF) 157 | 158 | # ~ def align_mod(inp, blen, mod, out=b"\x00"): 159 | # ~ out[0] |= (inp[0] << 8 - mod) & 0xff 160 | # ~ for j in range(1, blen]): 161 | # ~ tmp = out[-1] | (inp[j] >> mod) 162 | # ~ out = out[:-1] + bytes([tmp]) 163 | # ~ tmp2 = (inp[j] << 8 - mod) & 0xff 164 | # ~ out += bytes([tmp2]) 165 | # ~ return out 166 | -------------------------------------------------------------------------------- /dizzy/value.py: -------------------------------------------------------------------------------- 1 | # value.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from copy import copy 33 | 34 | def format_bytes(array, size): 35 | size = (size + 7) // 8 36 | length = len(array) 37 | if length > size: 38 | array = array[(length - size):] 39 | elif length < size: 40 | array = b'\x00' * (size - length) + array 41 | return array 42 | 43 | 44 | class Value(object): 45 | def __init__(self, byte=b'', size=None): 46 | if isinstance(byte, str): 47 | from dizzy.config import CONFIG 48 | byte = byte.encode(CONFIG["GLOBALS"]["CODEC"]) 49 | if isinstance(byte, bytes): 50 | if size is None: 51 | self.byte = byte 52 | self.size = len(self.byte) * 8 53 | elif isinstance(size, int): 54 | self.byte = format_bytes(byte, size) 55 | self.size = size 56 | else: 57 | raise TypeError() 58 | else: 59 | raise TypeError() 60 | 61 | def __add__(self, other): 62 | result = Value() 63 | 64 | if self.size == 0: 65 | result.byte = format_bytes(copy(other.byte), other.size) 66 | result.size = other.size 67 | elif other.size == 0: 68 | result.byte = format_bytes(copy(self.byte), self.size) 69 | result.size = self.size 70 | else: 71 | result.byte = bytearray(format_bytes(other.byte, other.size)) 72 | mod = other.size % 8 73 | 74 | if mod == 0: 75 | result.byte = self.byte + result.byte 76 | else: 77 | for x in reversed(self.byte): 78 | result.byte[0] |= (x << mod) & 0xff 79 | result.byte.insert(0, x >> (8 - mod)) 80 | 81 | result.size = self.size + other.size 82 | result.byte = bytes(format_bytes(result.byte, result.size)) 83 | 84 | return result 85 | 86 | def __bytes__(self): 87 | return self.byte 88 | 89 | def __repr__(self): 90 | return "Value(%s, %d)" % (self.byte, self.size) 91 | 92 | def __len__(self): 93 | return self.size 94 | 95 | def __eq__(self, other): 96 | if not isinstance(other, Value): 97 | return False 98 | return self.byte == other.byte and self.size == other.size 99 | -------------------------------------------------------------------------------- /dizzy_cmd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # dizzy_cmd.py 4 | # 5 | # Copyright 2017 Daniel Mende 6 | # 7 | 8 | # Redistribution and use in source and binary forms, with or without 9 | # modification, are permitted provided that the following conditions are 10 | # met: 11 | # 12 | # * Redistributions of source code must retain the above copyright 13 | # notice, this list of conditions and the following disclaimer. 14 | # * Redistributions in binary form must reproduce the above 15 | # copyright notice, this list of conditions and the following disclaimer 16 | # in the documentation and/or other materials provided with the 17 | # distribution. 18 | # * Neither the name of the nor the names of its 19 | # contributors may be used to endorse or promote products derived from 20 | # this software without specific prior written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | from argparse import ArgumentParser 34 | from os.path import basename 35 | from sys import version_info, exit, argv 36 | from dizzy import config #Needs to be load before anything using external dependencies 37 | from dizzy.job import Job 38 | from dizzy import profile 39 | from dizzy.log import print_dizzy, REDUCE 40 | 41 | if version_info.major < 3: 42 | print("This script is intended for use with python >= 3!") 43 | exit(1) 44 | 45 | def main(do_profile=False): 46 | print_dizzy("%s version %s running on %s" % (basename(argv[0]), config.CONFIG["GLOBALS"]["VERSION"], config.CONFIG["GLOBALS"]["PLATFORM"]), REDUCE) 47 | 48 | parser = ArgumentParser() 49 | parser.add_argument(dest='file', metavar='jobfile', nargs='?', default="") 50 | parser.add_argument("-s", help="Start at the given step", type=int, dest="start_at", default=0) 51 | parser.add_argument("-d", help="Output status every SEC seconds", type=float, dest="status_delay", metavar="SEC", 52 | default=60) 53 | parser.add_argument("-l", help="List all loaded modules and their contents.", dest="print_config", action='store_true') 54 | parser.add_argument("-o", help="Overwrite a config option", type=str, dest="options", action="append", default=[]) 55 | args = parser.parse_args() 56 | 57 | if do_profile: 58 | profile.profile_on() 59 | 60 | if args.print_config: 61 | config.print_config() 62 | exit(0) 63 | 64 | options = {} 65 | for i in args.options: 66 | if not "=" in i: 67 | parser.error("Argument to -o needs to be in form 'option.name=value'.") 68 | splited = i.split("=") 69 | opt_name = splited[0] 70 | value = splited[1] 71 | if not "." in opt_name: 72 | parser.error("Argument to -o needs to be in form 'option.name=value'.") 73 | splited = opt_name.split(".") 74 | opt = splited[0] 75 | name = splited[1] 76 | if not opt in options: 77 | options[opt] = { name : value } 78 | else: 79 | options[opt][name] = value 80 | 81 | if args.file == "": 82 | parser.error("Argument 'jobfile' is required.") 83 | 84 | j = Job(args.file, args.start_at, options) 85 | j.start() 86 | try: 87 | while j.is_alive(): 88 | j.join(args.status_delay) 89 | if j.is_alive(): 90 | j.print_status() 91 | except KeyboardInterrupt: 92 | if j.is_alive(): 93 | j.print_status() 94 | exit(1) 95 | 96 | if do_profile: 97 | profile.profile_off() 98 | import pprint 99 | pprint.pprint(profile.get_profile_stats()) 100 | 101 | if __name__ == '__main__': 102 | main(False) 103 | -------------------------------------------------------------------------------- /modules_src/Makefile: -------------------------------------------------------------------------------- 1 | # If the first argument is "newmod"... 2 | ifeq (newmod,$(firstword $(MAKECMDGOALS))) 3 | # use the rest as arguments for "newmod" 4 | RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) 5 | # ...and turn them into do-nothing targets 6 | $(eval $(RUN_ARGS):;@:) 7 | endif 8 | 9 | SHELL = /bin/bash 10 | 11 | .PHONY: modules clean newmod 12 | 13 | modules: 14 | if [ ! -d ../modules ]; then \ 15 | mkdir ../modules; \ 16 | fi 17 | @for a in $$(ls); do \ 18 | if [ -d $$a ]; then \ 19 | echo "Building module $$a"; \ 20 | $(MAKE) -f ../Makefile_Module -C $$a; \ 21 | fi; \ 22 | done; 23 | @echo "Done" 24 | 25 | clean: 26 | @for a in $$(ls); do \ 27 | if [ -d $$a ]; then \ 28 | echo "Cleaning module $$a"; \ 29 | $(MAKE) -f ../Makefile_Module -C $$a clean; \ 30 | fi; \ 31 | done; 32 | @echo "Done" 33 | 34 | newmod: 35 | if [ -e $(RUN_ARGS) ]; then \ 36 | echo "Module $(RUN_ARGS) exists"; \ 37 | else \ 38 | mkdir $(RUN_ARGS); mkdir $(RUN_ARGS)/$(RUN_ARGS); \ 39 | echo -e "name = \"$(RUN_ARGS)\"\ndependencies = []\nversion = \"0.1\"" > $(RUN_ARGS)/config.py; \ 40 | echo "__all__ = [ \"config\", \"$(RUN_ARGS)\" ]" > $(RUN_ARGS)/__init__.py; \ 41 | echo "Module $(RUN_ARGS) created"; \ 42 | fi \ 43 | 44 | all: clean modules 45 | -------------------------------------------------------------------------------- /modules_src/Makefile_Module: -------------------------------------------------------------------------------- 1 | NAME = $(shell basename $(CURDIR)) 2 | OBJ = config.py __init__.py $(NAME) 3 | 4 | FOLD = $(shell ls -G --format=commas $(NAME)/) 5 | FOLDA = $(shell echo $(FOLD) | sed 's/, /\\", \\"/g') 6 | DIZZ = $(shell if [ -d $(NAME)/dizz/ ]; then ls -G --format=commas $(NAME)/dizz/; fi) 7 | DIZZA = $(shell if [ -d $(NAME)/dizz/ ]; then echo $(DIZZ) | sed 's/, /\\", \\"/g'; fi) 8 | ACT = $(shell if [ -d $(NAME)/act/ ]; then ls -G --format=commas $(NAME)/act/; fi) 9 | ACTA = $(shell if [ -d $(NAME)/act/ ]; then echo $(ACT) | sed 's/, /\\", \\"/g'; fi) 10 | JOB = $(shell if [ -d $(NAME)/job/ ]; then ls -G --format=commas $(NAME)/job/; fi) 11 | JOBA = $(shell if [ -d $(NAME)/job/ ]; then echo $(JOB) | sed 's/, /\\", \\"/g'; fi) 12 | PROBE = $(shell if [ -d $(NAME)/probe/ ]; then ls -G --format=commas $(NAME)/probe/; fi) 13 | PROBEA = $(shell if [ -d $(NAME)/probe/ ]; then echo $(PROBE) | sed 's/, /\\", \\"/g' | sed 's/.py//g'; fi) 14 | SESS = $(shell if [ -d $(NAME)/session/ ]; then ls -G --format=commas $(NAME)/session/; fi) 15 | SESSA = $(shell if [ -d $(NAME)/session/ ]; then echo $(SESS) | sed 's/, /\\", \\"/g' | sed 's/.py//g'; fi) 16 | 17 | .PHONY: module clean 18 | module: clean 19 | echo "__all__ = [ \"$(FOLDA)\" ]" > $(NAME)/__init__.py 20 | if [ -d $(NAME)/dizz ]; then echo "__all__ = [ \"$(DIZZA)\" ]" > $(NAME)/dizz/__init__.py; fi 21 | if [ -d $(NAME)/act ]; then echo "__all__ = [ \"$(ACTA)\" ]" > $(NAME)/act/__init__.py; fi 22 | if [ -d $(NAME)/job ]; then echo "__all__ = [ \"$(JOBA)\" ]" > $(NAME)/job/__init__.py; fi 23 | if [ -d $(NAME)/probe ]; then echo "__all__ = [ \"$(PROBEA)\" ]" > $(NAME)/probe/__init__.py; fi 24 | if [ -d $(NAME)/session ]; then echo "__all__ = [ \"$(SESSA)\" ]" > $(NAME)/session/__init__.py; fi 25 | zip -r $(NAME) $(OBJ) 26 | mv $(NAME).zip ../../modules 27 | 28 | clean: 29 | rm -f $(NAME)/__init__.py 30 | if [ -d $(NAME)/dizz ]; then rm -f $(NAME)/dizz/__init__.py; fi 31 | if [ -d $(NAME)/act ]; then rm -f $(NAME)/act/__init__.py; fi 32 | if [ -d $(NAME)/job ]; then rm -f $(NAME)/job/__init__.py; fi 33 | if [ -d $(NAME)/probe ]; then rm -f $(NAME)/probe/__init__.py; fi 34 | if [ -d $(NAME)/session ]; then rm -f $(NAME)/session/__init__.py; fi 35 | 36 | all: clean module 37 | -------------------------------------------------------------------------------- /modules_src/demo/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ "config", "demo" ] 2 | -------------------------------------------------------------------------------- /modules_src/demo/config.py: -------------------------------------------------------------------------------- 1 | name = "demo" 2 | dependencies = [] 3 | version = "0.1" 4 | -------------------------------------------------------------------------------- /modules_src/demo/demo/act/demo.act: -------------------------------------------------------------------------------- 1 | name = "demo" 2 | 3 | def demo_func(interaction_iterator, dizzy_iterator, response): 4 | from pprint import pprint 5 | pprint(interaction_iterator) 6 | pprint(dizzy_iterator) 7 | pprint(response) 8 | 9 | objects = [ 10 | # A dizz object 11 | Dizzy("demo", "demo/dizz/demo.dizz", 1, 'std'), 12 | # A NullDizz object, doesn't send but reads. 13 | NullDizzy("read"), 14 | ] 15 | 16 | functions = { 17 | 1: [demo_func], 18 | } -------------------------------------------------------------------------------- /modules_src/demo/demo/dizz/demo.dizz: -------------------------------------------------------------------------------- 1 | name = "demo" 2 | format = 2 3 | 4 | objects = [ 5 | #Some Fields 6 | Field("field1", b"\x00", fuzz="std"), 7 | Field("field2", 3 * b"\x00", fuzz="std"), 8 | 9 | #a list 10 | List("list_field", config_value("a_global_option")), 11 | 12 | #a length field 13 | Field("len_field", b"\x00\x00", fuzz="std"), 14 | 15 | #some more fields 16 | Field("field3", 24, 2*8, fuzz="std", endian="<"), 17 | Field("field4", 0, 2*8, fuzz="std", endian="<"), 18 | ] 19 | 20 | functions = [ 21 | length_bytes("len_field", "field1", "field4"), 22 | ] 23 | -------------------------------------------------------------------------------- /modules_src/demo/demo/job/demo.conf: -------------------------------------------------------------------------------- 1 | [job] 2 | file = demo/act/demo.act 3 | mode = std 4 | delay = 0 5 | verbose = 4 6 | 7 | [output] 8 | type = session.stdout-hex 9 | timeout = 10 10 | 11 | [values] 12 | a_global_option = can be used in dizz and act files 13 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | python-tag = py3 3 | 4 | [egg_info] 5 | tag_build = 6 | tag_date = 0 7 | 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # setup.py 2 | # 3 | # Copyright 2017 Daniel Mende 4 | # 5 | 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following disclaimer 14 | # in the documentation and/or other materials provided with the 15 | # distribution. 16 | # * Neither the name of the nor the names of its 17 | # contributors may be used to endorse or promote products derived from 18 | # this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | from setuptools import setup 32 | from dizzy.config import CONFIG 33 | 34 | setup(name='dizzy', 35 | version=CONFIG["GLOBALS"]["VERSION"], 36 | description='Dizzy Fuzzing Library', 37 | author='Daniel Mende', 38 | author_email='mail@c0decafe.de', 39 | url='https://c0decafe.de', 40 | license='BSD', 41 | classifiers=[ 'Development Status :: 4 - Beta', 42 | 'Environment :: Console', 43 | 'License :: OSI Approved :: BSD License', 44 | 'Natural Language :: English ', 45 | 'Operating System :: POSIX', 46 | 'Operating System :: Microsoft :: Windows', 47 | 'Programming Language :: Python :: 3 :: Only', 48 | 'Topic :: Security', 49 | 'Topic :: Software Development :: Testing'], 50 | packages=['dizzy', 'dizzy.encodings', 'dizzy.functions', 'dizzy.objects', 'dizzy.probe', 'dizzy.session'], 51 | scripts=['dizzy_cmd'], 52 | data_files=[('share/dizzy/', ['lib/std_string_lib.txt'])], 53 | python_requires='>=3', 54 | install_requires=[ 55 | 'exrex', 56 | 'pcapy', 57 | # 'Crypto' 58 | ], 59 | test_suite='nose.collector', 60 | tests_require=['nose'], 61 | ) 62 | -------------------------------------------------------------------------------- /wireshark/dizzy.lua: -------------------------------------------------------------------------------- 1 | -- This example iterates through the field tree of the packets, and prints out the tree field information in a text window. 2 | -- It shows the current tree for the selected packet, but this does not mean it always shows the full tree, 3 | -- because wireshark performs multiple dissection passes of a packet, with the initial pass only being high-level and not 4 | -- dissecting fully (for performance reasons). You can see this behavior better by changing line 30 of this example 5 | -- to this, so it concatenates output instead of clearing it every time: 6 | -- output = output .. "\nTree fields for packet #".. pinfo.number .. ":\n" 7 | 8 | -- this only works in wireshark 9 | if not gui_enabled() then return end 10 | 11 | function string.starts(String,Start) 12 | return string.sub(String,1,string.len(Start)) == Start 13 | end 14 | 15 | function string.ends(String,End) 16 | return End == '' or string.sub(String,-string.len(End)) == End 17 | end 18 | 19 | local function getAllData(t, prevData) 20 | -- if prevData == nil, start empty, otherwise start with prevData 21 | local data = prevData or {} 22 | 23 | -- copy all the attributes from t 24 | for k,v in pairs(t) do 25 | data[k] = data[k] or v 26 | end 27 | 28 | -- get t's metatable, or exit if not existing 29 | local mt = getmetatable(t) 30 | if type(mt)~='table' then return data end 31 | 32 | -- get the __index from mt, or exit if not table 33 | local index = mt.__index 34 | if type(index)~='table' then return data end 35 | 36 | -- include the data from index into data, recursively, and return 37 | return getAllData(index, data) 38 | end 39 | 40 | 41 | local ascii = {["20"]=" ", ["21"]="!", ["22"]="\"", ["23"]="#", ["24"]="$", ["25"]="%", 42 | ["26"]="&", ["27"]="'", ["28"]="(", ["29"]=")", ["2A"]="*", ["2B"]="+", 43 | ["2C"]=",", ["2D"]="-", ["2E"]=".", ["2F"]="/", ["30"]="0", ["31"]="1", 44 | ["32"]="2", ["33"]="3", ["34"]="4", ["35"]="5", ["36"]="6", ["37"]="7", 45 | ["38"]="8", ["39"]="9", ["3A"]=":", ["3B"]=";", ["3C"]="<", ["3D"]="=", 46 | ["3E"]=">", ["3F"]="?", ["40"]="@", ["41"]="A", ["42"]="B", ["43"]="C", 47 | ["44"]="D", ["45"]="E", ["46"]="F", ["47"]="G", ["48"]="H", ["49"]="I", 48 | ["4A"]="J", ["4B"]="K", ["4C"]="L", ["4D"]="M", ["4E"]="N", ["4F"]="O", 49 | ["50"]="P", ["51"]="Q", ["52"]="R", ["53"]="S", ["54"]="T", ["55"]="U", 50 | ["56"]="V", ["57"]="W", ["58"]="X", ["59"]="Y", ["5A"]="Z", ["5B"]="[", 51 | ["5C"]="\\", ["5D"]="]", ["5E"]="^", ["5F"]="_", ["60"]="`", ["61"]="a", 52 | ["62"]="b", ["63"]="c", ["64"]="d", ["65"]="e", ["66"]="f", ["67"]="g", 53 | ["68"]="h", ["69"]="i", ["6A"]="j", ["6B"]="k", ["6C"]="l", ["6D"]="m", 54 | ["6E"]="n", ["6F"]="o", ["70"]="p", ["71"]="q", ["72"]="r", ["73"]="s", 55 | ["74"]="t", ["75"]="u", ["76"]="v", ["77"]="w", ["78"]="x", ["79"]="y", 56 | ["7A"]="z", ["7B"]="{", ["7C"]="|", ["7D"]="}"} 57 | 58 | local function to_python_string(bytes) 59 | local out = "" 60 | for hex in string.gmatch(tostring(bytes), "[0-9A-F][0-9A-F]") do 61 | local chr = ascii[hex] 62 | if chr == nil then 63 | out = out .. "\\x" .. hex 64 | else 65 | out = out .. chr 66 | end 67 | end 68 | return out 69 | end 70 | 71 | 72 | local function to_python_hex(bytes) 73 | local out = "" 74 | for hex in string.gmatch(tostring(bytes), "[0-9A-F][0-9A-F]") do 75 | out = out .. "\\x" .. hex 76 | end 77 | return out 78 | end 79 | 80 | local function create_listener_function(frame, directory) 81 | local function packet(pinfo, tvb, tabinfo) 82 | local header 83 | local fields = { all_field_infos() } 84 | local packet = {} 85 | local prev_name = "." 86 | local prev_end = -1 87 | 88 | for index, finfo in pairs(fields) do 89 | if header ~= nil then 90 | if finfo.len == 0 or finfo.hidden or finfo.generated or type(finfo.value) == "boolean" then 91 | goto for_end 92 | end 93 | if string.starts(finfo.name, prev_name .. ".") or finfo.offset < prev_end then 94 | table.remove(packet, #packet) 95 | end 96 | table.insert(packet, finfo) 97 | prev_name = finfo.name 98 | prev_end = finfo.offset + finfo.len 99 | elseif frame == finfo.name then 100 | header = finfo 101 | end 102 | ::for_end:: 103 | end 104 | 105 | if header == nil then 106 | return 107 | end 108 | 109 | prev_end = header.offset 110 | source = header.source 111 | dizzy = {} 112 | i = 0 113 | for index, finfo in pairs(packet) do 114 | if prev_end ~= finfo.offset then 115 | table.insert(dizzy, {name="Unknown_" .. i, 116 | value=to_python_hex(source:bytes(prev_end, finfo.offset - prev_end))}) 117 | i = i + 1 118 | end 119 | 120 | local val = source:bytes(finfo.offset, finfo.len) 121 | if finfo.type == ftypes.STRING or finfo.type == ftypes.STRINGZ then 122 | val = to_python_string(source:bytes(finfo.offset, finfo.len)) 123 | else 124 | val = to_python_hex(source:bytes(finfo.offset, finfo.len)) 125 | end 126 | table.insert(dizzy, {name=finfo.name .. "_" .. i, value=val}) 127 | i = i + 1 128 | prev_end = finfo.offset + finfo.len 129 | end 130 | 131 | local file = io.open(directory .. pinfo.number .. ".dizz", "w") 132 | file:write("objects = [\n") 133 | for index, element in pairs(dizzy) do 134 | file:write(" Field(\"" .. element.name .. "\", b\"" .. element.value .. "\"),\n") 135 | end 136 | --output = output .. " ]\n\n" 137 | file:write(" ]\n\n") 138 | file:write("functions = []\n") 139 | file:close() 140 | end 141 | return packet 142 | end 143 | 144 | local function export_to_dizzy(filter, frame, directory) 145 | if string.ends(directory, "/") == false then 146 | directory = directory .. "/" 147 | end 148 | local tap = Listener.new(frame, filter, true) 149 | tap.packet = create_listener_function(frame, directory) 150 | end 151 | 152 | local function dialog() 153 | new_dialog("Set Dizzy Listener", export_to_dizzy, "Filter", "Frame", "Directory") 154 | end 155 | 156 | -- add this to the Tools->Lua submenu 157 | register_menu("Set Dizzy Listener", dialog, MENU_TOOLS_UNSORTED) 158 | --------------------------------------------------------------------------------