2.5. Types in IDA¶
In IDA, a core feature is the ability to apply types to either operands, addresses, structure members, etc. This is a very powerful capability and is one of the more important features of IDA Pro. By doing this, this allows a reverse-engineer to identify the relationships between the types used by instruction operands and global variables. This is normally exposed to the user by various commands or hotkeys.
IDAPython and the IDA SDK expose an API to allow a user to programmatically apply and retrieve type information from the various places a type might have been applied to. There are a variety of functions that do this, and a number of enumerations, flags, and constants that allow one to customize their types.
Unfortunately, using these APIs is likely to get complex as one will need to identify which function to use combined with determining which constants or flags to represent the type they wish to apply.
This project attempts to simplify this by introducing a concept referred to as “Pythonic” types. This is done by dual-using the semantics of the types available in the Python language [1] to determine the correct constants and flags needed by IDA.
References
2.5.1. Pythonic types format¶
There are a number of places within this project that allow a user to display or apply a type. One such place where this is allowed is for a structure member. Although structure members in IDA contain numerous attributes, one of them is the type field which can be used to assign a Pythonic type into.
As mentioned before, Pythonic types utilise the semantics of the more commonly known types in Python in order to enable the user to view or apply a type to a structure field or other receiver of a type. A pythonic type is composed of what’s referred to as a container type, and inside the container type is the element type.
2.5.1.1. Describing the container¶
There are two types of container types, these are the tuple which uses “(” and “)” to group types, and the list which uses “[ and “]” to group the type. A tuple is produces the semantics of an atomic type and is just used to specify the size of the type. A list, however, is used to describe an array and thus is used to specify the number of elements in the array.
When grouping a Pythonic type with either of these, the first element is always the type or class, and the second element is referred to as the size or count. If neither of these container types are specified, then the default size for the core type will be assumed.
A few examples of how to describe an atomic type for an imaginary type
t
can be:
> res = (t, 3) # 3 bytes in size
> res = (t, 8) # 8 bytes in size
> res = t # 4 bytes on 32-bit architectures, 8 bytes on 64-bit
A few examples of using a list to describe an array for an imaginary
type t
can be:
> res = [t, 4] # 4 element array of a default sized 't'
> res = [(t, 2), 10] # 10 element array of a 't' that is 2 bytes in size
> res = [(t, 8), 4] # 4 element array of a 't' that is 8 bytes in size
2.5.1.2. Describing the core type¶
The core type is considered an atomic type and is simply composed of various keywords available in Python that are used to construct objects. It is important to recognize that these are used literally as keywords and they’re not intended to retain any kind of state.
Any one of the following Python types and functions can be used to represent a type that will be transformed into IDA’s format.
int or
long |
– | an integer type that uses types such as
idaapi.FF_WORD , idaapi.FF_DWORD , etc |
chr or
str |
– | a character/string type that uses types such
idaapi.FF_STRLIT , etc |
float |
– | a floating point type which uses types such as
idaapi.FF_FLOAT , idaapi.FF_DOUBLE , etc |
type |
– | a pointer type which uses idaapi.FF_OFF* |
None |
– | an alignment type using idaapi.FF_ALIGN |
structure_t |
– | a structure as retrieved by the
structure module using
structure.by() or similar |
Using these common keywords as types allows one to not have to remember or search through documentation for the correct flags to apply to IDA. For most general purposes this should suffice.
However, if a user chooses to not use this interface, most of the functions that take pythonic types are also capable of taking an integer. This integer is the manually combined flags that represent an IDA type. It is however suggested by the author that the users familiarize themselves with the way that Pythonic types appear in order to comprehend some of the output of functions that return their type in this format.
2.5.2. Examples¶
By using a container type combined with a core type, a vast number of IDA types can be represented. This allows a user to quickly identify what type is being represented without having to test any bits within the integer representing the type. Some examples of describing an atomic type in this format follows.
(int, 4) |
A 4 byte sized integer (dword) |
(int, 8) |
A 2 byte sized integer (qword) |
(int, 1) |
A single byte sized integer (byte) |
(float, 4) |
A 4 byte sized floating point number (single) |
chr |
A single byte sized character or string |
int |
A default sized integer (dword on 32-bit, qword on 64-bit) |
(None, 8) |
An alignment to a multiple of 8 |
(str, 10) or
(chr, 10) |
A 10 character string |
Some examples of using a list to describe an array of some particular element can be:
[(int, 4), 8] |
An 8 element array of 4-byte integers or dwords |
[float, 32] |
A 32 element array of default-sized floats |
[str, 256] |
A 256-element string |
If a instance of structure_t
is desired to be used, this can be
treated as an atomic type. Usage of this, however, does not allow a user to
size the structure using the “(” and “)” grouping operators. This does, though,
allow a user to specify a structure_t
as an array such as via
the following:
[mystruc, 6] |
if mystruc is an instance of
structure_t , then this would represent
a 6 element array of that type |
[mystruc, 1] |
A single element array of mystruc |
2.5.3. Examples – Usage¶
There are a number of places that Pythonic types are used, however the most
common place is within structure members via the type
attribute.
The following examples will demonstrate how to use pythonic types against
a structure member.
First, a structure will need to be identified and then a member which contains a type will need to be fetched:
> st = structure.search(like='*mystruc*')
> m = st.members[4]
Output the type of the 4th member within the structure:
> print m.type
[int, 6]
Modify the type of the 4th member to be the same number of bytes:
> m.type = [(int, 1), 24] # 6 * 4
Shrink the member down to just a 16-bit integer:
> m.type = (int, 2)
Change the member’s type into a particular structure_t
:
> st = structure.search('*someotherstructure*')
> m.type = st
Modify the member’s type so that it represents a 6 element array:
> m.type = [st, 6]
Modify the member’s type so that its a 3 element array of 8 byte floating point numbers (double):
> m.type = [(float, 8), 3]