Certificates Support

In the upcoming 5.5 version of Cerbero Suite and the 2.5 version of Cerbero Engine we support certificate formats. While Cerbero Suite already lets you inspect certificates inside binaries, now it can load them directly from disk and also lets you inspect each individual ASN1 object.

Both DER and PEM encodings for certificates are supported.

You can inspect all types of certificates, including X509, PKCS7 and PKCS12.

We have also exposed the code to our Python SDK in order to make the programmatic parsing of certificates a simple task.

For example, enumerating every ASN1 object in a certificate takes just a few lines of code:

from Pro.Core import *
from Pro.Certificates import *

def main():
    obj = proCoreContext().currentScanProvider().getObject()
    class Visitor(DERObjectVisitor):
        def Visit(self, obj, oi):
            print(oi.offset, oi.content_size)
            return 0
    v = Visitor()
    obj.VisitObjects(v)

main()

We’ll be fully documenting the Pro.Certificates module this year.

Command Line Improvements

In this post we’ll be talking about the improvements we’ve made to the command line in Cerbero Suite.

If you’re familiar with command-line scripting in Cerbero Suite, you might know that by running a script without the “-c” argument all output is redirected to the output view in the main window.

In certain cases, however, it might be desirable to avoid the creation of a main window.

For this purpose we have introduced the ‘-g’ argument.

For example:

cerpro.exe -g -r foo.py

If the script doesn’t create an output view, then the output of the ‘print’ function isn’t visible.

Furthermore, in the upcoming 5.5 version of Cerbero Suite we have added terminal support on Windows.

On Windows running scripts with the ‘-c’ argument results in not being able to see the stdout output. The reason for this is that the cerpro executable is built as a GUI application and therefore is not attached to a terminal.

To overcome this limitation we have added a launcher on Windows called “cerpro_console.exe”.

For example:

cerpro_console.exe -e "t=input('Input a string: ');print(t)"

The code asks the user to input a string and prints it back.

Of course, the cerpro_console executable can be used to launch any functionality of Cerbero Suite which supports console mode (‘-c’).

For example the following command prints out the command-line help to stdout:

cerpro_console.exe -h

Soon we’ll publish a complete tutorial about command-line scripting on our SDK page.

String Decrypter Package

We have just released our String Decrypter package on Cerbero Store for all licenses of Cerbero Suite. The String Decrypter package is very useful for reversing malware and during CTFs.

This utility can be invoked as an action from a hex view or a Carbon disassembly. It can be used to brute-force the decryption of strings and byte-arrays.

String Decrypter supports various types of string encodings combined with endianness and it can filter decoded strings with the following options:

– Don’t filter (include raw byte-arrays)
– Include only decoded strings
– Include only strings with ASCII characters
– Include only string matching a regular expression provided by the user

The plugin supports parallel execution, which will make the difference if more algorithms are added to the list. Also, for every decryption method the number of combinations is displayed.

For every decrypted entry, String Decrypter shows the performed operation along with the string encoding (if available).

Suite 5.4 and Engine 2.4 are out!

Here summarized are the main news of this release of Cerbero Suite 5.4 and Cerbero Engine 2.4.

.NET ReadyToRun Format Support

Thanks to one of our customers who reported it to us we have introduced support for the .NET ReadyToRun format.

We already support NGen generated native images and our support for the ReadyToRun format makes sure that it is not mistaken for an NGen generated image.

Hex Editing Processes on Linux

This release of Cerbero Suite introduces the capability to open processes in the hex editor on Linux. Windows has already supported this feature since the introduction of our hex workspace.

You can read more about the topic in our dedicated post.

We have also exposed our process API in the Core module to Python and documented it.

API Solver Package

We have released our API Solver package on Cerbero Store for all commercial licenses of Cerbero Suite Advanced. This package is especially useful when analyzing shellcode.

You can read more about the topic in our dedicated post.

Common Passwords Package

We moved our built-in password brute-forcers to an external package on Cerbero Store called “Common Passwords”. Cerbero Suite Advanced (both commercial and non-commercial) and Cerbero Engine have access to the package.

You can read more about the topic in our dedicated post.

Silicon Spreadsheet Documentation

We have fully documented our Excel macro emulator and spreadsheet visualization module.

Improved ITSF (CHM) Format Support

We have improved our support for Microsoft’s ITSF (also known as CHM) format and we have exposed the format to our Python SDK.

You can read more about the topic in our dedicated post.

Improved Hex Editor

We made it very easy to select contiguous ASCII, Hex and Base64 strings in the hex editor. This comes very handy when loading embedded files or decoding data.

