-
Notifications
You must be signed in to change notification settings - Fork 855
Plugins
It is possible to extend the core features of objection using plugins. In short, plugins allow one to use a single, self-contained python file (which may or may not include the necessary Frida scripts), and load it as part of the default objection REPL. This means, depending on what your plugin does, a tab completable command will be added to the objection REPL to execute whatever your plugin is built to do. Frida scripts outside of the default objection agent may be run using a plugin, meaning that one may not necessarily need to edit the agent to get new features locally, quickly.
Plugins only have a few required pieces of information for objection to be able to load it. At a high level, an __init__.py
file should exist in a folder, with two variables specified. Those variables are namespace
and plugin
.
The namespace
variable declares the namespace this plugin should be in. This string value is used internally to reference the plugin as well as to determine where the commands defined in an implementation
should go.
The plugin
variable should be a Class instance that inherits from objection.utils.plugin.Plugin
.
A few examples of plugins are available, and may be found at the following locations:
- An example plugin in the objection test suite lives here. This plugin bundles the Frida script sources as a global variable inside of the script, specifying the
script_src
attribute. - A plugin to side load Facebook's Stetho is available here. This plugin contains the Frida script sources as an
index.js
file next to the plugins__init__.py
file, meaning the plugin loader will automatically detect the Frida script sources without specifying any attributes. - A clipboard monitoring plugin is available here whereby the
index.js
is the result of a TypeScript project's compilation phase, also automatically loaded based in the Frida script's name. Larger plugin project's should consider using this method of building the Frida script sources.
Two options exist to load plugins in objection. The first is by specifying a folder to scan for plugin repositories with the -P/--plugin-folder
flag added to the explore
CLI command. The second is by running the plugin load
command, specifying the path to a repository inside of the objection REPL.
When specifying the -P/--plugin-folder
flag, the target directory should contain subdirectories, each being a valid objection plugin.
Every plugin should extend the objection.utils.plugin.Plugin
class.
While it's not a requirement to use a Frida script as part of your plugin, some hints are necessary to give your extended Plugin class an idea on where to look for the Frida script source code. There are a few options available:
- Specifying the
self.script_src
attribute with the Frida script source. - Specifying the
self.script_path
attribute with an absolute path to a file containing the Frida script source. - Not specifying any of the attributes such as
script_src
orscript_path
, but instead simply placing your Frida script sources in a file calledindex.js
next to the plugin's__init__.py
file. In this case, objection will automatically read this file.
Scripts loaded in a plugin may be injected using the base Plugin
class' inject()
method. This is a requirement before you will be able to interact with your Frida scripts exposed rpc.exports
. This method will most commonly be called in your extended Plugin
class' __init__
method. Once called, the extended Plugin
class will have an api
attribute, which are the snake case versions of the methods exposed using your scripts rpc.exports
.
The implementation
dictionary for a plugin contains the specification used by objection to populate the REPL command line. You may add as many 'commands' as you please. The format for this dictionary is as follows:
// Dictionary root
{
'meta': 'Work with Frida version information',
'commands': SUB_COMMANDS
}
// SUB_COMMANDS
{
{
'command_name': {
'meta': 'Short information about the command',
'exec': module.function # module method to call for this command
}
}
A full example implementation would be:
{
'meta': 'Work with Frida version information',
'commands': {
'info': {
'meta': 'Get the current Frida version',
'exec': self.version
}
}
}
The following plugin, when loaded, adds the plugin frida info
command to the objection REPL.
__description__ = "An example plugin, also used in a UnitTest"
from objection.utils.plugin import Plugin
s = """
rpc.exports = {
getInformation: function() {
console.log('hello from Frida'); // direct output
send('Incoming message'); // output via send for 'message' signal
return Frida.version; // return type
}
}
"""
class VersionInfo(Plugin):
""" VersionInfo is a sample plugin to get Frida version information """
def __init__(self, ns):
"""
Creates a new instance of the plugin
:param ns:
"""
# plugin sources are specified, so when the plugin is loaded it will not
# try and discover an index.js next to this file.
self.script_src = s
# as script_src is specified, a path is not necessary. this is simply an
# example of an alternate method to load a Frida script
# self.script_path = os.path.join(os.path.dirname(__file__), "script.js")
implementation = {
'meta': 'Work with Frida version information',
'commands': {
'info': {
'meta': 'Get the current Frida version',
'exec': self.version
}
}
}
super().__init__(__file__, ns, implementation)
self.inject()
def version(self, args: list):
"""
Tests a plugin by calling an RPC export method
called getInformation, and printing the result.
:param args:
:return:
"""
v = self.api.get_information()
print('Frida version: {0}'.format(v))
namespace = 'version'
plugin = VersionInfo