{{
import datetime
import malcat
from helpers import naturalsize



def pval(key, val=None, fmt="{}", va=False, pa=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 pa:
                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 sections_rights(rights):
    s = ""
    for e in [
        ("MemRead", "R"),
        ("MemWrite", "W"),
        ("MemExecute", "X"),
        ("ScnAlign1", "1"),
        ("ScnAlign2", "2"),
        ("ScnAlign8", "8"),
        ("MemShared", "S"),
        ("MemNotPaged", "P"),
        ("MemNotCached", "H"),
        ("MemDiscardable", "D"),
        ("SctCntCode", "C"),
        ("SctCntInitializedData", "I"),
        ("SctCntUnititializedData", "U"),
        ]:
        if rights[e[0]]:
            s += "[color5]{}[/color5]".format(e[1])
        else:
            s += "[dim]∙[/dim]"
    return s

def rsrc_name(n):
    if type(n) == int:
        return "#{:d}".format(n)
    else:
        return n.replace("[", "\\[")

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)

def print_offset(off):
    if off < analysis.file.size:
        return "[fa]{:08x}[/fa]".format(off)
    else:
        return "[error]{:08x}[/error]".format(off)

def print_rva(rva):
    ea = analysis.map.from_rva(rva)
    if ea is not None:
        ea = analysis.map.to_phys(ea)
    if ea is not None:
        return "[rva]{:08x}[/rva]".format(rva)
    else:
        return "[error]{:08x}[/error]".format(rva)
}}
[color1]PE Header[/color1]
  {{ pval("Machine", analysis.struct.PE.Machine, "{:X}") }}
  {{ pval("Timestamp", analysis.struct.PE.TimeDateStamp.value.strftime('%Y-%m-%d %H:%M:%S')) }}
  {{ pbitfield("Characteristics", analysis.struct.PE.Characteristics) }}

[color1]Optional Header[/color1]
  {{ pval("Magic", analysis.struct.OptionalHeader.Magic, "{:X}") }}
  {{ pval("Subsystem", analysis.struct.OptionalHeader.Subsystem, "{:X}") }}
  {{ pbitfield("DllCharacteristics", analysis.struct.OptionalHeader.DllCharacteristics) }}
  {{ pval("Checksum", analysis.struct.OptionalHeader.Checksum, "{:08x}") }}{{ if "InvalidChecksum" in analysis.anomalies: }}    ([error]should be {{ write("{:08x}".format(analysis.helper.pe_checksum())) }}[/error]){{:elif analysis.struct.OptionalHeader.Checksum.value:}} [dim](OK)[/dim]{{:if}}
  {{ pval("Entry point", analysis.struct.OptionalHeader.AddressOfEntryPoint, "{:08x}", rva=True) }}

  {{ pval("Linker version", "{:d}.{:d}".format(analysis.struct.OptionalHeader.MajorLinkerVersion.value, analysis.struct.OptionalHeader.MinorLinkerVersion.value), valign=20) }} {{ pval("Image version", "{:d}.{:d}".format(analysis.struct.OptionalHeader.MajorImageVersion.value, analysis.struct.OptionalHeader.MinorImageVersion.value)) }}
  {{ pval("OS version", "{:d}.{:d}".format(analysis.struct.OptionalHeader.MajorOperatingSystemVersion.value, analysis.struct.OptionalHeader.MinorOperatingSystemVersion.value), "{}", valign=20) }} {{ pval("Subsystem version", "{:d}.{:d}".format(analysis.struct.OptionalHeader.MajorSubsystemVersion.value, analysis.struct.OptionalHeader.MinorSubsystemVersion.value)) }}

  {{ pval("ImageBase", analysis.struct.OptionalHeader.ImageBase, "{:08x}", valign=20, va=True) }} {{ pval("Size of image", analysis.struct.OptionalHeader.SizeOfImage, "{:08x}") }}
  {{ pval("Base of Code", analysis.struct.OptionalHeader.BaseOfCode, "{:08x}", valign=20, rva=True) }} {{ pval("Size of Code", analysis.struct.OptionalHeader.SizeOfCode, "{:08x}") }}
{{ if "BaseOfData" in analysis.struct.OptionalHeader: }}
  {{ pval("Base of Data", analysis.struct.OptionalHeader.BaseOfData, "{:08x}", valign=20, rva=True) }} {{ pval("Size of Data", analysis.struct.OptionalHeader.SizeOfInitializedData, "{:08x}") }}
{{ :else: }}
  {{ pval("Size of Data", analysis.struct.OptionalHeader.SizeOfInitializedData, "{:08x}", valign=20) }}
{{ :end}}
  {{ pval("Size of uninitialized data", analysis.struct.OptionalHeader.SizeOfUninitializedData, "{:08x}") }}

  {{ pval("Section alignement", analysis.struct.OptionalHeader.SectionAlignment, "{:08x}", valign=20) }} {{ pval("File alignement", analysis.struct.OptionalHeader.FileAlignment, "{:08x}") }}
  {{ pval("Stack reserve", analysis.struct.OptionalHeader.SizeOfStackReserve, "{:08x}", valign=20) }} {{ pval("Stack commit", analysis.struct.OptionalHeader.SizeOfStackCommit, "{:08x}") }}
  {{ pval("Heap reserve", analysis.struct.OptionalHeader.SizeOfHeapReserve, "{:08x}", valign=20) }} {{ pval("Heap commit", analysis.struct.OptionalHeader.SizeOfHeapCommit, "{:08x}") }}
{{if "CLR.Metadata" in analysis.struct and "CLR.Metadata" in analysis.struct: }}