We have also made other minor improvements and fixed a few bugs.

Hex Editing of Processes on Linux

The upcoming 5.4 version of Cerbero Suite introduces the capability to open processes in the hex editor on Linux. Windows has already supported this feature since the introduction of our hex workspace.

Just like on Windows, it is not only possible to edit the entire address space, but also to edit individual modules.

If opening a process fails, most probably it is because root privileges are required to perform the operation. To avoid having the application create application files with different access rights, it is recommended to create a portable copy of Cerbero Suite and run that copy as root.

Creating a portable copy can be achieved from Settings -> Portable.

Improved ITSF (CHM) Format Support

The upcoming 5.4 version of Cerbero Suite and 2.4 of Cerbero Engine come with improved support for Microsoft’s ITSF (also known as CHM) format.

We also exposed the format to our Python SDK.

The following sample enumerates files in a CHM. The commented out line of code extracts the file data.

from Pro.Core import *
from Pro.ITSF import *

def parseCHM(chm_name):
    c = createContainerFromFile(chm_name)
    obj = ITSFObject()
    if not obj.Load(c):
        return
    if not obj.LoadHeaders() or not obj.DecompressSections():
        return
    n = obj.GetListingEntryCount()
    entry = ITSFDirectoryListingEntry()
    for i in range(n):
        if obj.GetDirectoryListingEntry(i, entry):
            print("name:", entry.name, "- size:", entry.size)
            #content = obj.GetFile(entry)

Suite 5.3 and Engine 2.3 are out!

We’re happy to announce the release of Cerbero Suite 5.3 and Cerbero Engine 2.3!

The main addition to this release is the introduction of our latest milestone: Cerbero Store.

We have covered Cerbero Store in depth in our previous post.

TL;DR: Cerbero Store modularizes our setup process, so that updates can be blazingly fast and experimental features can be introduced on a rolling basis. The only thing you need to access Cerbero Store is a current license for either Cerbero Suite or Cerbero Engine. Updating packages from Cerbero Store is equally comfortable.

We also moved our native UI for Ghidra plugin and our Windows memory analysis to packages on Cerbero Store.

In the next months we’ll be releasing new features not only in Cerbero Suite and Cerbero Engine, but also as packages on Cerbero Store, so make sure to follow us on our blog, Twitter or LinkedIn to stay up to date with the latest news!

Introducing Cerbero Store

We’re proud to present Cerbero Store which will be released with the upcoming 5.3 version of Cerbero Suite and 2.3 version of Cerbero Engine.

A few months ago we released our package technology to comfortably install plugins in Cerbero Suite and Cerbero Engine. Cerbero Store is what we had planned from the beginning when we started working on packages.

But how comfortable is it really to install a package from Cerbero Store you might ask?

It is really that simple.

We had various reasons to create Cerbero Store. Chief among these reason was the necessity to release faster updates. It didn’t make sense to update the whole application just to update a limited part. Also, our software runs on multiple platforms, which means that each update requires us to create multiple software packages. This problem is solved by Cerbero Store, since all platforms share the same package code.

Another advantage of Cerbero Store is that some components which are used by a minority of users can now be decoupled from the main application. In fact, we moved our Windows memory analysis functionality to a package on Cerbero Store. That way all our software packages are sensibly lighter. For instance, our macOS DMG archive dropped from 72 MBs to 60 MBs just for this reason.

We’ve made the update of packages equally comfortable.

Yet another component we have moved to a package on Cerbero Store is our native UI for Ghidra.

The reason for this is that Ghidra sometimes changes its API between releases and breaks our plugin code. So it happened in the past that we had to update our whole application just to update the plugin for Ghidra. This issue has now been solved by having the plugin for Ghidra as a separate package.

Installing and updating packages in Cerbero Engine is done by using the ProManage.py script inside the local ‘python’ directory. E.g.:

./python.sh ProManage.py -store --install "Windows Memory Analysis"

or

./python.sh ProManage.py -store --update "Windows Memory Analysis"

or

./python.sh ProManage.py -store --update-all

The same command line options are also available in Cerbero Suite.

We took great care in making Cerbero Store not only easy and comfortable to use, but also secure. All our packages are digitally signed and if someone managed to tamper with our packages online, the installer would refuse to install them, because they would no longer feature a valid signature.

The only thing you need to access Cerbero Store is a current license for either Cerbero Suite or Cerbero Engine. Not all packages available to Cerbero Suite are also available to Cerbero Engine and vice-versa. Certain packages may only be available to the advanced edition of Cerbero Suite, while others may be reserved to commercial licenses.

