Best practices for Tuoni Plugin development
Foreword
Before starting, please keep in mind that Tuoni C2 is a complex product that requires its plugins to be very diverse. Due to this, the plugin SDK is quite flexible in terms of the types of plugins that can be created. While this flexibility encourages creativity and avoids constraints, it also creates a steep learning curve that can make writing your first plugin more complex than it initially seems.
So, when starting, please be patient and read both these docs and the Java documentation thoroughly. In case of questions or confusion, don’t hesitate to reach out to the ShellDot team. We’re happy to help!
As mentioned earlier, most Tuoni plugins consist of the server-side plugin and the shellcode it generates for agents. This means one needs to write both the plugin code in Java (or another JVM language) for the server and separate code for the shellcode. In this example, we’re writing the server-side plugin code in Java 21, built with Gradle, and the shellcode in C# and .NET, built with Visual Studio.
To ease the first steps of creating your own plugin, you can use our example plugin repository as a starting point, copying the build logic and configuration validation/parsing from there: https://github.com/shell-dot/tuoni-example-plugins
Logging
Use the SLF4J logging facade for logging. This makes sure that the log statements end up in the Tuoni server log file. Do not use System.out
or System.err
for logging.
During early development, it is recommended to log all the things until you are sure that the plugin works as expected. After that, it is recommended to log only the necessary information.
Error handling
Always handle exceptions and errors that are received when calling SDK methods. Never silently fail and ignore exceptions. If the exception is not recoverable, then it is recommended to throw a new exception with a more descriptive message.
While most SDK methods have particular checked exceptions for throwing, it is also possible to throw RuntimeException
in case of unexpected errors.
Keep in mind, that errors from background threads will not automatically be propagated to the main thread, so it is recommended to log all exceptions that are thrown in background threads.
Performance
Keep in mind that the Tuoni server is a JVM application and that the plugin code is executed in the same JVM as the rest of the server. Therefore, it is recommended to keep the plugin code as fast as possible. Avoid busy loops and similar pitfalls which would not only affect your plugin, but the performance of the whole server.
JSON parsing and configuration validation
Tuoni server does not enforce use of any particular JSON library for parsing and validating configurations. It is up to the plugin developer to choose the library that suits their needs the best.
For ShellDot own plugins, we use:
- Jackson - JSON serialization and deserialization
- jsonschema-generator - Dynamically generating JSON schema based on Jackson annotations
- Hibernate Validator - For validating deserialized using annotations