# Functions (analysis.fns)

analysis.fns: malcat.Functions

  The `analysis.fns` object is a `malcat.Functions` instance that gives you access to all the functions identified by the Functions recovery algorithm.

Note that in addition to this documentation, you can find usage examples in the sample script which is loaded when you hit `F8`.

## Navigating over functions

To list and search for functions, you can use the methods from the `Functions` class:

class malcat.Functions

  This class contains all functions identified by the Functions recovery algorithm. Note that all addresses used in this class are effective addresses. See Addressing in Malcat for more details.

  __iter__()

    Iterate over the file’s identified functions

        for fn in analysis.fns:
            print("Function {} found at #{:x}".format(fn.name, analysis.map.a2p(fn.address)))

    Return type:

      iterator over `Function`

  __getitem__(interval)

    Iterate over all the functions contained in the interval (effective address):

        fns = list(analysis.fns[analysis.p2a(0) : analysis.p2a(0x5000)])
        print("there are {} functions in range[#0-#5000[".format(len(fns)))

    Parameters:

      interval (slice) – effective address interval

    Return type:

      iterator over the list of functions (`Function`)

  __getitem__(ea)

    Returns the `Function` containing the effective address ea.

        ep_rva = analysis.struct['OptionalHeader']['AddressOfEntryPoint']
        ep_fn = analysis.fns[analysis.r2a(ep_rva)]

    Parameters:

      ea (int) – effective address

    Return type:

      `Function`

    Raises:

      `KeyError` if no function is defined over ea

  __contains__(ea)

    return True iff there exists a function which contains the effective address ea

    Parameters:

      ea (int) – address to query

    Return type:

      bool

  __getitem__(name)

    Returns the first `Function` named name.

        memcpy = analysis.fns["memcpy"]

    Parameters:

      name (str) – name of the function you are looking for

    Return type:

      `Function` instance

    Raises:

      `KeyError` if no function named name can be found

  __contains__(name)

    return True iff there exists a function named name

    Parameters:

      name (str) – name of the function you are looking for

    Return type:

      bool

  find(ea)

    return the `Function` which starts at or contains the effective address ea, or None if no one can be found.

    Parameters:

      ea (int) – effective address for the query

    Return type:

      `Function` or None

  find_forward(ea)

    return the `Function` which starts at or contains the effective address ea or starts directly after ea, or None if no `Function` is defined beyond ea.

        first_function = analysis.fns.find_forward(0)
        if first_function is None:
            raise ValueError("No function in program!")

    Parameters:

      ea (int) – effective address for the query

    Return type:

      `Function` or None

  find_backward(ea)

    return the `Function` which starts at or contains the effective address ea or the first one that start before ea, or None if no `Function` is defined before ea.

        last_function = analysis.fns.find_backward(analysis.map.end)
        if last_function is None:
            raise ValueError("No function in program!")

    Parameters:

      ea (int) – effective address for the query

    Return type:

      `Function` or None

  force(ea)

    Force a new function definition at the given effective address. The address given should be the entry point of the new function. The method will return false iff a function is already defined at the address. If it succeeds, the operation will be registered in the Undo/redo manager.

        address = analysis.v2a(0x401000)
        if not address in analysis.fns and analysis.fns.force(address):
            print("New function defined at {}".format(analysis.ppa(address)))

    Parameters:

      ea (int) – effective address where to force the function definition (entry point of the new function)

    Return type:

      bool

    Note

    This method invalidates several analyses, which need to be rerun. If you Run a script from the user interface, the UI will take care of this for you at the end of the script. But if you Run Malcat from your python interpreter, you will have to call the method `malcat.Analysis.run()` at some point to take your change into account.

  unforce(ea)

    Un-force a function definition previously made via `force()`. The address given should be the entry point of the function. The method will return false iff a function was not forced at the address. If it succeeds, the unforce operation will be registered in the Undo/redo manager.

    Parameters:

      ea (int) – effective address given to a previous call to `force()` (entry point of the new function)

    Return type:

      bool

  __len__()

    return the number of identified functions

        if len(analysis.fns) == 0:
            raise ValueError("No function found!")

    Return type:

      int

  total_size: int

    Cumulative size of all identified functions

