#!/usr/bin/env python

# headlessly draw the feature map of a given binary

import os, sys, binaryninja
from binaryninja.enums import SymbolType, StringType

WIDTH, HEIGHT = 100, 800

FeatureMapBaseColor = (16, 16, 16)
FeatureMapNavLineColor = (16, 16, 16)
FeatureMapNavHighlightColor = (237, 223, 179)
FeatureMapDataVariableColor = (144, 144, 144)
FeatureMapAsciiStringColor = (162, 217, 175)
FeatureMapUnicodeStringColor = (222, 143, 151)
FeatureMapFunctionColor = (128, 198, 233)
FeatureMapImportColor = (237, 189, 129)
FeatureMapExternColor = (237, 189, 129)
FeatureMapLibraryColor = (237, 189, 129)

bv = binaryninja.load(sys.argv[1], update_analysis=True)

imgdata = [FeatureMapBaseColor]*(WIDTH*HEIGHT)

data_len = sum([s.end - s.start for s in bv.segments])
image_len = WIDTH*HEIGHT
factor = image_len / data_len

def addr_to_fmap_offset(addr):
	for (i,seg) in enumerate(bv.segments):
		#print(f'segment {i}: [{seg.start:08X}, {seg.end:08X})')
		if addr >= seg.start and addr < seg.end:
			a = sum([s.end - s.start for s in bv.segments[0:i]]) + (addr - seg.start)
			return int(factor * a)
	assert False, f'address {addr:08X} was not in any segment'

for seg in bv.segments:
	print(f'segment [{seg.start:08X}, {seg.end:08X}) -draw-> ' + \
		f'[{addr_to_fmap_offset(seg.start):08X}, {addr_to_fmap_offset(seg.end-1):08X}]')

def highlight(a0, a1, color):
	for i in range(addr_to_fmap_offset(a0), addr_to_fmap_offset(a1)):
		imgdata[i] = color

# data variables
for (addr, var) in bv.data_vars.items():
	sym = bv.get_symbol_at(addr)
	if sym and sym.type in [SymbolType.ImportAddressSymbol, SymbolType.ImportedFunctionSymbol, SymbolType.ImportedDataSymbol]:
		color = FeatureMapImportColor
	elif sym and sym.type in [SymbolType.ExternalSymbol]:
		color = FeatureMapExternColor
	else:
		color = FeatureMapDataVariableColor
	highlight(addr, addr + len(var), color)

# strings
for s in bv.strings:
	color = FeatureMapAsciiStringColor if s.type == StringType.AsciiString else FeatureMapUnicodeStringColor
	highlight(s.start, s.start + len(s), color)

# functions
for f in bv.functions:
	sym = f.symbol
	if sym and sym.type == SymbolType.ImportedFunctionSymbol:
		color = FeatureMapImportColor
	elif sym and sym.type == SymbolType.LibraryFunctionSymbol:
		color = FeatureMapLibraryColor
	else:
		color = FeatureMapFunctionColor

	for bb in f.basic_blocks:
		highlight(bb.start, bb.end, color)

# export image
import struct
from PIL import Image
data = b''.join([struct.pack('BBB', *rgb) for rgb in imgdata])
Image.frombytes('RGB', (WIDTH,HEIGHT), data).save('feature-map.png')
