Exemple #1
0
def render_locations(ostream, match):
    # its possible to have an empty locations array here,
    # such as when we're in MODE_FAILURE and showing the logic
    # under a `not` statement (which will have no matched locations).
    locations = list(sorted(match.get("locations", [])))
    if len(locations) == 1:
        ostream.write(" @ ")
        ostream.write(rutils.hex(locations[0]))
    elif len(locations) > 1:
        ostream.write(" @ ")
        if len(locations) > 4:
            # don't display too many locations, because it becomes very noisy.
            # probably only the first handful of locations will be useful for inspection.
            ostream.write(", ".join(map(rutils.hex, locations[0:4])))
            ostream.write(", and %d more..." % (len(locations) - 4))
        else:
            ostream.write(", ".join(map(rutils.hex, locations)))
Exemple #2
0
def render_rules(ostream, doc):
    """
    like:

        ## rules
        check for OutputDebugString error
        namespace  anti-analysis/anti-debugging/debugger-detection
        author     [email protected]
        scope      function
        mbc        Anti-Behavioral Analysis::Detect Debugger::OutputDebugString
        examples   Practical Malware Analysis Lab 16-02.exe_:0x401020
        function @ 0x10004706
          and:
            api: kernel32.SetLastError @ 0x100047C2
            api: kernel32.GetLastError @ 0x10004A87
            api: kernel32.OutputDebugString @ 0x10004767, 0x10004787, 0x10004816, 0x10004895
    """
    had_match = False
    for rule in rutils.capability_rules(doc):
        count = len(rule["matches"])
        if count == 1:
            capability = rutils.bold(rule["meta"]["name"])
        else:
            capability = "%s (%d matches)" % (rutils.bold(rule["meta"]["name"]), count)

        ostream.writeln(capability)
        had_match = True

        rows = []
        for key in capa.rules.META_KEYS:
            if key == "name" or key not in rule["meta"]:
                continue

            v = rule["meta"][key]
            if isinstance(v, list) and len(v) == 1:
                v = v[0]
            elif isinstance(v, list) and len(v) > 1:
                v = ", ".join(v)
            rows.append((key, v))

        ostream.writeln(tabulate.tabulate(rows, tablefmt="plain"))

        if rule["meta"]["scope"] == capa.rules.FILE_SCOPE:
            matches = list(doc["rules"][rule["meta"]["name"]]["matches"].values())
            if len(matches) != 1:
                # i think there should only ever be one match per file-scope rule,
                # because we do the file-scope evaluation a single time.
                # but i'm not 100% sure if this is/will always be true.
                # so, lets be explicit about our assumptions and raise an exception if they fail.
                raise RuntimeError("unexpected file scope match count: " + len(matches))
            render_match(ostream, matches[0], indent=0)
        else:
            for location, match in sorted(doc["rules"][rule["meta"]["name"]]["matches"].items()):
                ostream.write(rule["meta"]["scope"])
                ostream.write(" @ ")
                ostream.writeln(rutils.hex(location))
                render_match(ostream, match, indent=1)
        ostream.write("\n")

    if not had_match:
        ostream.writeln(rutils.bold("no capabilities found"))
Exemple #3
0
def render_rules(ostream, doc):
    """
    like:

        ## rules
        check for OutputDebugString error
        namespace  anti-analysis/anti-debugging/debugger-detection
        author     [email protected]
        scope      function
        mbc        Anti-Behavioral Analysis::Detect Debugger::OutputDebugString
        function @ 0x10004706
          and:
            api: kernel32.SetLastError @ 0x100047C2
            api: kernel32.GetLastError @ 0x10004A87
            api: kernel32.OutputDebugString @ 0x10004767, 0x10004787, 0x10004816, 0x10004895
    """
    functions_by_bb = {}
    for function, info in doc["meta"]["analysis"]["layout"]["functions"].items(
    ):
        for bb in info["matched_basic_blocks"]:
            functions_by_bb[bb] = function

    had_match = False

    for (_, _, rule) in sorted(
            map(
                lambda rule: (rule["meta"].get("namespace", ""), rule["meta"][
                    "name"], rule), doc["rules"].values())):
        # default scope hides things like lib rules, malware-category rules, etc.
        # but in vverbose mode, we really want to show everything.
        #
        # still ignore subscope rules because they're stitched into the final document.
        if rule["meta"].get("capa/subscope"):
            continue

        count = len(rule["matches"])
        if count == 1:
            capability = rutils.bold(rule["meta"]["name"])
        else:
            capability = "%s (%d matches)" % (rutils.bold(
                rule["meta"]["name"]), count)

        ostream.writeln(capability)
        had_match = True

        rows = []
        for key in capa.rules.META_KEYS:
            if key == "name" or key not in rule["meta"]:
                continue

            if key == "examples":
                # I can't think of a reason that an analyst would pivot to the concrete example
                # directly from the capa output.
                # the more likely flow is to review the rule and go from there.
                # so, don't make the output messy by showing the examples.
                continue

            v = rule["meta"][key]
            if not v:
                continue

            if key in ("att&ck", "mbc"):
                v = [rutils.format_parts_id(vv) for vv in v]

            if isinstance(v, list) and len(v) == 1:
                v = v[0]
            elif isinstance(v, list) and len(v) > 1:
                v = ", ".join(v)
            rows.append((key, v))

        ostream.writeln(tabulate.tabulate(rows, tablefmt="plain"))

        if rule["meta"]["scope"] == capa.rules.FILE_SCOPE:
            matches = list(
                doc["rules"][rule["meta"]["name"]]["matches"].values())
            if len(matches) != 1:
                # i think there should only ever be one match per file-scope rule,
                # because we do the file-scope evaluation a single time.
                # but i'm not 100% sure if this is/will always be true.
                # so, lets be explicit about our assumptions and raise an exception if they fail.
                raise RuntimeError("unexpected file scope match count: %d" %
                                   (len(matches)))
            render_match(ostream, matches[0], indent=0)
        else:
            for location, match in sorted(
                    doc["rules"][rule["meta"]["name"]]["matches"].items()):
                ostream.write(rule["meta"]["scope"])
                ostream.write(" @ ")
                ostream.write(rutils.hex(location))

                if rule["meta"]["scope"] == capa.rules.BASIC_BLOCK_SCOPE:
                    ostream.write(" in function " +
                                  rutils.hex(functions_by_bb[location]))

                ostream.write("\n")
                render_match(ostream, match, indent=1)
        ostream.write("\n")

    if not had_match:
        ostream.writeln(rutils.bold("no capabilities found"))