In the next months we’ll be releasing new features not only in our applications, but also as packages on Cerbero Store, so make sure to follow us on our blog, Twitter or LinkedIn to stay up to date with the latest news!

Cerbero Suite 5.2 is out!

We’re happy to announce the release of Cerbero Suite 5.2 and Cerbero Engine 2.2!

In this post we summarize the most important new features.

Multi-Processing

The main feature of this release is the introduction of our multi-processing technology.

Our products make use of parallel processing in terms of multi-threading whenever possible, but there are limitations to the capabilities of multi-threading.

Some of the advantages offered by multi-processing are:

  • Possible process isolation
  • Increased stability for 3rd party components
  • Overcoming the Global Interpreter Lock (GIL) in Python

We have already detailed our multi-processing technology in two previous posts (part 1, part 2), but with this release we also fully documented the API.

Sleigh Decompiler Parallelization

We used our new multi-processing technology to parallelize the Sleigh decompiler by running it in a different process. This guarantees complete stability in case Sleigh encounters an issue and makes every decompiling operation safe to cancel.

We didn’t notice slow-downs by running the decompiler in a different process, in fact it’s still blazingly fast.

By parallelizing the decompiler we were also able to initialize it during the loading of the file/database. Thus, when the decompiler is invoked for the first time there is no initial delay.

Although the decompiler doesn’t take much time to load, the preloading makes it extra-snappy.

It is also possible to choose to run the decompiler in the same process as before from the Carbon settings.

Carbon Documentation

We have fully documented the Carbon API to disassemble and decompile native binaries.

The documentation contains numerous code examples which cover the decryption of strings, disassembling of files, decompiling of functions and the creation of custom file loaders.

ZeroMQ Module

Our multi-processing technology relies on ZeroMQ. Therefore, we exposed ZeroMQ to our Python SDK.

Rather than using the provided Python wrappers, we exposed the C interface directly. We just added a few methods to convert from and to bytes objects in Python.

This is a basic client-server example using send/recv.

The client:

from Pro.zmq import *
import ctypes

context = zmq_ctx_new()

socket = zmq_socket(context, ZMQ_REQ)
zmq_connect(socket, "tcp://localhost:5555")

for i in range(1000):
    zmq_send_bytes(socket, b"Hello, world!", 0)
print("info: sent")

zmq_close(socket)
zmq_ctx_destroy(context)

The server:

from Pro.zmq import *

context = zmq_ctx_new()

socket = zmq_socket(context, ZMQ_REP)
rc = zmq_bind(socket, "tcp://127.0.0.1:5555")

if rc == 0:
    while True:
        b = zmq_recv_bytes(socket, 13, 0)
        print(b)
        break
else:
    print("error: couldn't bind to port")

zmq_close(socket)
zmq_ctx_destroy(context)

And this is a basic client-server example using messages.

The client:

from Pro.zmq import *
import ctypes

context = zmq_ctx_new()

socket = zmq_socket(context, ZMQ_REQ)
zmq_connect(socket, "tcp://localhost:5555")

msg = zmq_msg_t()
zmq_msg_init_bytes(msg, b"Hello, world!")
rc = zmq_msg_send(msg, socket, 0)
print(rc)
print("info: sent")

zmq_close(socket)
zmq_ctx_destroy(context)

The server:

from Pro.zmq import *

context = zmq_ctx_new()

socket = zmq_socket(context, ZMQ_REP)
rc = zmq_bind(socket, "tcp://127.0.0.1:5555")

if rc == 0:
    msg = zmq_msg_t()
    zmq_msg_init(msg)
    while True:
        # wait until a message is received
        rc = zmq_msg_recv(msg, socket, 0)
        if rc != -1:
            print(zmq_msg_bytes(msg))
        zmq_msg_close (msg)
        break
else:
    print("error: couldn't bind to port")

zmq_close(socket)
zmq_ctx_destroy(context)

Improved Logic Providers

We optimized logic provider extensions. In particular, it is now possible to specify the type option for standalone tools:

type = tool

When this option is specified, the init function of the logic provider must return False. This causes the logic provider to be treated as a standalone tool rather than a scan logic provider and avoids creating a scan report for it.

Improved Custom Views

We added the progress bar control and idle notifications to custom views. You can find both features documented on the SDK page of the UI module.

Finally, a thank you to Insid3Code Team for reporting three of the bugs we fixed in this release.

Remote Containers

In our previous post we introduced multi-processing as implemented in the upcoming Cerbero Suite 5.2 and Cerbero Engine 2.2. In this post we’re going to talk about remote containers, which are an additional functionality of our multi-processing technology.

