Analysis object (analysis)

analysis: Analysis

scripts run inside the Script editor have access to the current analysis result via the analysis global variable.

The malcat.Analysis class is the entry point of Malcat’s scripting interface. It contains the analysis results for a single file/project. You have access to all of the specialized analyses as well as to the raw file using the File object (analysis.file).

The Analysis object

class malcat.Analysis

Attributes

architecture: malcat.Architecture

The main CPU architecture of the file (only set for programs). This property can be overriden, disabling the automatic file type detection. Note that changing it will also invalidate all previous computations (like a call to invalidate()). Setting it to malcat.Architecture.None will re-enable the automatic CPU architecture detection.

a = malcat.analyse("/the/file")

a.architecture = malcat.FileType.Architecture.X86
# this is likely to impact a lot of analyses, so evtg was invalidated. re-run all analyses.
a.run()
type: str

The file type as a short string, e.g. “PE”. This property can be overriden, disabling the automatic file type detection. Note that changing it will also invalidate all previous computations (like a call to invalidate()). Setting it to the empty string ("") will re-enable the automatic type detection.

a = malcat.analyse("/the/file")

a.type = "PE"
# this is likely to impact a lot of analyses, so evtg was invalidated. re-run all analyses.
a.run()
category: malcat.FileType.Category

The category of the identified file type

imagebase: int

The virtual address at which the file should be loaded

metadata: Dict[str, Dict[str, str]]

The file metadata, as extracted from the File parsers. This is what you see in the Summary view. Metadata are str->str associations sorted into categories (the first dictionnary keys).

file: malcat.File

A pointer to the File object (analysis.file).

history: malcat.UndoRedoManager

A pointer to the undo/redo list manager for this analysis.

Error management

ok: bool

True if the analysis ran without error

failed: bool

True if there was an error (analysis result is only partially valid, better to just discard evtg)

log: List[AnalysisError]

the complete list of all errors/warning that took place during the last run

raise_if_failed()

if the analysis failed (failed is True), throws a ValueError with the most recent error (i.e everything beside malcat.AnalysisError.Status.WARNING) seen

Access to annotations / sub-analyses

entropy: malcat.Entropy

A pointer to the File entropy (analysis.entropy).

map: malcat.MappingAnnotation

A pointer to the Address mapping (analysis.map).

struct: malcat.FileStructure

A pointer to the File structures (analysis.struct).

asm: malcat.Asm

A pointer to the Disassembly (analysis.asm).

cfg: malcat.CFG

A pointer to the Control Flow Graph (analysis.cfg).

loops: malcat.Loops

A pointer to the Strongly Connected Components (analysis.loops).

fns: malcat.Functions

A pointer to the Functions (analysis.fns).

strings: malcat.Strings

A pointer to the Strings (analysis.strings).

xref: malcat.CrossReferences

A pointer to the Cross References (analysis.xref).

syms: malcat.Symbols

A pointer to the Symbols (analysis.syms).

sigs: malcat.Signatures

A pointer to the Yara signatures (analysis.sigs).

carved: malcat.SubFiles

A pointer to the Carved files (analysis.carved).

vfiles: List[malcat.VirtualFile]

The list of virtual files identified by the File parsers.

anomalies: malcat.Anomalies

A pointer to the Anomalies (analysis.anomalies).

comments: malcat.UserComments

A pointer to the User comments (analysis.comments).

highlights: malcat.UserHighlights

A pointer to the User highlighted regions (analysis.highlights).

Computation

run()

Some edit operations in Malcat (e.g. malcat.Functions.force()) invalidate one ore several sub-analyses. The whole analysis result can also be invalidated manually using invalidate().

This function re-runs all invalidated/dirty sub-analyses. The analysis is blocking and can take some time.

Note

If you Run a script from the user interface, you don’t have to call this function since the UI will do it for you at the end of your script

invalidate()

Tag all analyses as dirty and needing recomputation. The recomputation can then be triggered via run().

a = malcat.analyse("/the/file")

# modify the file
a.file[10:200] = b"\x00" * 190

# we don't know what we have modified, so re-run all analyses
a.invalidate()
a.run()

# the different sub-analyses know reflect the new state of the file

I/O

save(project_file_path)

Save any user modifications to a Malcat project file (usually ending with a .malcat extension). User modifications include changed flags, user comments, highlighted regions, user labels, forced function starts, etc.

a = malcat.analyse("/the/file")

# make some modifications to the analysis
a.comments[a.p2a(0x100)] = "a comment"

# save the project
if a.save("/the/file.malcat"):
    print("changes were saved and will be loaded by the UI")
Parameters:

project_file_path (str) – the destination path for the project file. If you wan the UI to load it automatically, it should be <path of the file>.malcat.

Raises:

RuntimeError – if something went wrong during the save

Return type:

bool

Note

Everything except the file data is saved. You can save any modification made to the file using malcat.File.save().

load(project_file_path)

Load any user modifications from a Malcat project file (usually ending with a .malcat extension). User modifications include changed flags, user comments, highlighted regions, user labels, forced function starts, etc. After loading, the whole analysis is automatically invalidated. Call run()

a = malcat.analyse("/the/file")
if a.load("/the/file.malcat"):
    print("user changes loaded")
a.run() # rerun the analysis
Parameters:

project_file_path (str) – the path of the project file.

Raises:

RuntimeError – if something went wrong during the save

Return type:

bool

Helpers

ppa(ea, resolve=True, interactive=False)

