{{
import datetime
import malcat
import itertools
from helpers import naturalsize

DD_NAMES = [
"Export Table",
"Import Table",
"Resource Directory",
"Exception Table",
"Certificate Table",
"Relocation Table",
"Debug Data",
"Architecture",
"Global Pointer",
"TLS Table",
"Load Config Table",
"Bound Import Table",
"Import Address Table",
"Delay Import Descriptor",
"CLR Runtime",
"Unused"
]



def pval(key, val=None, fmt="{}", va=False, fa=False, rva=False, size=False, kalign=30, valign=0, formated=False):
    r = "[normal]{}[/normal]:".format(key)
    lr = len(r) - 17
    if lr < kalign:
        r += (" "*(kalign-lr))
    if isinstance(val, malcat.StructAccess) or isinstance(val, malcat.FieldAccess):
        if val.has_enum and val.enum:
            d = "[color6]{}[/color6]([dim]{}[/dim])".format(val.enum, fmt.format(val.value))
            formated = True
            ld = len(d) - len("[color3][/color3][dim][/dim]")
        else:
            if size:
                d = naturalsize(val.value, binary=True)
            else:
                toprint = val.value
                if type(toprint) == str:
                    toprint = toprint.replace("[", "\\[")
                d = fmt.format(toprint)
            ld = len(d)
        val = val.value
    elif size and type(val) == int:
        d = naturalsize(val, binary=True)
        ld = len(d)
    else:
        d = fmt.format(val)
        ld = len(d)
    if not formated:
        if type(val) == int: 
            if val == 0:
                grp = "dim"
            elif fa:
                grp = "fa"
            elif rva:
                grp = "rva"
            elif va:
                grp = "va"
            else:
                grp = "normal"
        else:
            grp = "normal"
        d = "[{}]{}[/{}]".format(grp,d,grp)
    if ld < valign:
        d += (" "*(valign-ld))
    write(r+d)  

def bitfieldpp(fa):
    s = []
    for i in range(fa.count - 1, -1, -1):
        bit = fa.at(i)
        if bit.value:
            s.append("[color5]{}[/color5]([dim]{:X}[/dim])".format(bit.name, 1 << i))
    if s:
        return  " + ".join(s)
    else:
        return "[dim]0[/dim]"

def pbitfield(key, fa, kalign=30):    
    r = "[normal]{}[/normal]:".format(key)
    lr = len(r) - 17
    if lr < kalign:
        r += (" "*(kalign-lr))
    write(r + bitfieldpp(fa))

def format_version(v):    
    return "{}.{}.{}".format(v["Major"], v["Minor"], v["Patch"])

def sections_flags(rights):
    s = ""
    for e in [
        ("Alloc", "A"),
        ("Write", "W"),
        ("Execute", "X"),
        ("Merged", "M"),
        ("Strings", "S"),
        ("InfoLink", "I"),
        ("PreserveLinkOrder", "L"),
        ("OsSpecific", "O"),
        ("GroupMember", "G"),
        ("Tls", "T"),
        ("Compressed", "Z"),
        ]:
        if rights[e[0]]:
            s += "[color5]{}[/color5]".format(e[1])
        else:
            s += "[dim]∙[/dim]"
    return s

def segments_flags(rights):
    s = ""
    for e in [
        ("VM_PROT_READ", "R"),
        ("VM_PROT_WRITE", "W"),
        ("VM_PROT_EXECUTE", "X"),
        ("VM_PROT_NO_CHANGE", "N"),
        ("VM_PROT_COPY", "C"),
        ("VM_PROT_WANTS_COPY", "W"),
        ("VM_PROT_TRUSTED", "T"),
        ("VM_PROT_STRIP_READ", "S"),
        ]:
        if rights[e[0]]:
            s += "[color5]{}[/color5]".format(e[1])
        else:
            s += "[dim]∙[/dim]"
    return s

def section_flags(rights):
    s = ""
    for e in [
        ("S_ATTR_LOC_RELOC", "r"),
        ("S_ATTR_EXT_RELOC", "R"),
        ("S_ATTR_SOME_INSTRUCTIONS", "i"),
        ("S_ATTR_DEBUG", "D"),
        ("S_ATTR_LIVE_SUPPORT", "L"),
        ("S_ATTR_NO_DEAD_STRIP", "d"),
        ("S_ATTR_STRIP_STATIC_SYMS", "S"),
        ("S_ATTR_NO_TOC", "t"),
        ("S_ATTR_PURE_INSTRUCTIONS", "I"),
        ]:
        if rights[e[0]]:
            s += "[color5]{}[/color5]".format(e[1])
        else:
            s += "[dim]∙[/dim]"
    return s

def display_entropy(start, size):
    if start is None or size <= 0:
        return " "
    entropy = analysis.entropy[start:start+size]
    return "[ent-{val}:{ent}] [/ent-{val}]".format(val=entropy//4, ent=entropy)
}}
[color1]MACHO Header[/color1]
  {{ pval("CPU", analysis.struct.MachOHeader.CpuType, valign=24) }} {{ pval("Endianeness", analysis.parser.lsb and "LSB" or "MSB") }} 
  {{ pval("CPU-sub",  analysis.struct.MachOHeader.CpuSubType, valign=24) }} {{ pbitfield("CPU Flags", analysis.struct.MachOHeader.CpuFlags) }} 
{{ if "Main" in analysis.struct:}}
  {{ pval("Entry point", analysis.struct.Main.EntryPoint, "{:08x}", fa=True) }}
{{ :endif }}

{{ if "BuildVersion" in analysis.struct:}}
[color1]Requirements[/color1]
  {{ pval("Plateform",  analysis.struct.BuildVersion.Plateform, valign=24) }} 
  {{ pval("Minimum OS", format_version(analysis.struct.BuildVersion.MinimumOS), valign=24) }} {{ pval("Minimum SDK", format_version(analysis.struct.BuildVersion.SDK)) }} 
{{ if "Tools" in analysis.struct.BuildVersion: }}
{{ for tool in analysis.struct.BuildVersion.Tools: }}
  {{ pval("Tool", tool.Tool, valign=24) }} {{ pval("Version", format_version(tool.Version)) }} 
{{ :endfor }}
{{ :endif }}
{{ :endif }}

[color1]Segments & Sections[/color1]
{{ if not analysis.parser.macho_segments:}}
  No segment !
{{:else: }}
{{for i, s in enumerate(analysis.parser.macho_segments):}}
  ■ [color6]{{ "{:14.14s}".format(s["Name"].replace("[", "\\[")) }}[/color6] \[{{ write(segments_flags(s["MaximumProtection"]))}}]  --→  Memory: {{if s["VirtualSize"]:}}[va]{{ "{:08X}".format( s["VirtualAddress"])}}[/va]-[va]{{ "{:08X}".format(s["VirtualAddress"] + s["VirtualSize"] - 1)}}[/va]{{:else:}}[dim]00000000[/dim]-[dim]00000000[/dim]{{:end}} ([dim]{{"{:8x}".format(s["VirtualSize"])}}[/dim] bytes)    Disk: {{if s["FileSize"]:}}[fa]{{ "{:08X}".format(s["FileOffset"])}}[/fa]-[fa]{{ "{:08X}".format(s["FileOffset"] + s["FileSize"] - 1)}}[/fa] {{ write(display_entropy(analysis.map.from_phys(s["FileOffset"]), s["FileSize"]))}}{{:else:}}[dim]00000000[/dim]-[dim]00000000[/dim]  {{:end}} ([dim]{{"{:8x}".format(s["FileSize"])}}[/dim] bytes)
{{ if "Sections" in s: }}
{{ for i, section in enumerate(s["Sections"]): }}
  {{if i < s["Sections"].count - 1:}}├{{:else:}}└{{:endif}} [color7]{{ "{:14.14s}".format(section["SectionName"]).replace("[", "\\[") }}[/color7] \[{{ write(section_flags(section["Flags"]))}}] --→  Memory: {{if section["VirtualSize"]:}}[va]{{ "{:08X}".format( section["VirtualAddress"])}}[/va]-[va]{{ "{:08X}".format(section["VirtualAddress"] + section["VirtualSize"] - 1)}}[/va]{{:else:}}[dim]00000000[/dim]-[dim]00000000[/dim]{{:endif}} ([dim]{{"{:8x}".format(section["VirtualSize"])}}[/dim] bytes)    Disk: {{if section["Offset"]:}}[fa]{{ "{:08X}".format(section["Offset"])}}[/fa]-[fa]{{ "{:08X}".format(section["Offset"] + section["VirtualSize"] - 1)}}[/fa] {{ write(display_entropy(analysis.map.from_phys(section["Offset"]), section["VirtualSize"]))}}{{:else:}}[dim]00000000[/dim]-[dim]00000000[/dim]{{:end}} ([dim]{{"{:8x}".format(section["VirtualSize"])}}[/dim] bytes)
{{ :endfor }}
{{ :endif }}
{{ :endfor }}
{{ :endif }}

[color1]Librairies[/color1]
{{for lib in analysis.parser.macho_libs:}}
  [color6]{{write(lib.LibraryName.value.replace("[", "\\["))}}[/color6]
  {{ pval("Timestamp", lib.Timestamp, valign=24) }} 
  {{ pval("Current Version", format_version(lib.CurrentVersion), valign=24) }} {{ pval("Compatiblity Version", format_version(lib.CompatiblityVersion)) }} 

{{ :endfor }}