Containers (NTContainer) are a way to encapsulate any kind of raw data (e.g.: memory, files) and are used ubiquitously. There might be occasions in which a manager wants to share a container with a worker.

The API to accomplish this is very simple: all the manager has to do is to share the container using shareContainer() and the worker can access the container using getSharedContainer().

In the following example a 10 mega-bytes container is created with a signature appended at the end. A local and remote search is performed to find the signature.

from Pro.Core import NTContainer, MB_SIZE, NTTime
from Pro.MP import *
import time

remote_code = r"""
from Pro.Core import NTTime
from Pro.MP import *

def main():
    c = proWorkerObject().getSharedContainer('NAME')
    # remote search
    magic = b'\xAA\xBB\xCC\xDD'
    t = NTTime()
    t.start()
    match = c.findFirst(magic)
    print('remote search (ms): ' + str(t.elapsed()))

main()
"""

def main():
    magic = b"\xAA\xBB\xCC\xDD"
    buf = b"\xFF" * 10 * MB_SIZE + magic
    c = NTContainer()
    c.setData(buf)
    
    # local search
    t = NTTime()
    t.start()
    match = c.findFirst(magic)
    print("local search (ms):", t.elapsed())

    m = ProManager()
    m.setOptions(ProMPOpt_AtomicOutput)
    
    m.shareContainer("NAME", c)
    
    worker_id = m.startWorker()

    m.evalPythonCode(worker_id, remote_code)
    
    while m.isBusy():
        m.processMessages()
        time.sleep(0.1)
    
main()

The output is:

local search (ms): 17
remote search (ms): 351

The reason for the time difference is, of course, that accessing the remote data is comparatively slower. This factor needs to be taken into consideration when working with remote containers.

Yet another limitation regarding remote containers is that they are read-only. This is for security reasons, as it wouldn’t be safe to allow other processes to change the original container.

In the next example the code asks the user to choose a Windows executable (PE), opens it and shares the container. The import table of the PE is then parsed from the worker process.

from Pro.Core import *
from Pro.UI import *
from Pro.MP import *
import time

remote_code = r'''
from Pro.Core import *
from Pro.MP import *
from Pro.PE import *

def main():
    c = proWorkerObject().getSharedContainer("PE")
    # print imported modules
    obj = PEObject()
    if not obj.Load(c):
        print("error: couldn't load file")
        return
    imp = obj.ImportDirectory()
   
    it = CFFStructIt(imp)
    while it.hasNext():
        cur = it.next()
        name_rva = cur.Uns("Name")
        name_offs = obj.RvaToOffset(name_rva)
        if name_offs != INVALID_STREAM_OFFSET:
            name = obj.ReadUInt8String(name_offs, 1000)[0]
            name = name.decode("utf-8", errors="ignore")
            print("imported module: " + name)
            

main()
'''

def main():
    fname = proContext().getOpenFileName("Select Windows executable...", str(), "Executable files (*.exe)")
    if not fname:
        return

    c = createContainerFromFile(fname)
    if c.isNull():
        return

    m = ProManager()
    m.setOptions(ProMPOpt_AtomicOutput)
    
    m.shareContainer("PE", c)
    
    worker_id = m.startWorker()

    m.evalPythonCode(worker_id, remote_code)
    
    while m.isBusy():
        m.processMessages()
        time.sleep(0.1)
    
main()

An example of output is:

imported module: KERNEL32.dll
imported module: SHLWAPI.dll

In the following example the shared container is shown in a hex view from the worker process.

from Pro.Core import *
from Pro.UI import *
from Pro.MP import *
import time

remote_code = r'''
from Pro.Core import *
from Pro.UI import *
from Pro.MP import *

def main():
    c = proWorkerObject().getSharedContainer("DATA")
    ctx = proContext()
    hv = ctx.createView(ProView.Type_Hex, "Remote Container Data")
    hv.setData(c)
    dlg = ctx.createDialog(hv)
    dlg.show()

main()
'''

def main():
    fname = proContext().getOpenFileName("Select a file...")
    if not fname:
        return

    c = createContainerFromFile(fname)
    if c.isNull():
        return

    m = ProManager()
    m.setOptions(ProMPOpt_AtomicOutput)
    
    m.shareContainer("DATA", c)
    
    worker_id = m.startWorker()

    m.evalPythonCode(worker_id, remote_code)
    
    while m.isBusy():
        m.processMessages()
        time.sleep(0.1)
    
main()

This is a screenshot from running the last example.

Sharing containers with workers is a very inexpensive operation in terms of resources. Therefore, sharing many containers is not an issue.