There are two ways to use Chrysalide from Python:

Here are some basic steps to introduce both of these usages.

Loading a binary file

First case: known format

If the binary format is known and fixed, loading process can be straightforward:

1
2
3
4
5
6
7
8
9
from pychrysalide.features import *

cnt = FileContent('/path/to/binary_file')

fmt = ElfFormat(cnt)

binary = LoadedBinary(fmt)

binary.analyze_and_wait()
Here is a detailed explanation:

Base for other cases

Here is the general process to load binaries, which remains quite simple:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from pychrysalide.features import *

prj = StudyProject()

cnt = FileContent('/path/to/binary_file')

prj.discover(cnt)

wait_for_all_global_works()

binary = prj.contents[0]
Once again some explanation:

Working with binaries

Browsing symbols

Strings are a special kind of symbols, which can be handled with code like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
for s in binary.format.symbols:

    if not(s.target_type in [ BinSymbol.STP_RO_STRING, BinSymbol.STP_DYN_STRING ]):
        continue

    print('0x%04x - %s' % (s.range.addr.phys, s.label))

    origin = binary.content.read_raw(s.range.addr, s.range.length)

    print('  -> origin:', origin)

    print('  -> raw:', s.raw)

    print('  -> utf8:', s.utf8)

    assert(s.target_type == BinSymbol.STP_DYN_STRING or s.utf8 == origin.decode('utf-8')[:-1])
A few comments:

Analysing instructions

Disassembled instructions can be accessed using the processor attribute of loaded binaries, but also from basic blocks:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
biggest = None

for s in binary.format.symbols:

    if s.target_type != BinSymbol.STP_ROUTINE:
        continue

    if biggest is None or s.basic_blocks.count > biggest.basic_blocks.count:
        biggest = s


def link_type_to_str(t):

    links = [ getattr(ArchInstruction, a) for a in dir(ArchInstruction) if a.startswith('ILT_') ]

    return str(links[links.index(t)])[4:]


def show_block(blk, grp):

    first, last = blk.boundaries

    print('Block @ 0x%x: %s - %s' % (first.range.addr.phys, first.keyword, last.keyword), end='')

    for db, dt in blk.destinations:
        print('  |-> 0x%x (%s)' % (db.boundaries[0].range.addr.phys, link_type_to_str(dt)), end='')

    print()


for bb in biggest.basic_blocks:
    show_block(bb, biggest.basic_blocks)
Here are the key points:

GUI integration

Custom panel

There are plenty of panels in the GUI main window.

Creating a new plugin is a way to build a new kind of panel. The first step is to setup a directory for this new plugin :

1
2
mkdir hellopanel
echo "from hellopanel.core import HelloPlugin as AutoLoad" > hellopanel/__init__.py

The directory containing the hellopanel directory has to be in $PYTHONPATH.

At startup Chrysalide will load the HelloPlugin class as plugin because AutoLoad is an alias for it.

The content of the hellopanel/core.py file is the following one:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pychrysalide.features import *
from .panel import HelloPanel


class HelloPlugin(PluginModule):
    """Simple demo plugin to build a GUI panel."""


    def __init__(self):
        """Initialize the plugin for Chrysalide."""

        interface = {

            'name' : 'HelloPanel',
            'desc' : 'Say hello in the main GUI',
            'version' : '0.1',

            'actions' : ( )

        }

        super(HelloPlugin, self).__init__(**interface)

        p = HelloPanel()

        register_panel(p);

The last file is hellopanel/panel.py; its content is:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from gi.repository import Gtk
from pychrysalide.features import *


DEFAULT_MSG = 'No active loaded binary'


class HelloPanel(PanelItem):

    def __init__(self):
        """Initialize the GUI panel."""

        self._label = Gtk.Label()
        self._label.set_text(DEFAULT_MSG)

        params = {

            'name' : 'Hello',
            'widget' : self._label,

            'personality' : PanelItem.PIP_SINGLETON,
            'lname' : 'Hello panel description',
            'dock' : True,
            'path' : 'MEN'

        }

        super(HelloPanel, self).__init__(**params)


    def _change_content(self, old, new):
        """Get notified about loaded content change."""

        if type(new) is LoadedBinary:

            count = len(list(new.processor.instrs))

            self._label.set_text('Loaded binary with %u instructions' % count)

        else:

            self._label.set_text(DEFAULT_MSG)

A few last words:

Other resources

This tutorial does not explain all the Python API yet.

The reference documentation provides information about all available features.

The snippets repository contains more advanced examples. Some real case plugins are also defined in the Chrysalide's source code.