Strings statistics

Purpose

Display statistics about strings accesses such as:

  • binary that read/write a string.
  • number of read/write accesses a binary does on a string.

How to use

usage: strings_stat.py [-h] [--host HOST] [--port PORT] [-v] [pattern]

Display statistics about strings accesses.

positional arguments:
  pattern        Pattern of the string, looking for "*pattern*", does not
                 support Regular Expression. If no pattern provided, all
                 strings will be used.

optional arguments:
  -h, --help     show this help message and exit
  --host HOST    Reven host, as a string (default: "localhost")
  --port PORT    Reven port, as an int (default: 13370)
  -v, --verbose  Increase output verbosity

Known limitations

N/A

Supported versions

REVEN 2.2+

Supported perimeter

Any REVEN scenario.

Dependencies

The script requires that the target REVEN scenario have: * The Strings feature replayed. * The OSSI feature replayed.

Source

import argparse
import builtins
import logging

import reven2


"""
# Strings statistics

## Purpose

Display statistics about strings accesses such as:
  * binary that read/write a string.
  * number of read/write accesses a binary does on a string.

## How to use

```bash
usage: strings_stat.py [-h] [--host HOST] [--port PORT] [-v] [pattern]

Display statistics about strings accesses.

positional arguments:
  pattern        Pattern of the string, looking for "*pattern*", does not
                 support Regular Expression. If no pattern provided, all
                 strings will be used.

optional arguments:
  -h, --help     show this help message and exit
  --host HOST    Reven host, as a string (default: "localhost")
  --port PORT    Reven port, as an int (default: 13370)
  -v, --verbose  Increase output verbosity
```

## Known limitations

N/A

## Supported versions

REVEN 2.2+

## Supported perimeter

Any REVEN scenario.

## Dependencies

The script requires that the target REVEN scenario have:
    * The Strings feature replayed.
    * The OSSI feature replayed.
"""


class BinaryStringOperations(object):
    def __init__(self, binary):
        self.binary = binary
        self.read_count = 0
        self.write_count = 0


def strings_stat(reven_server, pattern=""):
    for string in reven_server.trace.strings(args.pattern):
        binaries = builtins.dict()
        try:
            # iterates on all accesses to all binaries that access to the string (read/write).
            for memory_access in string.memory_accesses():
                ctx = memory_access.transition.context_before()
                if ctx.ossi.location() is None:
                    continue
                binary = ctx.ossi.location().binary
                try:
                    binary_operations = binaries[binary.path]
                except KeyError:
                    binaries[binary.path] = BinaryStringOperations(binary)
                    binary_operations = binaries[binary.path]
                if memory_access.operation == reven2.memhist.MemoryAccessOperation.Read:
                    binary_operations.read_count += 1
                else:
                    binary_operations.write_count += 1
        except RuntimeError:
            # Limitation of `memory_accesses` method that raise a RuntimeError when
            # the service timeout.
            pass
        yield (string, binaries)


def parse_args():
    parser = argparse.ArgumentParser(description="Display statistics about strings accesses.")
    parser.add_argument(
        "--host", dest="host", help='Reven host, as a string (default: "localhost")', default="localhost", type=str
    )
    parser.add_argument("--port", dest="port", help="Reven port, as an int (default: 13370)", type=int, default=13370)
    parser.add_argument(
        "-v",
        "--verbose",
        dest="log_level",
        help="Increase output verbosity",
        action="store_const",
        const=logging.DEBUG,
        default=logging.INFO,
    )
    parser.add_argument(
        "pattern",
        nargs="?",
        help="Pattern of the string, looking for "
        '"*pattern*", does not support Regular Expression. If no pattern provided, '
        "all strings will be used.",
        default="",
        type=str,
    )
    return parser.parse_args()


if __name__ == "__main__":
    args = parse_args()

    logging.basicConfig(format="%(message)s", level=args.log_level)

    logging.debug('##### Getting stat for all strings containing "{0}" #####\n'.format(args.pattern))

    # Get a server instance
    reven_server = reven2.RevenServer(args.host, args.port)

    # Print strings
    for string, binaries in strings_stat(reven_server, args.pattern):
        logging.info('"{}":'.format(string.data))
        for binary_operations in binaries.values():
            logging.info(
                "\t- {} (Read: {} - Write: {})".format(
                    binary_operations.binary.filename, binary_operations.read_count, binary_operations.write_count
                )
            )
        logging.info("")
    logging.debug("##### Done #####")