[color1]CLR Headers[/color1]
  {{ pval("CLR version", "{:d}.{:d}".format(analysis.struct["CLR.Header"]["CLRMajor"], analysis.struct["CLR.Header"]["CLRMinor"]), valign=20) }} {{if "Version" in analysis.struct["CLR.Metadata"]: }}{{ pval("NET version", analysis.struct["CLR.Metadata"]["Version"]) }} {{ :endif }}
  {{ pbitfield("Flags", analysis.struct["CLR.Header"].Flags) }}
{{ :endif }}

[dim]---------------------------------------------------------------------------------------------[/dim]

[color1]Sections[/color1]
{{ if "Sections" in analysis.struct: }}
{{for i, s in enumerate(analysis.struct.Sections):}}
  [normal]{{ "{:8s}".format(s["Name"].replace("[", "\\[")) }}[/normal] [dim]\[[/dim]{{ write(sections_rights(s["Characteristics"]))}}[dim]][/dim]  --→  Memory: {{if s["VirtualSize"]:}}[va]{{ "{:08X}".format( analysis.struct["OptionalHeader"]["ImageBase"] + s["VirtualAddress"])}}[/va]-[va]{{ "{:08X}".format(analysis.struct["OptionalHeader"]["ImageBase"] + s["VirtualAddress"] + s["VirtualSize"] - 1)}}[/va]{{:else:}}[dim]00000000[/dim]-[dim]00000000[/dim]{{:end}} ([dim]{{"{:8x}".format(s["VirtualSize"])}}[/dim] bytes)    Disk: {{if s["SizeOfRawData"]:}}{{ write(print_offset(s["PointerToRawData"]))}}-{{ write(print_offset(s["PointerToRawData"] + s["SizeOfRawData"] - 1))}}{{:else:}}[dim]00000000[/dim]-[dim]00000000[/dim]{{:end}} {{ write(display_entropy(analysis.map.from_phys(s["PointerToRawData"]), s["SizeOfRawData"]))}} ([dim]{{"{:8x}".format(s["SizeOfRawData"])}}[/dim] bytes)
{{ 
if i >= 256: 
    write(f"  [error]... and {analysis.struct.Sections.count - i} more ...[/error]\n")
    break
}}
{{ :end }}
{{ :endif }}
{{if hasattr(analysis.parser, "streams_net"): }}

[color1].NET streams[/color1]
{{ meta = analysis.struct["CLR.Metadata"] }}
{{for i in range(8, meta.count):}}
  [normal]{{ "{:8s}".format(meta[i]["Name"].replace("[", "\\[")) }}[/normal]                  --→    Disk: {{ write(print_offset(meta.offset + meta[i]["Offset"]))}}-{{ write(print_offset(meta.offset + meta[i]["Offset"] + meta[i]["Size"] - 1))}} {{ write(display_entropy(analysis.map.from_phys(meta.offset + meta[i]["Offset"]), meta[i]["Size"]))}} ([dim]{{"{:8x}".format(meta[i]["Size"])}}[/dim] bytes)
{{ :end }}
{{ :endif }}

[dim]---------------------------------------------------------------------------------------------[/dim]