Format the given effective address using the best string representation. If the effective address lies in memory, its virtuall address will be returned (prefixed with 0x), otherwise its file offset will be returned (prefixed with #).

address = analysis.p2a(0x1000)
print(analysis.ppa(address, resolve=True))

# if you run the script from the UI and use gui.print, you can make clickable addresses like this
gui.print("Click here to go to {}".format(analysis.ppa(address, interactive=True)))
Parameters:
  • ea (int) – effective address to print

  • resolve (bool) –

    if True, the function will append additional context to the address, e.g.:

    • (sub_401c4e + 1e) if the address lies within a function

    • (<Optional header> + 3c) if the address lies within a structure

    • (.section0 + 123c) if the address lies within a section

  • interactive (bool) – if True, the address will be encosed in markers to make it clickable when printed via malcat.UI.print().

Return type:

str

p2a(offset)

converts a file offset to an effective address. Shortcut for malcat.MappingAnnotation.from_phys().

a2p(effective_address)

converts an effective address to a file offset. Shortcut for malcat.MappingAnnotation.to_phys().

v2a(virtual_address)

converts a virtual address to an effective address. Shortcut for malcat.MappingAnnotation.from_virt().

a2v(effective_address)

converts an effective address to a virtual address. Shortcut for malcat.MappingAnnotation.to_virt().

r2a(rva)

converts a Relative Virtual Address to an effective address. Shortcut for malcat.MappingAnnotation.from_rva().

a2r(effective_address)

converts an effective address to a virtual address. Shortcut for malcat.MappingAnnotation.to_rva().

Errors

The malcat.Analysis.last_error and malcat.Analysis.log attributes both manipulate error objects, which are defined below:

class malcat.AnalysisError

This class describes an error that took place during analysis

severity: malcat.AnalysisError.Status

The severity of the error (see below)

module: int

A unique identifier for the sub-analysis module at fault (or 0 if none)

msg: str

An explicative error message

__repr__()

Error pretty printing

Return type:

str

class malcat.AnalysisError.Status
WARNING

A warning: something was unusual, but analysis could continue

ERROR

An error: something unexepected happened that did prevent the analysis to continue

SETUP

An error happened during the analysis setup phase taht prevented the analysis to start at all

MISSING

Malcat is missing one dependency (an analysis engine, a library): no result at all

CRASH

One of Malcat’s analyses unexpectly crashed. Analysis result is only partial at best

Category enum

The malcat.Analysis.category attribute is an enum which can take the following values:

class FileType.Category

This enum describes the type/category of the analyzed file. I can has one of the following values:

UNKNOWN

No file type could be infered, i.e the file was rejected by all parsers

PROGRAM

The file a an executable program (PE, ELF, NSIS script, etc.)

IMAGE

The file is an image

SOUND

The file is a sound file format

DOCUMENT

The file is a document, e.g. an Excel stylesheet

ARCHIVE

The file is an archive, e.g. zip or rar

FILESYSTEM

The file is a filesystem, e.g. a SquashFS container or a FAT32 image

CPU architectures enum

The malcat.Analysis.architecture attribute is an enum which can take the following values:

class malcat.Architecture

This enum describes the main CPU architecture that should be used to interpret the code portion of the file (if any). Note that some file types main contain code for more than one architecture, e.g. Visual Basic Pcode + x86.

NONE
X86
X64
DOTNET
PCODE

Visual Basic Pcode

AU3

AutoIt tokens

BIFF

Biff8 or Biff12 Excel stylesheet. Stylesheets can contain bytcode formulas, thus the architecture.

PY36
PY37
PY38
PY39
PY310

Undo/redo manager

Each edit operations in Malcat (e.g. a file write, a new comment or a forced function start) is saved into the list of undoed operations. This list is programmatically available through the Analysis.history parameter.

Undo/redo manager

class malcat.UndoRedoManager

This class allows you to interact with Malcat’s undo/redo functionnalities for a particular analysis.

__iter__()

Iterate over all the undoable operations for this analysis

for undoable_op in analysis.history:
    print(f"{undoable_op} done at {analysis.ppa(undoable_op.address)}")
Return type:

iterator over UndoableOperation

__len__()

return the number of undoable operations

if len(analysis.history) == 0:
    raise ValueError("No user edit so far")
Return type:

int

undo()

undo the last undoable edit operation. Returns falls if nothing can be undone or an error happened

Return type:

bool

undo_all()

undo all edit operations. Returns true iff no error.

Return type:

bool

redo()

redo the next redoable edit operation. Returns falls if nothing can be redone or an error happened

Return type:

bool

record(bool enable)

enable or disable recording subsequent edit operations inside this undo/redo manager

analysis.history.record(False)
# ... add your comments, they won't appear in the undo list
for i in range(malcat.map.end):
     malcat.comments[i] = "..."
# ... reactivate undo/redo
analysis.history.record(True)
Parameters:

enable (bool) – if True, edit operations for this analysis will be saved inside the undo/redo manager

group()

context manager that will group all edit operations in a single undoable operation

with analysis.history.group()
    # ... add your comments
    for i in range(malcat.map.end):
         malcat.comments[i] = "..."
analysis.history.undo()     # <-- will undo all comments in one go
Parameters:

enable (bool) – if True, edit operations for this analysis will be saved inside the undo/redo manager

Undoable operation

Edits which can be undone (which is currently all edits supported by Malcat) are represented in the undo/redo manager by a UndoableOperation instance, defined below:

class malcat.UndoableOperation

An user edit targetting this analysis which can be undo

address: int (effective address)

the (first) address where the edit took place

size: int

the number of bytes affected by the edit

label: str

the content of the comment. May contains newlines.

__repr__()

return the value of the label property

Return type:

str

__len__()

return the value of the size property

Return type:

int