Created
March 3, 2024 12:39
-
-
Save schwartz1375/545ebfcb0a43a107d2e2f7d00c240830 to your computer and use it in GitHub Desktop.
This script estimates the cyclomatic complexity of a binary file by analyzing the assembly code extracted using objdump.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
__author__ = 'Matthew Schwartz' | |
import argparse | |
import subprocess | |
def get_assembly_code(binary_file): | |
try: | |
output = subprocess.check_output(["objdump", "-d", binary_file], text=True) | |
return output | |
except Exception as e: | |
print(f"Error during objdump execution: {e}") | |
return None | |
def estimate_cyclomatic_complexity(assembly_code): | |
conditional_jumps = [ | |
'ja', 'jae', 'jb', 'jbe', 'jc', 'jcxz', 'jecxz', | |
'jrcxz', 'je', 'jg', 'jge', 'jl', 'jle', 'jna', | |
'jnae', 'jnb', 'jnbe', 'jnc', 'jne', 'jng', 'jnge', | |
'jnl', 'jnle', 'jno', 'jnp', 'jns', 'jnz', 'jo', | |
'jp', 'jpe', 'jpo', 'js', 'jz' | |
] | |
count = sum(1 for line in assembly_code.splitlines() if any(jump in line for jump in conditional_jumps)) | |
return count + 1 # Adding 1 to account for the initial path | |
def main(): | |
parser = argparse.ArgumentParser(description="Estimate the cyclomatic complexity of a binary file.") | |
parser.add_argument("file", help="The path to the binary file to analyze.") | |
args = parser.parse_args() | |
assembly_code = get_assembly_code(args.file) | |
if assembly_code: | |
complexity = estimate_cyclomatic_complexity(assembly_code) | |
print(f"Cyclomatic Complexity Estimate: {complexity}") | |
else: | |
print("Failed to estimate cyclomatic complexity.") | |
if __name__ == "__main__": | |
main() | |
''' | |
The list of conditional jump instructions in the estimate_cyclomatic_complexity function is a | |
basic set and might not be exhaustive for all x86 and x86_64 binaries. | |
objdump -M intel -d file | grep -E -c 'ja|jae|jb|jbe|jc|jcxz|jecxz|jrcxz|je|jg|jge|jl|jle|jna|jnae|jnb|jnbe|jnc|jne|jng|jnge|jnl|jnle|jno|jnp|jns|jnz|jo|jp|jpe|jpo|js|jz' | |
ja/jnbe: Jump if above/not below or equal (unsigned) | |
jae/jnb/jnc: Jump if above or equal/not below/no carry | |
jb/jnae: Jump if below/not above or equal (unsigned) | |
jbe/jna: Jump if below or equal/not above (unsigned) | |
jc: Jump if carry | |
jcxz/jecxz/jrcxz: Jump if CX/ECX/RCX register is zero | |
je/jz: Jump if equal/zero | |
jg/jnle: Jump if greater/not less or equal (signed) | |
jge/jnl: Jump if greater or equal/not less (signed) | |
jl/jnge: Jump if less/not greater or equal (signed) | |
jle/jng: Jump if less or equal/not greater (signed) | |
jna: Jump if not above (unsigned) | |
jnae: Jump if not above or equal (unsigned) | |
jnb: Jump if not below (unsigned) | |
jnbe: Jump if not below or equal (unsigned) | |
jnc: Jump if no carry | |
jne/jnz: Jump if not equal/not zero | |
jng: Jump if not greater (signed) | |
jnge: Jump if not greater or equal (signed) | |
jnl: Jump if not less (signed) | |
jnle: Jump if not less or equal (signed) | |
jno: Jump if not overflow | |
jnp/jpo: Jump if not parity/parity odd | |
jns: Jump if not sign (non-negative) | |
jo: Jump if overflow | |
jp/jpe: Jump if parity/parity even | |
js: Jump if sign (negative) | |
jz: Jump if zero | |
''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment