--- tags: decompiler title: Other decompilers --- # Unpyc37 Total: 5356 Exception raised: 33 (Explicit errors) Attribution error: 37 Total exceptions: 80 debugging: `Traceback (most recent call last):` How to find implicit errors? - Inconsistencies in bytecode functions: 4086 - prune out those that are still equivalent? - Find jump instructions diff Example: :::spoiler Example 1 (15660): ``` def _download_impl(self, url, pathfmt): while True: if tries: if response: response.close() response = None self.log.warning("%s (%s/%s)", msg, tries, self.retries+1) if tries > self.retries: return False time.sleep(tries) tries += 1 file_header = None # collect HTTP headers headers = {"Accept": "*/*"} # file-specific headers extra = kwdict.get("_http_headers") if extra: headers.update(extra) # general headers if self.headers: headers.update(self.headers) # partial content file_size = pathfmt.part_size() if file_size: headers["Range"] = "bytes={}-".format(file_size) # connect to (remote) source try: response = self.session.request( kwdict.get("_http_method", "GET"), url, stream=True, headers=headers, data=kwdict.get("_http_data"), timeout=self.timeout, proxies=self.proxies, verify=self.verify, ) except (ConnectionError, Timeout) as exc: msg = str(exc) continue except Exception as exc: self.log.warning(exc) return False # check response code = response.status_code if code == 200: # OK offset = 0 size = response.headers.get("Content-Length") elif code == 206: # Partial Content offset = file_size size = response.headers["Content-Range"].rpartition("/")[2] elif code == 416 and file_size: # Requested Range Not Satisfiable break else: msg = "'{} {}' for '{}'".format(code, response.reason, url) tmp = code == 429 or 500 <= code < 600 ## LOOK HEREEE!!!!! XXX if tmp: # Server Error continue self.log.warning(msg) return False # check for invalid responses validate = kwdict.get("_http_validate") if validate: result = validate(response) if isinstance(result, str): url = result tries -= 1 continue if not result: self.log.warning("Invalid response") return False # set missing filename extension from MIME type if not pathfmt.extension: pathfmt.set_extension(self._find_extension(response)) if pathfmt.exists(): pathfmt.temppath = "" return True # check file size size = text.parse_int(size, None) if size is not None: if self.minsize and size < self.minsize: self.log.warning( "File size smaller than allowed minimum (%s < %s)", size, self.minsize) return False if self.maxsize and size > self.maxsize: self.log.warning( "File size larger than allowed maximum (%s > %s)", size, self.maxsize) return False content = response.iter_content(self.chunk_size) # check filename extension against file header if adjust_extension and not offset and \ pathfmt.extension in FILE_SIGNATURES: try: file_header = next( content if response.raw.chunked else response.iter_content(16), b"") except (RequestException, SSLError, OpenSSLError) as exc: msg = str(exc) print() continue if self._adjust_extension(pathfmt, file_header) and \ pathfmt.exists(): pathfmt.temppath = "" return True # set open mode if not offset: mode = "w+b" if file_size: self.log.debug("Unable to resume partial download") else: mode = "r+b" self.log.debug("Resuming download at byte %d", offset) # download content self.downloading = True with pathfmt.open(mode) as fp: if file_header: fp.write(file_header) offset += len(file_header) elif offset: if adjust_extension and \ pathfmt.extension in FILE_SIGNATURES: self._adjust_extension(pathfmt, fp.read(16)) fp.seek(offset) self.out.start(pathfmt.path) try: self.receive(fp, content, size, offset) except (RequestException, SSLError, OpenSSLError) as exc: msg = str(exc) print() continue # check file size if size and fp.tell() < size: msg = "file size mismatch ({} < {})".format( fp.tell(), size) print() continue break self.downloading = False if self.mtime: kwdict.setdefault("_mtime", response.headers.get("Last-Modified")) else: kwdict["_mtime"] = None return True ``` ::: # Uncompyle2 Total: 22222 Parse errors (explicit): 36 Implicit errors? # Pycdc ## Segmentation fault Segmentation fault Errors: ### Py27 pattern1: ``` if x: return #z=z return {a: b} ``` pattern2: ``` if cmd == '': return self.default(line) #z=z try: func = getattr(self, 'do_' + cmd) except AttributeError: return self.default(line) ``` pattern3: ``` assert X #z=z for i in x: pass ``` pattern4: ``` if not _winreg: return #z=z def enum_types(mimedb): pass ``` pattern5: ``` if not _winreg: return #z=z for i in x: pass ``` Pattern6: ``` assert unicodedata.unidata_version == '3.2.0' #z=z def in_table_a1(code): pass ``` Pattern7: ``` if not hasattr(os, 'utime'): return #z=z try: os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime)) except EnvironmentError as e: raise ExtractError('could not change modification time') ``` Pattern 8: ``` try: if a: return b #z=z except OSError as msg: raise URLError(msg) ``` Pattern 9: ``` if a: if b: if c: k = k + 1 # z=z temp = temp + 1 ``` ### Py34 Infinitely runs: Pattern 3: ``` assert action_tuples z=z for action, args, option_string in action_tuples: take_action(action, args, option_string) ``` Pattern 10: ``` while True: # change to temp if action is None: extras.append(arg_strings[start_index]) # return start_index + 1 if a: arg_count = match_argument(action, 'A') else: break ``` pattern2: ``` if cmd == '': return self.default(line) #z=z try: func = getattr(self, 'do_' + cmd) except AttributeError: return self.default(line) ``` Pattern6: ``` assert unicodedata.unidata_version == '3.2.0' #z=z def in_table_a1(code): pass ``` ### py35 pattern2: ``` if cmd == '': return self.default(line) #z=z try: func = getattr(self, 'do_' + cmd) except AttributeError: return self.default(line) ``` Pattern6: ``` assert unicodedata.unidata_version == '3.2.0' #z=z def in_table_a1(code): pass ``` Pattern 11: ``` assert x #z=z try: func = getattr(self, 'do_' + cmd) except AttributeError: return self.default(line) ``` pattern4: ``` if not _winreg: return #z=z def enum_types(mimedb): pass ``` Pattern 8: ``` try: if a: return b #z=z except OSError as msg: raise URLError(msg) ``` pattern3: ``` assert X #z=z for i in x: pass ``` Pattern 12: ``` if a: b = b +1 else: return #z=z try: func = getattr(self, 'do_' + cmd) except AttributeError: return self.default(line) ``` Pattern 13: ``` try: if fut is not None: # assign following boolean to a variable assert self._read_fut is fut or not (self._read_fut is None and self._closing) except ConnectionAbortedError as exc: pass ``` ### py36 Pattern 8: ``` try: if a: return b #z=z except OSError as msg: raise URLError(msg) ``` pattern3: ``` assert X #z=z for i in x: pass ``` Pattern 10: ``` while True: # change to temp if action is None: extras.append(arg_strings[start_index]) # return start_index + 1 if a: arg_count = match_argument(action, 'A') else: break ``` Pattern 11: ``` assert x #z=z try: func = getattr(self, 'do_' + cmd) except AttributeError: return self.default(line) ``` pattern2: ``` if cmd == '': return self.default(line) #z=z try: func = getattr(self, 'do_' + cmd) except AttributeError: return self.default(line) ``` Pattern6: ``` assert unicodedata.unidata_version == '3.2.0' #z=z def in_table_a1(code): pass ``` Pattern 13: ``` try: if fut is not None: # assign following boolean to a variable assert self._read_fut is fut or not (self._read_fut is None and self._closing) except ConnectionAbortedError as exc: pass ``` pattern4: ``` if not _winreg: return #z=z def enum_types(mimedb): pass ``` Pattern 8: ``` try: if a: return b #z=z except OSError as msg: raise URLError(msg) ``` ### py37 Pattern6: ``` assert unicodedata.unidata_version == '3.2.0' #z=z def in_table_a1(code): pass ``` pattern3: ``` assert X #z=z for i in x: pass ``` Pattern 10: ``` while True: # change to temp if action is None: extras.append(arg_strings[start_index]) # return start_index + 1 if a: arg_count = match_argument(action, 'A') else: break ``` Pattern 11: ``` assert x #z=z try: func = getattr(self, 'do_' + cmd) except AttributeError: return self.default(line) ``` pattern2: ``` if cmd == '': return self.default(line) #z=z try: func = getattr(self, 'do_' + cmd) except AttributeError: return self.default(line) ``` pattern4: ``` if not _winreg: return #z=z def enum_types(mimedb): pass ``` Pattern 8: ``` try: if a: return b #z=z except OSError as msg: raise URLError(msg) ``` Pattern 13: ``` try: if fut is not None: # assign following boolean to a variable assert self._read_fut is fut or not (self._read_fut is None and self._closing) except ConnectionAbortedError as exc: pass ``` pattern5: ``` if not _winreg: return #z=z for i in x: pass ``` Pattern 14: ``` # tmp =(2, 7) < version_info[:2] < (3, 2) if (2, 7) < version_info[:2] < (3, 2): #tmp: import collections def callable(x): return isinstance(x, collections.Callable) else: callable = callable ``` ### py38 Pattern 11: Pattern6: ``` assert unicodedata.unidata_version == '3.2.0' #z=z def in_table_a1(code): pass ``` ``` assert x #z=z try: func = getattr(self, 'do_' + cmd) except AttributeError: return self.default(line) ``` pattern 15: ``` for i in x: if a: raise URLError(msg) #z=z ``` Pattern 14: ``` # tmp =(2, 7) < version_info[:2] < (3, 2) if (2, 7) < version_info[:2] < (3, 2): #tmp: import collections def callable(x): return isinstance(x, collections.Callable) else: callable = callable ``` pattern2: ``` if cmd == '': return self.default(line) #z=z try: func = getattr(self, 'do_' + cmd) except AttributeError: return self.default(line) ``` Pattern 10: ``` while True: # change to temp if action is None: extras.append(arg_strings[start_index]) # return start_index + 1 if a: arg_count = match_argument(action, 'A') else: break ``` Pattern 16: ``` for line in source.split("\n"): line = line.strip() if line and line[0] != '#': break # comment this ``` pattern4: ``` if not _winreg: return #z=z def enum_types(mimedb): pass ``` Pattern 17: ``` for kind in ('episode', 'clip'): common_data = (cache.get(current_key) or list(cache.values())[0])[kind] break # remove ```