Python tricks: Counter class
| 3 min read
Introduction
Whilst attending this year's REcon conference I discovered a, then unknown to me, class in Python that I thought was pretty neat. This being Counter from the collections module. In the training I had selected-Automated Reverse Engineering-I found myself having to find the most and least common instruction type within a given binary.
Not wanting to manually create lists and dictionaries of all possible instruction types, I stumbled upon the Counter class.
What is a Counter?
A Counter is a dict subclass for counting hashable objects. It is a collection where elements are stored as dictionary keys and their counts are stored as dictionary values. Counts are allowed to be any integer value including zero or negative counts. The Counter class is similar to bags or multisets in other languages.
And an example from the same docs page:
# Tally occurrences of words in a list
cnt = Counter()
for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
cnt[word] += 1
cnt
Counter({'blue': 3, 'red': 2, 'green': 1})
# Find the ten most common words in Hamlet
import re
words = re.findall(r'\w+', open('hamlet.txt').read().lower())
Counter(words).most_common(10)
[('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
('you', 554), ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]
This sounded perfect for the task, and at least much better than what I had in mind trying to do this semi-manually/ semi-automatically.
Counter in action
Applying this Counter to our use case of finding the least and most common instruction type within a binary looks about the following through Binary Ninja's Python API:
from collections import Counter
counter = Counter()
for inst in bv.hlil_instructions:
for expr in inst.traverse(lambda e: e):
counter[expr.operation.name] += 1
log_info("3 most and least common instruction type:")
log_info(f"3 most common: {counter.most_common(3)}")
log_info(f"3 least common: {counter.most_common()[:-4:-1]}")
Which will output the following for /usr/bin/xprotect on macOS 26.5.2:
[Default] 3 most and least common instruction type:
[Default] 3 most common: [('HLIL_VAR', 1716), ('HLIL_CONST_PTR', 1118), ('HLIL_CONST', 1073)]
[Default] 3 least common: [('HLIL_WHILE', 1), ('HLIL_CMP_ULE', 1), ('HLIL_DO_WHILE', 2)]
All in all, pretty neat for such a use case! :) Makes me wonder what other useful builtins I am unaware of that could simplify my Python needs