## Functions and prototypes

### The function object

A function is a `Function` instance offering the following python methods and properties:

class malcat.Function
  __iter__()

    > Iterate over the function’s basic blocks. Shortcut for `analysis.cfg[fn.start:fn.end]`.

    >     first_function = analysis.fns.find_forward(0)
    >     for bb in first_function:
    >         print("Basic block found at {}".format(analysis.ppa(bb.address)))

    > rtype:

    >   iterator over `BasicBlock`

    #### Base

    address: int (effective address)

      the start of the function

    start: int (effective address)

      same as `address`

    end: int (effective address)

      last effective address inside the function + 1

    size: int

      size in bytes of the function: `end` - `start`

    __len__()

      return the size in bytes of the function: `end` - `start`

      Return type:

        int

    __contains__(ea)

      return True iff the effective address ea is within the function boundaries

      Parameters:

        ea (int) – address to query

      Return type:

        bool

    disasm(use_hexadecimal=True, only_valid_code=True, use_smart_labels=True, resolve_symbols=True, resolve_functions=True, resolve_strings=True, resolve_structures=True)

      Disassemble this function following the given formatting

      Parameters:

        - use_hexadecimal (bool) – display immediates in hexadecimal base

        - only_valid_code (bool) – ignore/don’t disasm basic blocks not recognized as valid code

        - use_smart_labels (bool) – replace in-function jump targets with .1, .2 .. .N labels

        - resolve_symbols (bool) – known symbol addresses will be replaced by their symbol names

        - resolve_functions (bool) – known function start addresses will be replaced by their function name

        - resolve_strings (bool) – known string addresses will be replaced by the string content

        - resolve_structures (bool) – known structure/fields addresses will be replaced by their structure/field name

      Return type:

        str

    decompile(resolve_symbols=True, resolve_functions=True, resolve_strings=True, resolve_structures=True)

      Decompile this function following the given formatting. Currently only works for “real” architectures, i.e. x86/x64, arm, mips and aarch64

      Parameters:

        - resolve_symbols (bool) – known symbol addresses will be replaced by their symbol names

        - resolve_functions (bool) – known function start addresses will be replaced by their function name

        - resolve_strings (bool) – known string addresses will be replaced by the string content

        - resolve_structures (bool) – known structure/fields addresses will be replaced by their structure/field name

      Return type:

        str

    set_decompile_aliases(renaming_map)

      Define new names for variables in the decompiled code of this function (same as the gui “rename variable” action)

      Parameters:

        renaming_map (dict[str, str]) – an old_var_name -> new_var_name mapping

    get_decompile_aliases()

      Returns all the variable renamings for this function

      Return type:

        dict[str, str]

    hex(exclude_off=False, exclude_disp=False, exclude_reg=False, exclude_imm=False)

      Get the masked out function hex bytes, e.g. `558BEC68????????8374??45..`

      Parameters:

        - exclude_off (bool) – exclude absolute offsets in valid instructions, e.g. `mov eax, off_15bf34`

        - exclude_disp (bool) – exclude displacements in valid instructions, e.g. `mov eax, [ecx+128]`

        - exclude_reg (bool) – exclude registers in valid instructions, e.g. `push eax`

        - exclude_imm (bool) – exclude immediates in valid instructions, e.g. `mov eax, 0x1223`

      Return type:

        str

    callers: List[malcat.Function]

      return the list of functions calling this function

          fn = analysis.fns[analysis.v2a(0x18000127c)]
          for caller in fn.callers:
              print(f"function {caller} is calling {fn}")

    callees: List[malcat.Function]

      return the list of functions called by some code in this function

          fn = analysis.fns[analysis.v2a(0x18000127c)]
          for callee in fn.callees:
              print(f"function {callee} is called by {fn}")

    inrefs: List[malcat.Reference]

      list of all data and code (a list of `malcat.Reference` objects) that references the start of this function

          fn = analysis.fns[analysis.v2a(0x18000127c)]
          for inref in fn.inrefs:
              print(f"function {fn} is referenced by {analysis.ppa(inref.address)} ({inref.type})")
          >> "function sub_18000127c is referenced by 0x1800015f3 (sub_1800014c4+12f) (Type.CODE)"

    outrefs: List[malcat.Reference]

      list of all data and code (a list of `malcat.Reference` objects) referenced by any code instruction of this basic block

          fn = analysis.fns[analysis.v2a(0x18000127c)]
          for outref in fn.outrefs:
              print(f"An instruction of {fn} references {analysis.ppa(outref.address)} (r={outref.r}, x={outref.x})")

    #### Prototype

    fullname: str

      fully qualified function name, e.g X1.X2.Y<Z1, Z2>(A1, A2)

    module: str

      module part of name:, X1.X2.Y<Z1, Z2>(A1, A2) -> X1.X2

    name: str

      identifier part of name: X1.X2.Y<Z1, Z2>(A1, A2) -> Y

      can also be set to rename the function:

          fn = analysis.fns[analysis.v2a(0x18000127c)]
          fn.name = "new_function_name"

    template_args: List[str]

      list of template parameters (strings): X1.X2.Y<Z1, Z2>(A1, A2) -> [Z1, Z2]

    args: List[FunctionParameter]

      list of arguments (see below): X1.X2.Y<Z1, Z2>(A1, A2) -> [A1, A2]

    #### Identification

    category: malcat.Detection.Level

      return the category of the function: CLEAN, LIBRARY, MALWARE or UNKNOWN

    label: str

      in case of malware or library function: what are the matchin malware/library names?

    is_library: bool

      true iff the function is part of a known library (e.g. was recognized by a FLIRT or Kesakode signature)

    #### Statistics

    Malcat computes some statistics for every functions. These are used in anomalies, but can be used in scripts too.

    num_bb: int

      number of basic blocks inside the function (both code and data blocks)

    num_bb_code: int

      number of code basic blocks inside the function

    num_bb_data: int

      number of data basic blocks inside the function (e.g. a switch pointer array)

    size_code: int

      size in bytes of all code blocks inside the functions

    size_data: int

      size in bytes of all data blocks inside the functions

    num_intra_jumps: int

      number of jumps whose target lies within the function

    num_dynamic_jumps: int

      number of jump/call <register>

    num_calls: int

      number of call/syscall

    num_unique_immediate_bytes: int

      number of unique bytes defined across all immediate operands in the function (range between 0 and 256)

    num_highvalue_immediates: int

      number of unique immediate operands which have at least 2 non-zero non-ff bytes defined

    num_string_references: int

      number of string references seen in the function

    num_known_constants_uses: int

      number of know constant uses seen in the function (both inline code constants or referenced constants)

    num_api_calls: int

      number of direct API calls eprformed inside the function

    num_instructions: int

      number of assembly instructions found inside all block codes within the function

    num_xor_ops: int

      number of xor-like opcodes inside the function

    num_arithmetic_ops: int

      number of add/sub/div/mul-like opcodes inside the function

    num_assign_ops: int

      number of mov-like opcodes inside the function

    num_binary_ops: int

      number of and/or/xor/shl/shr-like opcodes inside the function

    num_stack_ops: int

      number of push/pop/frame-pointer-like opcodes inside the function

    num_fpu_ops: int

      number of FPU opcodes inside the function

    num_mmx_ops: int

      number of MMX/AVX opcodes inside the function

### Function arguments

Function arguments are represented in Malcat using `FunctionParameter` instances. Note that debug information parsing in Malcat is work in progress, and that there is currently no stack analysis to automatically deduce the functions arguments. So the argument list is empty most of the time, except when FLIRT signatures have matched or for .NET functions.

Also note that there is no calling convention information yet, since it will be added in another analysis object.

class malcat.FunctionParameter
  name: str

    name of the argument

  type: str

    string representing the type (e.g “unsigned int”) of the argument

  size: int

    size in byte of the argument