diff --git a/floss/features/extract.py b/floss/features/extract.py index f79bbf880..7b6cd263a 100644 --- a/floss/features/extract.py +++ b/floss/features/extract.py @@ -36,6 +36,7 @@ TightFunction, KindaTightLoop, NzxorTightLoop, + ComplexFunctions, ) # security cookie checks may perform non-zeroing XORs, these are expected within a certain @@ -317,10 +318,43 @@ def extract_function_loop(f): yield Loop(comp) +def extract_function_complex_functions(f): + """ + Cyclomatic complexity + M = E - N + 2P + where: + E = edges + N = nodes (basic blocks) + P = number of connected components (usually 1) + """ + try: + cfg = viv_utils.CFG(f) + blocks = list(cfg.basic_blocks) + + num_nodes = len(blocks) + + num_edges = 0 + for bb in blocks: + succs = cfg.get_successor_basic_blocks(bb) + num_edges += len(list(succs)) + + complexity = num_edges - num_nodes + 2 + + complexity = max(1, complexity) + + yield ComplexFunctions(complexity) + + except Exception as e: + logger.warning("Failed to calculate complexity for function: %s", e) + # Yield default complexity of 1(a purely linear function, with no branches) on error + yield ComplexFunctions(1) + + FUNCTION_HANDLERS = ( extract_function_calls_to, extract_function_loop, extract_function_kinda_tight_loop, + extract_function_complex_functions, # extract_function_order, # TODO decoding functions are often one of the first in a program # extract_num_api_calls, # TODO decoding functions don't normally contain many (API) calls ) diff --git a/floss/features/features.py b/floss/features/features.py index 1ea341d87..0dfbec459 100644 --- a/floss/features/features.py +++ b/floss/features/features.py @@ -199,3 +199,29 @@ def __init__(self): def score(self): return 1.0 + + +class ComplexFunctions(Feature): + weight = MEDIUM + + def __init__(self, comp): + super(ComplexFunctions, self).__init__(comp) + + def score(self): + complexity = self.value + + if complexity <= 1: + return 0.0 + elif complexity <= 10: + # Simple functions + return 0.2 + elif complexity <= 20: + return 0.4 + elif complexity <= 30: + return 0.6 + elif complexity <= 50: + # Very complex + return 0.8 + else: + # Extremely complex + return 1.0