API Solver Package

We have just released our API Solver package on Cerbero Store for all commercial licenses of Cerbero Suite Advanced.

You can click on the image for a video introduction.

API Solver comes very handy to analyze shellcode and it can also be used programmatically:

from Pkg.APISolver import APISolver

solver = APISolver("win32", ("kernel32", "urlmon"))
for hash in (0xEC0E4E8E, 0x702F1A36, 0xE8AFE98, 0x73E2D87E):
    print(solver.solve(hash))

Output:

['KERNEL32.LoadLibraryA' (ror13_add_32)]
['URLMON.URLDownloadToFileA' (ror13_add_32)]
['KERNEL32.WinExec' (ror13_add_32)]
['KERNEL32.ExitProcess' (ror13_add_32)]

Stay tuned as we’ll soon be releasing more packages for all types of licenses on Cerbero Store!

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.

Common Passwords Package

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

There are two reasons why we created a standalone package:

1) We wanted to provide the same functionality to Cerbero Engine.

2) We intend to expand on the functionality of this package in the future, so making it easy to update was essential.

To obtain the package just open Cerbero Store and install it from there.

Only the default passwords key provider is enabled by default. Other key providers must be enabled manually.

This is a very practical package if you want to avoid typing usual passwords like “password” or “infected”. These are considered default passwords and just installing the package will have you covered.

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.

Sleigh Decompiler Parallelization

In the upcoming Cerbero Suite 5.2 we used our new multi-processing technology (part 1, part 2) 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.

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.