Latest available version: IDA and decompilers v8.4.240320 see all releases
Hex-Rays logo State-of-the-art binary code analysis tools
email icon

In this blog post, we are going to illustrate how to use some of the new UI features introduced in IDA Pro 6.1 (embedded choosers, custom icons, etc…) by writing a VirusTotal reporting and file submission plugin for IDA Pro. The plugin will allow you to get reports from VirusTotal based on the input file MD5 or a file of your choice. The plugin will offer to upload the file if the file was not analyzed before.
vt_ui_dlg

The VirusTotal public API

The VirusTotal API is web-service that uses HTTP POST requests with JSON object responses. To work with it you need an API key (get one by creating a VT Community account) and a programming language/library that can make HTTP POST requests and is able to parse JSON strings. In this article, we are going to borrow some code from Bryce Boe’s blog entry “Submitting Binaries to VirusTotal” and modify it a bit. The resulting changes can be found in the BboeVt module as part of this article’s files. Our plugin is going to use the get_file_report() to get a report given a file’s MD5 and the scan_file() to submit a new file to VT

Writing the plugin

Let us first breakdown the plugin requirements into small and simple steps and then at the end we can glue everything together.
Here are the components we need:

  1. A Python wrapper for the VT API: We will use the module mentioned above.
  2. A configuration utility: A simple utility class to save persistent information (like the API key or the reporting options)
  3. A chooser control: This control will store the VT report result in a nice ListView with two columns (AV Vender/Malware name). This control should function in embedded or standalone modes.
  4. A form control: It will allow us to take input information and display the results
  5. The plugin: A plugin class that will orchestrate all of the above components

The configuration utility class

We need a simple class to store persistent data. The data store location will be determined using the idaapi.get_user_idadir(). It resolves to “%APPDATA%\Hex-Rays\IDA Pro” on MS Windows and to “~/.idapro” on Linux or Mac OS.
The persistent information we need to store are the apikey and the plugin dialog options. The other configuration fields are computed during runtime:

  • md5sum: when IDA Pro loads a file, it stores the input file’s MD5 in the database. We can use the idc.GetInputMD5() to retrieve the store MD5 value.
  • input file: by default we take the debugged input file path (this value can be changed in the “Debug/Process options” menu).

vt_func_cfg

The chooser control

The chooser control will be a subclass of the Choose2 class. This Python class is a wrapper for the choose2() IDA Pro SDK function. Embedded choosers (introduced in IDA Pro 6.1) allow choosers not only to exist as separate dockable windows but also to be embedded inside forms. Please see Daniel’s post.
Before creating the chooser class, let us illustrate how to work with custom icons:
vt_func_custom_icon
As you can see, two function calls are involved:

  • load_custom_icon(): The icon data can be a path to a file or a binary string. In the former you pass filename=”path_to_img” parameter and in the latter you need to pass data and format.
    The resulting icon id can be used with any API function that needs an icon id.
  • free_custom_icon(): When done using the icon, make sure you free it with this function.

This is the chooser class (important aspects in the code are marked):
vt_func_echooser

  • The Chooser2 class takes a new (optional) parameter named embedded. If set to true, then this chooser will embeddable and can be hosted by a form (as we will see in the next section).
  • The icon parameter will contain an icon_id that is created with a load_custom_icon() call.
  • The items parameter is a list of lists. Since we are working with two columns then it should have the following format: [ [“row1_col1”, “row1_col2”, “row2_col1”, “row2_col2”], …. ]
  • The OnSelectLine() is a callback that is triggered whenever the user double-clicks on an item. We made our plugin open a web-browser and issue a search query

vt_ui_browser
To create the chooser it is sufficient to create an instance (and pass the mandatory parameters) then call the Show() method.

The form control

The form control is implemented by subclassing the new Form class found in IDAPython >= 1.5.
In essence, the idaapi.Form class wraps around AskUsingForm() IDA Pro SDK function.
If you’ve worked with AskUsingForm() before then you will find it so easy to work with the Form class.
The advantage of using AskUsingForm() or IDAPython’s Form class, instead of operating system UI functions, is that your UI will be portable and will work across platforms and most importantly you don’t need any external dependencies or 3rd party UI libraries installed on the system.
For example, this is how the form will be rendered on Mac OS:
vt_ui_dlg_mac_upload
This is how the form creation code looks like (explanation will follow):
vt_func_form_create
The following sections will explain in more details how to use this class.