[color1]Data Directories[/color1]
{{ if "DataDirectory" in analysis.struct.OptionalHeader: }}
{{ for i, dd in enumerate(analysis.struct.OptionalHeader.DataDirectory): }}
{{ if "Offset" in dd: }}
  [color1]\[{{ write("{:2d}".format(i))}}][/color1] {{ pval(format(dd.name), "", "{}") }} {{ pval("off", dd.Offset, "{:08x}", kalign=0, pa=True) }}   {{ pval("size", dd.Size, "{:08x}", kalign=0) }}
{{ :else: }}
  [color1]\[{{ write("{:2d}".format(i))}}][/color1] {{ pval(format(dd.name), "", "{}") }} {{ pval("rva", dd.Rva, "{:08x}", kalign=0, rva=True) }}   {{ pval("size", dd.Size, "{:08x}", kalign=0) }}
{{ :endif }}
{{ :end }}
{{ :endif }}
{{if hasattr(analysis.parser, "index_tables_net"): }}

[color1]CLR Tables[/color1]
{{for name in analysis.parser.index_tables_net.keys():}}
{{ if not f"{name}Table" in analysis.struct: continue }}
{{ table = analysis.struct[f"{name}Table"] }}
  [color1]{{ pval(format(name), "", "{}") }}[/color1]      {{ pval("off", table.offset, "{:08x}", kalign=0, pa=True) }}   {{ pval("size", table.size, "{:08x}", kalign=0) }}  [dim]({{table.count}})[/dim]
{{ :end }}
{{ :endif }}

[dim]---------------------------------------------------------------------------------------------[/dim]
{{ if "PackageInfo" in analysis.struct: }}

[color1]Delphi units[/color1]
  {{ pbitfield("Build flags", analysis.struct.PackageInfo.BuildFlags) }}
{{ for unit in analysis.struct.PackageInfo[2:]: }}
{{ if not "Name" in unit: continue }}
  [normal]{{ "{:29s}".format(unit["Name"].replace("[", "\\[")) }}[/normal] [dim]Flags: {{ "0x{:04x}".format(unit["Info"]) }}[/dim]
{{ :end }}

[dim]---------------------------------------------------------------------------------------------[/dim]

{{ :endif }}
{{ if hasattr(analysis.parser, "static_fields_net"): }}

[color1]Static fields initialisation[/color1]
{{ for name, off, size, _ in analysis.parser.static_fields_net: }}
  [normal]{{ "{:29s}".format(name.replace("[", "\\[")) }}[/normal]   Offset: {{ write(print_offset(off))}}-{{ write(print_offset(off + size))}} {{ write(display_entropy(analysis.map.from_phys(off), size))}} ([dim]{{"{:x}".format(size)}}[/dim] bytes)
{{ :end }}

[dim]---------------------------------------------------------------------------------------------[/dim]

{{ :endif }}
{{ if analysis.parser.resources: }}
[color1]Resources[/color1]
{{
last_type = None
last_name = None
}}
{{for s in analysis.parser.resources.values():}}
{{ if last_type is not None and s.type != last_type: }}{{write("\n")}}{{:endif}}
  {{ if last_type is not None and s.type == last_type and s.name == last_name:}}          [dim]" "[/dim]                         {{:else:}}\[[block{{write(hash(s.type) % 8)}}]{{"{:^6.6s}".format(str(s.type))}}[/block{{write(hash(s.type) % 8)}}]]  [normal]{{ "{:28.28s}".format(rsrc_name(s.name)) }}[/normal]{{:endif}} [dim]{{"{:^7.7s}".format(rsrc_name(s.lang))}}[/dim] --→   RVA: {{ write(print_rva(s.rva))}}-{{ write(print_rva(s.rva + s.size))}} {{ write(display_entropy(analysis.map.from_rva(s.rva), s.size))}} ([dim]{{write(naturalsize(s.size))}}[/dim])
{{ 
last_type = s.type
last_name = s.name
}}
{{:end}}
{{:endif}}
{{if hasattr(analysis.parser, "resources_net"): }}

[color1].NET resources[/color1]
{{for s in analysis.parser.resources_net.values():}}
  [normal]{{ "{:45.45s}".format(rsrc_name(s.name)) }}[/normal]  --→   RVA: {{ write(print_rva(s.rva))}}-{{ write(print_rva(s.rva + s.size))}} {{ write(display_entropy(analysis.map.from_rva(s.rva), s.size))}} ([dim]{{write(naturalsize(s.size))}}[/dim])
{{:endfor}}
{{:endif}}
{{if hasattr(analysis.parser, "resources_vb"): }}

[color1]VB resources[/color1]
{{for s in analysis.parser.resources_vb.values():}}
  [normal]{{ "{:45.45s}".format(rsrc_name(s.name)) }}[/normal]  --→   RVA: {{ write(print_rva(s.rva))}}-{{ write(print_rva(s.rva + s.size))}} {{ write(display_entropy(analysis.map.from_rva(s.rva), s.size))}} ([dim]{{write(naturalsize(s.size))}}[/dim])
{{:endfor}}
{{:endif}}
