DIE's dynamic analysis abilities are only as good as its value parsers. Value parser plugins are the beating heart of DIE as they parse the collected runtime values and transform them from raw hex values into something we can actually understand and use.
Value parser plugin main logic is fairly simple, they receive two input arguments:
- The raw value collected at runtime as a hex value.
- [Optional] Type information as an
idaapi.type_info_t
object
They then try to parse the raw value to a "Human Readable" form and register it as a ParsedValue
object if successful.
If no type information is provided, a parser may still choose to guess the parsed value and assign a relevant guessing score in scale of 1-10. The better the guess, the higher the score. (A score of 0 is considered a perfect match and is reserved to cases where the type is known).
If a value is guessed, more then one ParsedValue
may be assigned to it, since there may be more then one guess for the
correct value.
In order to ease the development of parser plugins, a PluginManager
object has been added to DIE.
This PluginManager
takes care of most of internal integration with DIE, so that if you want to add a new parser
plugin all that is left to do is to implement the parsing logic.
A Boolean parser makes a perfect example of creating your own value parser plugin.
We would like our to receive either the raw value 0x0
or 0x1
and register the ParsedValue True
of False
accordingly.
{ADD ILLUSTRATION}
Each parser plugin is composed of two files, both of which must be located at the same directory.
- The main python file holding the parsing logic.
- A
.yapsy-plugin
file holding the plugin information.
The yapsy-plugin
file should contain at least the following information, for the PluginManager
to recognize the
plugin and for the plugin's author eternal fame.
[Core]
Name = BoolParser
Module = BoolParser
[Documentation]
Author = Anonymous Author
Description = Just a simple boolean parser
Note: the Module
value must match the parser class name.
Simply create a new class which inherits from DataPluginBase
.
class BoolParser(DataPluginBase):
After initializing the class and it's parent class, the plugin type should be set.
self.setPluginType
marks the name that will be given to each of the parsed values registered by this plugin.
Only a single value may be registered as a plugin type.
In our case, we simply register the type "Bool"
.
def __init__(self):
super(BoolParser, self).__init__()
self.setPluginType("Bool")
As stated before, DIE's PluginManager
receives both the raw value and the type info for any collected runtime value,
It then has to decide which of the parser plugins should be assigned to parser this value.
If type info is not available, there is no way of telling which of the parser plugins should be assign,
so the PluginManager
simply assigns all of the plugin parsers, and request they will attempt to guess the value.
On the other hand, if the type information is known, The PluginManager
should receive some information from each
plugin that will inform him of the types supported by this plugin.
This is exactly what the registerSupportedTypes
method is designed to do. it will be called upon plugin initialization
and will inform the plugin manager of the supported types trough the addSupportedType
routine.
Each supported type is simply the name of the type supported by this plugin (e.g "INT"
, "CHAR"
, "BOOL"
).
Adding a supported type is optional, and multiple types can be registered to the same plugin.
(The second argument passed to addSupportedValue
is a description value and is not currently implemented).
In our case, we notify the PluginManager
that the type "BOOL"
should be parsed by this parser.
def registerSupportedTypes(self):
"""
Register string types
@return:
"""
self.addSuportedType("BOOL", 0)
In case type name matching (which is done by the registerSupportedTypes
method) is not enough in order to validate that this plugin can parse the passed raw value, additional matching logic can be added by implementing the matchType
method and checking the passed idaapi.type_info_t
object.
In our case there is no more logic, so we just return True
.
def matchType(self, type):
"""
@param type: IDA type_info_t object
"""
# Add any additional logic here
return True
All that is left to do now is to implement the actual parsing\guessing logic.
Parsing logic is done when the type is known and the ParserManager
have successfully matched the type against this plugin. Guessing logic on the other hand is done when no type have been provided.
The reason for breaking this logic into two different methods is that there may be cases where the logic changes between parsing a value and guessing it. This is not the case of our BoolParser though.
Whenever a value is parsed successfully its parsed value should be registered using the self.addParsedvalue
.
self.addParsedValue
has 4 arguments:
- The parsed value
- The parsing score
- A description of the parsed value
- The raw value representation.
In the case of our simple Boolean parser, the parsing and guessing methods should look like this:
def guessValues(self, rawValue):
"""
Guess string values
"""
if rawValue == 1: # Guess True
self.addParsedvalue(value="True", score=5, description="Boolean", raw=hex(rawValue))
return True
if rawValue == 0: # Guess False
self.addParsedvalue(value="False", score=5, description="Boolean", raw=hex(rawValue))
return True
return False
def parseValue(self, rawValue):
"""
Parse the string value
@return:
"""
if rawValue == 1: # Parse True
self.addParsedvalue(value="True", score=0, description="Boolean", raw=hex(rawValue))
return True
if rawValue == 0: # Parse False
self.addParsedvalue(value="False", score=0, description="Boolean", raw=hex(rawValue))
return True
return False
If you are still unsure of how to create your own plugin, feel free to check out the existing parser plugins code.