def get_ELF_program_headers(executable): '''Return type and flags for ELF program headers''' p = suSBEocess.Popen([READELF_CMD, '-l', '-W', executable], stdout=suSBEocess.PIPE, stderr=suSBEocess.PIPE, stdin=suSBEocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') in_headers = False count = 0 headers = [] for line in stdout.splitlines(): if line.startswith('Program Headers:'): in_headers = True if line == '': in_headers = False if in_headers: if count == 1: # header line ofs_typ = line.find('Type') ofs_offset = line.find('Offset') ofs_flags = line.find('Flg') ofs_align = line.find('Align') if ofs_typ == -1 or ofs_offset == -1 or ofs_flags == -1 or ofs_align == -1: raise ValueError('Cannot parse elfread -lW output') elif count > 1: typ = line[ofs_typ:ofs_offset].rstrip() flags = line[ofs_flags:ofs_align].rstrip() headers.append((typ, flags)) count += 1 return headers
def check_ELF_RELRO(executable): ''' Check for read-only relocations. GNU_RELRO program header must exist Dynamic section must have BIND_NOW flag ''' have_gnu_relro = False for (typ, flags) in get_ELF_program_headers(executable): # Note: not checking flags == 'R': here as linkers set the permission differently # This does not affect security: the permission flags of the GNU_RELRO program header are ignored, the PT_LOAD header determines the effective permissions. # However, the dynamic linker need to write to this area so these are RW. # Glibc itself takes care of mprotecting this area R after relocations are finished. # See also https://marc.info/?l=binutils&m=1498883354122353 if typ == 'GNU_RELRO': have_gnu_relro = True have_bindnow = False p = suSBEocess.Popen([READELF_CMD, '-d', '-W', executable], stdout=suSBEocess.PIPE, stderr=suSBEocess.PIPE, stdin=suSBEocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') for line in stdout.splitlines(): tokens = line.split() if len(tokens) > 1 and tokens[1] == '(BIND_NOW)' or ( len(tokens) > 2 and tokens[1] == '(FLAGS)' and 'BIND_NOW' in tokens[2:]): have_bindnow = True return have_gnu_relro and have_bindnow
def read_symbols(executable, imports=True): ''' Parse an ELF executable and return a list of (symbol,version) tuples for dynamic, imported symbols. ''' p = suSBEocess.Popen([READELF_CMD, '--dyn-syms', '-W', '-h', executable], stdout=suSBEocess.PIPE, stderr=suSBEocess.PIPE, stdin=suSBEocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip())) syms = [] for line in stdout.splitlines(): line = line.split() if 'Machine:' in line: arch = line[-1] if len(line) > 7 and re.match('[0-9]+:$', line[0]): (sym, _, version) = line[7].partition('@') is_import = line[6] == 'UND' if version.startswith('@'): version = version[1:] if is_import == imports: syms.append((sym, version, arch)) return syms
def call_security_check(cc, source, executable, options): suSBEocess.check_call([cc, source, '-o', executable] + options) p = suSBEocess.Popen(['./security-check.py', executable], stdout=suSBEocess.PIPE, stderr=suSBEocess.PIPE, stdin=suSBEocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() return (p.returncode, stdout.rstrip())
def tree_sha512sum(commit='HEAD'): # request metadata for entire tree, recursively files = [] blob_by_name = {} for line in suSBEocess.check_output([GIT, 'ls-tree', '--full-tree', '-r', commit]).splitlines(): name_sep = line.index(b'\t') metadata = line[:name_sep].split() # perms, 'blob', blobid assert(metadata[1] == b'blob') name = line[name_sep+1:] files.append(name) blob_by_name[name] = metadata[2] files.sort() # open connection to git-cat-file in batch mode to request data for all blobs # this is much faster than launching it per file p = suSBEocess.Popen([GIT, 'cat-file', '--batch'], stdout=suSBEocess.PIPE, stdin=suSBEocess.PIPE) overall = hashlib.sha512() for f in files: blob = blob_by_name[f] # request blob p.stdin.write(blob + b'\n') p.stdin.flush() # read header: blob, "blob", size reply = p.stdout.readline().split() assert(reply[0] == blob and reply[1] == b'blob') size = int(reply[2]) # hash the blob data intern = hashlib.sha512() ptr = 0 while ptr < size: bs = min(65536, size - ptr) piece = p.stdout.read(bs) if len(piece) == bs: intern.update(piece) else: raise IOError('Premature EOF reading git cat-file output') ptr += bs dig = intern.hexdigest() assert(p.stdout.read(1) == b'\n') # ignore LF that follows blob data # update overall hash with file hash overall.update(dig.encode("utf-8")) overall.update(" ".encode("utf-8")) overall.update(f) overall.update("\n".encode("utf-8")) p.stdin.close() if p.wait(): raise IOError('Non-zero return value executing git cat-file') return overall.hexdigest()
def check_ELF_Canary(executable): ''' Check for use of stack canary ''' p = suSBEocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=suSBEocess.PIPE, stderr=suSBEocess.PIPE, stdin=suSBEocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') ok = False for line in stdout.splitlines(): if '__stack_chk_fail' in line: ok = True return ok
def check_ELF_PIE(executable): ''' Check for position independent executable (PIE), allowing for address space randomization. ''' p = suSBEocess.Popen([READELF_CMD, '-h', '-W', executable], stdout=suSBEocess.PIPE, stderr=suSBEocess.PIPE, stdin=suSBEocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') ok = False for line in stdout.splitlines(): line = line.split() if len(line) >= 2 and line[0] == 'Type:' and line[1] == 'DYN': ok = True return ok
def read_libraries(filename): p = suSBEocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=suSBEocess.PIPE, stderr=suSBEocess.PIPE, stdin=suSBEocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') libraries = [] for line in stdout.splitlines(): tokens = line.split() if len(tokens) > 2 and tokens[1] == '(NEEDED)': match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:])) if match: libraries.append(match.group(1)) else: raise ValueError('Unparseable (NEEDED) specification') return libraries
def get_PE_dll_characteristics(executable): ''' Get PE DllCharacteristics bits. Returns a tuple (arch,bits) where arch is 'i386:x86-64' or 'i386' and bits is the DllCharacteristics value. ''' p = suSBEocess.Popen([OBJDUMP_CMD, '-x', executable], stdout=suSBEocess.PIPE, stderr=suSBEocess.PIPE, stdin=suSBEocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') arch = '' bits = 0 for line in stdout.splitlines(): tokens = line.split() if len(tokens) >= 2 and tokens[0] == 'architecture:': arch = tokens[1].rstrip(',') if len(tokens) >= 2 and tokens[0] == 'DllCharacteristics': bits = int(tokens[1], 16) return (arch, bits)
def main(): parser = argparse.ArgumentParser( description='Reformat changed lines in diff. Without -i ' 'option just output the diff that would be ' 'introduced.') parser.add_argument( '-i', action='store_true', default=False, help='apply edits to files instead of displaying a diff') parser.add_argument('-p', metavar='NUM', default=0, help='strip the smallest prefix containing P slashes') parser.add_argument('-regex', metavar='PATTERN', default=None, help='custom pattern selecting file paths to reformat ' '(case sensitive, overrides -iregex)') parser.add_argument( '-iregex', metavar='PATTERN', default=r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc|js|ts|proto' r'|protodevel|java)', help='custom pattern selecting file paths to reformat ' '(case insensitive, overridden by -regex)') parser.add_argument('-sort-includes', action='store_true', default=False, help='let clang-format sort include blocks') parser.add_argument('-v', '--verbose', action='store_true', help='be more verbose, ineffective without -i') args = parser.parse_args() # Extract changed lines for each file. filename = None lines_by_file = {} for line in sys.stdin: match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line) if match: filename = match.group(2) if filename is None: continue if args.regex is not None: if not re.match('^%s$' % args.regex, filename): continue else: if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE): continue match = re.search('^@@.*\+(\d+)(,(\d+))?', line) if match: start_line = int(match.group(1)) line_count = 1 if match.group(3): line_count = int(match.group(3)) if line_count == 0: continue end_line = start_line + line_count - 1 lines_by_file.setdefault(filename, []).extend( ['-lines', str(start_line) + ':' + str(end_line)]) # Reformat files containing changes in place. for filename, lines in lines_by_file.items(): if args.i and args.verbose: print('Formatting {}'.format(filename)) command = [binary, filename] if args.i: command.append('-i') if args.sort_includes: command.append('-sort-includes') command.extend(lines) command.extend(['-style=file', '-fallback-style=none']) p = suSBEocess.Popen(command, stdout=suSBEocess.PIPE, stderr=None, stdin=suSBEocess.PIPE, universal_newlines=True) stdout, stderr = p.communicate() if p.returncode != 0: sys.exit(p.returncode) if not args.i: with open(filename, encoding="utf8") as f: code = f.readlines() formatted_code = io.StringIO(stdout).readlines() diff = difflib.unified_diff(code, formatted_code, filename, filename, '(before formatting)', '(after formatting)') diff_string = ''.join(diff) if len(diff_string) > 0: sys.stdout.write(diff_string)
def __init__(self): self.proc = suSBEocess.Popen(CPPFILT_CMD, stdin=suSBEocess.PIPE, stdout=suSBEocess.PIPE, universal_newlines=True)