├── .gitignore ├── LICENSE ├── README.md ├── base64_extract_hidden_bits.py ├── change_jpeg_dimensions.py ├── keystrokes_from_pcap.py ├── mouse_move_from_pcap.py ├── png_fix.py └── wav_parse.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Sam Leonard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ctf-scripts 2 | A collection of scripts I've written to solve recurring challenges in CTFs. 3 | 4 | If you find any of these useful, please consider giving the repo a star :pleading_face: 5 | 6 | ## Scripts 7 | 8 | - `change_jpeg_dimensions.py`: 9 | This script changes the dimensions on a JPEG image by editing the Start of Frame segment directly, leaving the image data untouched. 10 | 11 | - `keystrokes_from_pcap.py`: 12 | This script attempts to parse the keystrokes out of a USB pcap file. 13 | 14 | - `png_fix.py`: 15 | This script implements a number of techniques for fixing broken / corrupted PNG files. 16 | 17 | - `wav_parse.py`: 18 | Really not too sure ngl to you, it does parse WAV files though. 19 | -------------------------------------------------------------------------------- /base64_extract_hidden_bits.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import string 4 | 5 | base64 = string.ascii_uppercase + string.ascii_lowercase + string.digits + "+/" 6 | 7 | 8 | def decode_base64(s: str) -> tuple[bytes, str]: 9 | out = [] 10 | curr_char = 0 11 | bitpos = 0 12 | for c in s: 13 | if c == "=": 14 | bits = 0 15 | else: 16 | bits = base64.index(c) 17 | 18 | # print( 19 | # f"{bytes(out).decode(errors="ignore"):<10}, {c = }, curr_char = 0b{curr_char:08b}, {bitpos = }, bits = 0b{bits:08b}" 20 | # ) 21 | 22 | match bitpos: 23 | case 0: 24 | curr_char = bits << 2 25 | bitpos = 6 26 | case 6: 27 | curr_char |= bits >> 4 28 | out.append(curr_char) 29 | curr_char = (bits & 0b1111) << 4 30 | bitpos = 4 31 | case 4: 32 | curr_char |= bits >> 2 33 | out.append(curr_char) 34 | curr_char = (bits & 0b11) << 6 35 | bitpos = 2 36 | case 2: 37 | curr_char |= bits 38 | out.append(curr_char) 39 | bitpos = 0 40 | 41 | # print( 42 | # f"{bytes(out).decode(errors="ignore"):<10}, {c = }, curr_char = 0b{curr_char:08b}, {bitpos = }, bits = 0b{bits:08b}" 43 | # ) 44 | 45 | if s.endswith("=="): 46 | last_byte = out.pop() 47 | last_byte = out.pop() 48 | return bytes(out), f"{last_byte >> 4:04b}" 49 | if s.endswith("="): 50 | last_byte = out.pop() 51 | return bytes(out), f"{last_byte >> 6:02b}" 52 | else: 53 | return bytes(out), "" 54 | 55 | 56 | with open("strings") as f: 57 | lines = f.read().splitlines() 58 | 59 | all_hidden = "" 60 | for line in lines: 61 | data, hidden = decode_base64(line) 62 | all_hidden += hidden 63 | 64 | out = [int(all_hidden[i : i + 8], 2) for i in range(0, len(all_hidden), 8)] 65 | print(bytes(out)) 66 | -------------------------------------------------------------------------------- /change_jpeg_dimensions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import struct 4 | import argparse 5 | 6 | 7 | def main(): 8 | parser = argparse.ArgumentParser() 9 | 10 | parser.add_argument("image", help="The image to change the dimensions of.") 11 | parser.add_argument("changed_image", help="The file name to save the new image to.") 12 | parser.add_argument("--width", required=True, help="The new width of the image.", type=int) 13 | parser.add_argument("--height", required=True, help="The new height of the image.", type=int) 14 | 15 | args = parser.parse_args() 16 | 17 | with open(args.image, "rb") as f: 18 | imdata = f.read() 19 | 20 | new_shape = struct.pack(">HH", args.height, args.width) 21 | 22 | # start of frame marker (yes there are others, I'm lazy) 23 | if (sof := imdata.find(bytes([0xFF, 0xC0]))) is not None: 24 | imdata = imdata[:sof+5] + new_shape + imdata[sof+9:] 25 | 26 | with open(args.changed_image, "wb") as f: 27 | f.write(imdata) 28 | else: 29 | print("Couldn't find Start of frame marker.") 30 | 31 | 32 | if __name__ == "__main__": 33 | main() 34 | -------------------------------------------------------------------------------- /keystrokes_from_pcap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import numpy as np 3 | import pandas as pd 4 | import struct 5 | import sys 6 | from collections import defaultdict 7 | 8 | def warn(msg): 9 | yellow = "\x1b[0;33m" 10 | white = "\x1b[0;37m" 11 | print(f"{yellow}[warn] {msg}{white}", file=sys.stderr) 12 | 13 | CAPSLOCK_SCANCODE = 0x39 14 | CAPSLOCK_SENTINEL = "[CAPSLOCK]" 15 | 16 | def main(): 17 | if len(sys.argv) != 2: 18 | print(f"Usage: {sys.argv[0]} ") 19 | sys.exit(0) 20 | 21 | df = pd.read_csv(sys.argv[1], index_col=0) 22 | 23 | capdata = df["HID Data"].dropna().reset_index(drop=True) 24 | 25 | data = "" 26 | prev_pressed = [0] * 6 27 | caps = False 28 | for i, hid_data in enumerate(capdata): 29 | modifiers, _pad, *pressed = bytes.fromhex(hid_data) 30 | # modifier bits: 31 | # [MSB] right gui, right alt, right shift, right ctrl, left gui, left alt, left shift, left ctrl [LSB] 32 | shift = modifiers & 0b0010_0010 != 0 33 | upper = caps ^ shift 34 | for k in pressed: 35 | # keys are left packed so if we see a zero we are at the end 36 | if k == 0: 37 | break 38 | 39 | if k in prev_pressed: 40 | continue 41 | 42 | if (key := keymap.get((upper, k))) is None: 43 | warn(f"unrecognised scancode: k=0x{k:02x} @ {i}") 44 | continue 45 | 46 | if key == CAPSLOCK_SENTINEL: 47 | caps = not caps 48 | continue 49 | 50 | data += key 51 | prev_pressed = pressed 52 | 53 | print(data) 54 | 55 | # add any missing ones based on wireshark's dissection 56 | keymap = { 57 | (False, 0x04): "a", (True, 0x04): "A", 58 | (False, 0x05): "b", (True, 0x05): "B", 59 | (False, 0x06): "c", (True, 0x06): "C", 60 | (False, 0x07): "d", (True, 0x07): "D", 61 | (False, 0x08): "e", (True, 0x08): "E", 62 | (False, 0x09): "f", (True, 0x09): "F", 63 | (False, 0x0A): "g", (True, 0x0A): "G", 64 | (False, 0x0B): "h", (True, 0x0B): "H", 65 | (False, 0x0C): "i", (True, 0x0C): "I", 66 | (False, 0x0D): "j", (True, 0x0D): "J", 67 | (False, 0x0E): "k", (True, 0x0E): "K", 68 | (False, 0x0F): "l", (True, 0x0F): "L", 69 | (False, 0x10): "m", (True, 0x10): "M", 70 | (False, 0x11): "n", (True, 0x11): "N", 71 | (False, 0x12): "o", (True, 0x12): "O", 72 | (False, 0x13): "p", (True, 0x13): "P", 73 | (False, 0x14): "q", (True, 0x14): "Q", 74 | (False, 0x15): "r", (True, 0x15): "R", 75 | (False, 0x16): "s", (True, 0x16): "S", 76 | (False, 0x17): "t", (True, 0x17): "T", 77 | (False, 0x18): "u", (True, 0x18): "U", 78 | (False, 0x19): "v", (True, 0x19): "V", 79 | (False, 0x1A): "w", (True, 0x1A): "W", 80 | (False, 0x1B): "x", (True, 0x1B): "X", 81 | (False, 0x1C): "y", (True, 0x1C): "Y", 82 | (False, 0x1D): "z", (True, 0x1D): "Z", 83 | (False, 0x1E): "1", (True, 0x1E): "!", 84 | (False, 0x1F): "2", (True, 0x1F): '"', 85 | (False, 0x20): "3", (True, 0x20): "£", 86 | (False, 0x21): "4", (True, 0x21): "$", 87 | (False, 0x22): "5", (True, 0x22): "%", 88 | (False, 0x23): "6", (True, 0x23): "^", 89 | (False, 0x24): "7", (True, 0x24): "&", 90 | (False, 0x25): "8", (True, 0x25): "*", 91 | (False, 0x26): "9", (True, 0x26): "(", 92 | (False, 0x27): "0", (True, 0x27): ")", 93 | (False, 0x28): "\n",(True, 0x28): "\n", 94 | (False, 0x2A): "[DEL]",(True, 0x28): "[DEL]", 95 | (False, 0x2C): " ", (True, 0x2C): " ", 96 | (False, 0x2D): "-", (True, 0x2D): "_", 97 | (False, 0x2F): "[", (True, 0x2F): "{", 98 | (False, 0x30): "]", (True, 0x30): "}", 99 | (False, 0x31): "\\",(True, 0x31): "|", 100 | (False, 0x32): "#", (True, 0x32): "~", 101 | (False, 0x33): ";", (True, 0x33): ":", 102 | (False, 0x34): "'", (True, 0x34): "@", 103 | (False, 0x36): ",", (True, 0x36): ",", 104 | (False, 0x37): ".", (True, 0x37): ".", 105 | (False, 0x38): "/", (True, 0x38): "/", 106 | (False, CAPSLOCK_SCANCODE): CAPSLOCK_SENTINEL, (True, CAPSLOCK_SCANCODE): CAPSLOCK_SENTINEL, 107 | } 108 | 109 | 110 | if __name__ == "__main__": 111 | main() 112 | -------------------------------------------------------------------------------- /mouse_move_from_pcap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import pandas as pd 3 | import struct 4 | import sys 5 | from collections import namedtuple 6 | from PIL import Image, ImageDraw 7 | import os 8 | 9 | DIFFERENTIATE_BUTTONS = os.getenv("DIFFERENTIATE_BUTTONS") is not None 10 | DRAW_WIDTH = int(os.getenv("DRAW_WIDTH", "0")) 11 | 12 | MouseHIDData = namedtuple( 13 | "MouseHIDData", 14 | ["x_off", "y_off", "wheel", "pan", "left_click", "right_click", "middle_click"], 15 | ) 16 | 17 | MousePosition = namedtuple( 18 | "MousePosition", 19 | ["x", "y", "left_click", "right_click", "middle_click"], 20 | ) 21 | 22 | 23 | def u12_to_i12(u12: int) -> int: 24 | mask_lower_11 = (1 << 11) - 1 25 | return (u12 >> 11) * -0x800 + (u12 & mask_lower_11) 26 | 27 | 28 | def parse_packed_xy(xy: bytes) -> tuple[int, int]: 29 | xy_int = struct.unpack("> 12 32 | return u12_to_i12(x_u12), u12_to_i12(y_u12) 33 | 34 | 35 | def main(): 36 | if len(sys.argv) != 2: 37 | print(f"Usage: {sys.argv[0]} ") 38 | sys.exit(0) 39 | 40 | df = pd.read_csv(sys.argv[1], index_col=0) 41 | 42 | capdata = df["HID Data"].dropna().reset_index(drop=True) 43 | 44 | hid_data: list[MouseHIDData] = [] 45 | for row in capdata: 46 | report_id, buttons, xy, wheel, pan = struct.unpack( 47 | " bytes: 21 | return struct.pack(">I", self.length) + self.type.encode("UTF-8") + self.data + struct.pack("!I", self.crc) 22 | 23 | def recalc_crc(self): 24 | self.crc = binascii.crc32(self.type.encode() + self.data) 25 | 26 | def log(self, action: str): 27 | if VERB > 0: 28 | print(f"{action} {self.type} chunk") 29 | 30 | if VERB > 1: 31 | print(f"\tlength = {self.length}") 32 | 33 | if VERB > 2: 34 | print(f"\tdata = {self.data}") 35 | 36 | if VERB > 1: 37 | print(f"\tcrc = {self.crc:08X}") 38 | 39 | 40 | @dataclass 41 | class PngMetadata: 42 | width: int 43 | height: int 44 | bit_depth: int 45 | color_type: int 46 | compression_method: int 47 | filter_method: int 48 | interlace_method: int 49 | 50 | def __init__(self, data: bytes): 51 | ( 52 | self.width, 53 | self.height, 54 | self.bit_depth, 55 | self.color_type, 56 | self.compression_method, 57 | self.filter_method, 58 | self.interlace_method, 59 | ) = struct.unpack(">IIBBBBB", data) 60 | 61 | if VERB > 2: 62 | print(f"Parsed IHDR: {self}") 63 | 64 | def pack(self) -> bytes: 65 | return struct.pack( 66 | ">IIBBBBB", 67 | self.width, 68 | self.height, 69 | self.bit_depth, 70 | self.color_type, 71 | self.compression_method, 72 | self.filter_method, 73 | self.interlace_method, 74 | ) 75 | 76 | 77 | def read_header(pngfile: BytesIO) -> bytes: 78 | header = pngfile.read(8) 79 | if VERB > 2: 80 | print(f"Read header: {header}") 81 | return header 82 | 83 | 84 | def write_header(pngfile: BytesIO, header: bytes): 85 | pngfile.write(header) 86 | if VERB > 2: 87 | print(f"Wrote header: {header}") 88 | 89 | 90 | def read_chunk(pngfile: BytesIO) -> Chunk | None: 91 | try: 92 | (length,) = struct.unpack(">I", pngfile.read(4)) 93 | chunk_type = pngfile.read(4).decode() 94 | chunk_data = pngfile.read(length) 95 | (crc,) = struct.unpack("!I", pngfile.read(4)) 96 | except struct.error: 97 | return None 98 | 99 | chunk = Chunk(length, chunk_type, chunk_data, crc) 100 | 101 | chunk.log(action="Read") 102 | return chunk 103 | 104 | 105 | def write_chunk(pngfile: BytesIO, chunk: Chunk): 106 | pngfile.write(chunk.pack()) 107 | chunk.log(action="Wrote") 108 | 109 | 110 | def bruteforce_ihdr_dimensions(chunk: Chunk) -> Chunk: 111 | for i, j in it.product(range(1, 10000), range(1, 10000)): 112 | new_chunk = chunk 113 | new_chunk.data = struct.pack(">II", i, j) + chunk.data[8:] 114 | new_chunk.recalc_crc() 115 | 116 | if VERB > 2: 117 | print(f"trying: width = {i}, height = {j}") 118 | 119 | if new_chunk.crc == chunk.crc: 120 | if VERB > 0: 121 | print(f"found matching CRC, width = {i}, height = {j}") 122 | 123 | return new_chunk 124 | 125 | raise Exception("Failed to find valid size by bruteforce") 126 | 127 | 128 | def randomise_plte(chunk): 129 | new_chunk = Chunk( 130 | length=chunk.length, 131 | type=chunk.type, 132 | data=random.randbytes(chunk.length), 133 | ) 134 | new_chunk.recalc_crc() 135 | 136 | if VERB > 0: 137 | print("Generated random PLTE chunk.") 138 | 139 | if VERB > 2: 140 | print(f"\tdata = {chunk.data}.") 141 | 142 | return new_chunk 143 | 144 | 145 | def parse_file_chunks(input_file): 146 | chunks = [] 147 | while (chunk := read_chunk(input_file)) is not None: 148 | chunks.append(chunk) 149 | 150 | return chunks 151 | 152 | 153 | def parse_png(input_file) -> tuple[bytes, list[Chunk]]: 154 | header = read_header(input_file) 155 | chunks = parse_file_chunks(input_file) 156 | return header, chunks 157 | 158 | 159 | def save_png(output_file: BytesIO, header: bytes, chunks: list[Chunk]): 160 | write_header(output_file, header) 161 | for chunk in chunks: 162 | write_chunk(output_file, chunk) 163 | 164 | 165 | @click.command() 166 | @click.argument("input-file", type=click.File("rb")) 167 | @click.argument("output-file", type=click.File("wb")) 168 | @click.option( 169 | "--fix-ihdr/--no-fix-ihdr", 170 | default=False, 171 | help="Brute force values to find the right dimensions for the image, using the CRC to verify the chunk data.", 172 | ) 173 | @click.option( 174 | "--rand-plte/--no-rand-plte", 175 | default=False, 176 | help="Insert random data into the PLTE chunk if one exists.", 177 | ) 178 | @click.option( 179 | "--fix-crc/--no-fix-crc", 180 | default=False, 181 | help="Re-calculate the CRC for every chunk.", 182 | ) 183 | @click.option("--verbosity", default=0, help="Set the verbosity level.") 184 | def main(input_file, output_file, fix_ihdr, rand_plte, fix_crc, verbosity): 185 | global VERB 186 | VERB = verbosity 187 | 188 | header, chunks = parse_png(input_file) 189 | 190 | if fix_ihdr: 191 | chunks = [(bruteforce_ihdr_dimensions(chunk) if chunk.type == "IHDR" else chunk) for chunk in chunks] 192 | 193 | if rand_plte: 194 | chunks = [randomise_plte(chunk) if chunk.type == "PLTE" else chunk for chunk in chunks] 195 | 196 | if fix_crc: 197 | for chunk in chunks: 198 | chunk.recalc_crc() 199 | 200 | save_png(output_file, header, chunks) 201 | 202 | 203 | if __name__ == "__main__": 204 | main() 205 | -------------------------------------------------------------------------------- /wav_parse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import struct 3 | 4 | 5 | class WavFile: 6 | header_format = "4sI4s4sIHHIIHH4sI" 7 | 8 | def __init__(self, fname): 9 | with open(fname, "rb") as f: 10 | self.__parse(f) 11 | 12 | def __parse(self, fp): 13 | 14 | ( 15 | self.magic, 16 | self.file_size, 17 | self.file_type_header, 18 | self.format_chunk_marker, 19 | self.len_fmt_data, 20 | self.fmt_type, 21 | self.no_chans, 22 | self.samp_rate, 23 | self.sr_bps_cs_8, 24 | self.bps_cs_8, 25 | self.bps, 26 | self.data_header, 27 | self.file_size_data, 28 | ) = struct.unpack( 29 | self.header_format, fp.read(struct.calcsize(self.header_format)) 30 | ) 31 | self.data = fp.read() 32 | 33 | def save(self, fname): 34 | with open(fname, "wb") as f: 35 | f.write( 36 | struct.pack( 37 | self.header_format, 38 | self.magic, 39 | self.file_size, 40 | self.file_type_header, 41 | self.format_chunk_marker, 42 | self.len_fmt_data, 43 | self.fmt_type, 44 | self.no_chans, 45 | self.samp_rate, 46 | self.sr_bps_cs_8, 47 | self.bps_cs_8, 48 | self.bps, 49 | self.data_header, 50 | self.file_size_data, 51 | ) 52 | ) 53 | f.write(self.data) 54 | 55 | def print_header(self): 56 | print( 57 | f""" 58 | WavFile(, 59 | magic: {self.magic}, 60 | file_size: {self.file_size}, 61 | file_type_header: {self.file_type_header}, 62 | format_chunk_marker: {self.format_chunk_marker}, 63 | len_fmt_data: {self.len_fmt_data}, 64 | fmt_type: {self.fmt_type}, 65 | no_chans: {self.no_chans}, 66 | samp_rate: {self.samp_rate}, 67 | sr_bps_cs_8: {self.sr_bps_cs_8}, 68 | bps_cs_8: {self.bps_cs_8}, 69 | bps: {self.bps}, 70 | data_header: {self.data_header}, 71 | file_size_data: {self.file_size_data}, 72 | ) 73 | """ 74 | ) 75 | 76 | 77 | def main(): 78 | br = WavFile("BankRobbing") 79 | valid = WavFile("valid.wav") 80 | 81 | br.print_header() 82 | valid.print_header() 83 | 84 | br.magic = valid.magic 85 | br.file_size = len(br.data) + struct.calcsize(WavFile.header_format) - 8 86 | br.file_type_header = valid.file_type_header 87 | br.format_chunk_marker = valid.format_chunk_marker 88 | br.len_fmt_data = valid.len_fmt_data 89 | br.fmt_type = 1 #valid.fmt_type 90 | br.no_chans = 1 #valid.no_chans 91 | br.samp_rate = valid.samp_rate 92 | br.sr_bps_cs_8 = valid.sr_bps_cs_8 93 | br.bps_cs_8 = valid.bps_cs_8 94 | br.bps = valid.bps 95 | br.data_header = valid.data_header 96 | br.file_size_data = br.file_size - struct.calcsize(WavFile.header_format) 97 | 98 | br.print_header() 99 | valid.print_header() 100 | 101 | br.save("fixed.wav") 102 | 103 | 104 | if __name__ == "__main__": 105 | main() 106 | --------------------------------------------------------------------------------