The form string syntax

The form string syntax is based on the description found in kernwin.hpp SDK header. The syntax is slightly different; the difference is that you should describe form controls using special tags (surrounded by { and }). Please search for “Format string for AskUsingForm_c()” inside the kernwin.hpp header file to get more documentation about the form string syntax.
The form string explained:

  1. The first line contains some special AskUsingForm() directives. Here we use the STARTITEM to specify which control takes the initial focus. A special function {id:ControlName} is used. It will be replaced by an actual id later when the form is compiled.
  2. After the directives comes the form title
  3. Then we declare that we have a form change callback (notice the {FormChangeCb})
  4. Input form controls are denoted inside “<” and “>”:
    1. The general syntax is: <#Hints if any#S~o~me label:{ControlName}>
    2. You can escape the curly braces with “\{“
    3. Group controls (such as radio or checkbox groups) should have an extra “>{GroupControl}>” termination
    4. The tilde character is equivalent to the ampersand character (on MS Windows). It is used to specify an access key to a given control.
  5. Label controls (address and string labels) can be created too
  6. All other text in this section will be rendered as-is in the dialog

If you’re curious, this is how the form would look like if we did not using the Form class syntax:
vt_func_compform
As you see, all the {Control} strings are expanded to the actual syntax required by AskUsingForm() (also notice how the IDs are assigned as well).

Form controls

All the allowed form controls (input or label controls) are declared as inner-classes in the Form class. When you create the form, you need to pass the form string (as we seen above) and a dictionary of controls with key=ControlName and value=Control_Object_Instance.
Please refer to Form documentation or the ex_askusingform.py example.
Embedded choosers cannot be directly embedded, first you need to create an embedded chooser control and then link it to the embedded chooser instance.

Form change callback

The FormChangeCb control will allow you to specify a callback that will be triggered on form events. Form change callback resemble dialog procedures on MS Windows. This callback will be triggered whenever a form control is focused or changed. From this callback you can hide/enable/move/modify other form controls.
vt_func_form_chgcb
In the code above, we check which control id generated an event and handle actions accordingly.
For example we are enabling/disabling certain checkboxes and handling the “Report” button click by calling the VtReport() function and updating the embedded chooser contents by calling Form.RefreshField().

Showing the form

A form must be compiled with Compile() before it can be displayed with Execute(). No need to compile the form more than once (unless you change the form string). To check if the form is already compiled use the Compiled() function.
It is possible to populate the initial form controls values in two ways:

  • When a form is compiled and before calling Execute() you can directly write to the “value”, “selected” or “checked” field of a form control
  • In the Form change callback: control id == –1 will be passed when the form is created and control id == –2 when the form is closed by pressing Ok.

This is how we show the form (first compile, populate and then display):

vt_func_form_show

Writing the plugin

Now we need to glue all the components together by writing a plugin_t sub-class (a regular IDA Pro script plugin).
vt_func_plg
The run() method does the following:

  • Load a custom icon (the VT icon)
  • Create the form
  • Populate and display the form
  • Free the form
  • If requested by the user, create a regular (not embedded) chooser to show the results in IDA Pro (after the plugin has terminated)

The results shown in a chooser:
vt_ui_pop_results
The results (but this time docked):
vt_ui_docked_results

Using the VirusTotal IDA Pro plugin

Before using the plugin, you need to do the following things:

  • Install simplejson Python package in your system
  • Download and install IDAPython 1.5.1 (Unfortunately, idaapi.AskUsingForm() was mistakenly not compiled into IDAPython 1.5.0).Download the article files (BboeVt module) and the VirusTotal plugin
  • Copy the BboeVt module (from the article files) to your Python site-packages
  • Copy the VirusTotal.py plugin to $IDA\plugins folder

Using the plugin:

  • Run IDA Pro and open a database
  • Invoke the VirusTotal plugin (the default hotkey is Alt-F8)
  • You may fetch a report using a file path (if you browse a file) or a file hash (By default the file path and then file hash will be populated from the currently opened database)
  • Double click on an result item to open the browser and read more about the given report

That’s it! Comments, questions or suggestions are welcome.