3

Lets say that i have manually defined a local struct while debugging in IDA pro, when i hover my mouse over any variable that is of that struct type, i can see its members.

Now if i have the variables's address, how to tell IDA to print every member of the struct on that variable in the output window? I am using windbg to debug kernel using IDA.

2
  • did you find a way to do this?
    – Benni
    Commented Jan 21, 2022 at 23:55
  • @Benni Nope, even asked this question again and no one answered.. But if you are using windbg, you can still print windows based structs using dt command.
    – OneAndOnly
    Commented Jan 22, 2022 at 6:35

1 Answer 1

1

The minsc plugin at https://github.com/arizvisa/ida-minsc supports structure/union decoding, arithmetic, and layouting. This can be used to print out a manually defined structure

Disclaimer: I'm the maintainer of the minsc plugin.

As an example, I just looked up some structure that had some data already defined. To fetch the structure associated with a specific address, the database.type.structure function can be used. The structure that I'm using has the following layout (and just happens to be variable-length).

Python>db.t.struc(0x29748)
<class 'structure' name='_EH4_SCOPETABLE' offset=0x29748 size=0x10>

Python>_.members
<class 'structure' name='_EH4_SCOPETABLE' offset=0x29748 size=0x10>
[0] 29748+0x4                            DWORD 'GSCookieOffset'    (<class 'int'>, 4)
[1] 2974c+0x4                            DWORD 'GSCookieXOROffset' (<class 'int'>, 4)
[2] 29750+0x4                            DWORD 'EHCookieOffset'    (<class 'int'>, 4)
[3] 29754+0x4                            DWORD 'EHCookieXOROffset' (<class 'int'>, 4)
[4] 29758+0x0  struct _EH4_SCOPETABLE_RECORD[] 'ScopeRecord'       [<class 'structure' name='_EH4_SCOPETABLE_RECORD' offset=0x29758 size=0xc>, 0]

Python>db.t.struc().up()
[ref_t(address=0x29748, access=access_t(r)), ref_t(address=0x29768, access=access_t(r))]

It's worth noting that the members_t (accessed by the structure_t.members property) supports basic arithmetic (for translating the containing fields to an address/offset), slice-assignments (for copying members/types), or contiguous layout.

Python>db.t.struc().members * 3
[<class 'structure' name='_EH4_SCOPETABLE' offset=0x29748 size=0x10>
[0] 29748+0x4                            DWORD 'GSCookieOffset'    (<class 'int'>, 4)
[1] 2974c+0x4                            DWORD 'GSCookieXOROffset' (<class 'int'>, 4)
[2] 29750+0x4                            DWORD 'EHCookieOffset'    (<class 'int'>, 4)
[3] 29754+0x4                            DWORD 'EHCookieXOROffset' (<class 'int'>, 4)
[4] 29758+0x0  struct _EH4_SCOPETABLE_RECORD[] 'ScopeRecord'       [<class 'structure' name='_EH4_SCOPETABLE_RECORD' offset=0x29758 size=0xc>, 0], <class 'structure' name='_EH4_SCOPETABLE' offset=0x29758 size=0x10>
[0] 29758+0x4                            DWORD 'GSCookieOffset'    (<class 'int'>, 4)
[1] 2975c+0x4                            DWORD 'GSCookieXOROffset' (<class 'int'>, 4)
[2] 29760+0x4                            DWORD 'EHCookieOffset'    (<class 'int'>, 4)
[3] 29764+0x4                            DWORD 'EHCookieXOROffset' (<class 'int'>, 4)
[4] 29768+0x0  struct _EH4_SCOPETABLE_RECORD[] 'ScopeRecord'       [<class 'structure' name='_EH4_SCOPETABLE_RECORD' offset=0x29768 size=0xc>, 0], <class 'structure' name='_EH4_SCOPETABLE' offset=0x29768 size=0x10>
[0] 29768+0x4                            DWORD 'GSCookieOffset'    (<class 'int'>, 4)
[1] 2976c+0x4                            DWORD 'GSCookieXOROffset' (<class 'int'>, 4)
[2] 29770+0x4                            DWORD 'EHCookieOffset'    (<class 'int'>, 4)
[3] 29774+0x4                            DWORD 'EHCookieXOROffset' (<class 'int'>, 4)
[4] 29778+0x0  struct _EH4_SCOPETABLE_RECORD[] 'ScopeRecord'       [<class 'structure' name='_EH4_SCOPETABLE_RECORD' offset=0x29778 size=0xc>, 0]]

As per the answer to your question, decoding structures can be done using the database.get.structure function from the database.get namespace. A related function, database.get.array, can also be used if you want to decode a variable amount of data at a given address.

These functions return structure values as a python dictionary which means that if you want to perform an action based on the specific type associated with the value, the field name would need to be correlated with the field information from the member_t that was fetched using database.type.structure.

Python>db.get.struc()
{'EHCookieOffset': 0xffffffc0, 'EHCookieXOROffset': 0x0, 'GSCookieOffset': 0xfffffffe, 'GSCookieXOROffset': 0x0, 'ScopeRecord': [{'EnclosingLevel': 0xfffffffe, 'FilterFunc': 0x113d6, 'HandlerFunc': 0x113e4}]}

Python>pp(db.get.struc())
{'EHCookieOffset': 4294967232,
 'EHCookieXOROffset': 0,
 'GSCookieOffset': 4294967294,
 'GSCookieXOROffset': 0,
 'ScopeRecord': [{'EnclosingLevel': 4294967294,
                  'FilterFunc': 70614,
                  'HandlerFunc': 70628}]}

Python>db.disasm()
'29768: _EH4_SCOPETABLE <0FFFFFFFEh, 0, 0FFFFFFC8h, 0, <0FFFFFFFEh, \\'

# Combine both to print out each field name and their value.
Python>decoded = db.get.struc()
Python>for m in db.t.struc().members:
Python> print([m.name, decoded[m.name]])
Python>
['GSCookieOffset', 4294967294]
['GSCookieXOROffset', 0]
['EHCookieOffset', 4294967240]
['EHCookieXOROffset', 0]
['ScopeRecord', {'EnclosingLevel': 4294967294, 'FilterFunc': 160384, 'HandlerFunc': 160388}]

If you need to decode an arbitrary address (using a structure that hasn't been applied to said address), the database.get.structure function has the following variations. The example following the list of variations decodes a GUID at offset 0x2a00c.

Python>?db.get.structure
Help on method structure in module database:

structure(*arguments, **keywords) class method of database.get
    get.structure(**byteorder)                                                -> Return the decoded fields of the structure at current address as a dictionary.
    get.structure(ea=int, **byteorder)                                        -> Return the decoded fields of the structure at address `ea` as a dictionary.
    get.structure(ea=int, sptr=struc_t, **byteorder)                          -> Return a dictionary containing the decoded fields of the structure represented by `sptr` using the data at address `ea`.
    get.structure(name=str, *suffix, **byteorder)                             -> Return the decoded fields for the structure belonging to the symbol `name` as a dictionary.
    get.structure(ea=int, structure=structure_t|tinfo_t|str|int, **byteorder) -> Return the decoded fields of the given `structure` from the address `ea` as a dictionary.
    get.structure(ea=int, sptr=struc_t|structure_t, size=int, **byteorder)    -> Return a dictionary containing the decoded fields of the structure `sptr` using `size` bytes from the data at address `ea`.
    get.structure(ea=int, structure=tinfo_t|int|str, size=int, **byteorder)   -> Return a dictionary containing the decoded fields of the given `structure` using `size` bytes from the data at address `ea`.

# Treat data at address 0x2a00c as a GUID.
Python>db.get.structure(0x2a00c, 'GUID')
{'Data1': 0x44bf19b1, 'Data2': 0x520, 'Data3': 0x1993, 'Data4': array('B', [0, 0, 0, 0, 0, 0, 0, 0])}

Likewise, the database.get.array function has similar variations.

Python>?db.get.array
Help on method array in module database:

array(*arguments, **keywords) class method of database.get
    get.array(**byteorder)                           -> Return the values of the array at the current selection or address.
    get.array(bounds=bounds_t, **byteorder)          -> Decode the data within the provided `bounds` as an array.
    get.array(ea=int, **byteorder)                   -> Decode the data at the address `ea` as an array.
    get.array(bounds=bounds_t, type, **byteorder)    -> Return the values within the provided `bounds` as an array of the pythonic element `type`.
    get.array(ea=int, type, **byteorder)             -> Decode the data at the address `ea` as an array of the pythonic element `type`.
    get.array(name=str, *suffix, **byteorder)        -> Decode the data at the symbol identified by `name` as an array.
    get.array(ea=int, length=int, **byteorder)       -> Decode the data at the address `ea` as a `length`-element array.
    get.array(ea=int, type, length=int, **byteorder) -> Decode the data at the address `ea` as a `length`-element array of the pythonic element `type`.

# Decode data at address 0x2a00c as a 4-element array of 16-bit sized integers.
Python>db.get.array(0x2a00c, (int,2), length=4)
array('H', [6577, 17599, 1312, 6547])

# Decode data at address 0x2a00c as a 4-element array of GUID structures.
Python>db.get.array(0x2a00c, struc.by('GUID'), length=4)
[{'Data1': 0x44bf19b1, 'Data2': 0x520, 'Data3': 0x1993, 'Data4': array('B', [0, 0, 0, 0, 0, 0, 0, 0])}, {'Data1': 0x0, 'Data2': 0x2020, 'Data3': 0x2020, 'Data4': array('B', [32, 32, 32, 32, 32, 32, 32, 32])}, {'Data1': 0x20202020, 'Data2': 0x2020, 'Data3': 0x2020, 'Data4': array('B', [32, 32, 32, 32, 32, 32, 32, 32])}, {'Data1': 0x20202020, 'Data2': 0x2020, 'Data3': 0x2020, 'Data4': array('B', [32, 32, 32, 32, 32, 32, 32, 32])}]

In versions of IDA prior to 9.0, frames can also be treated as structures. Hence, the function.frame function can be used to get the current frame and allow it to be treated as a structure. Similar to all structure/unions, this can be used with database.get.structure to decode arbitrary data using the fields from the frame.

Python>func.frame().members
<class 'structure' name='$ F1F974' offset=-0x6c size=0x88>
     -6c+0xc                                        [None, 12]
[0]  -60+0x4                         int 'var_5C'   (<class 'int'>, 4)
[1]  -5c+0x4                         int 'var_58'   (<class 'int'>, 4)
[2]  -58+0x4                         int 'var_54'   (<class 'int'>, 4)
[3]  -54+0x4                       void* 'Src'      (<class 'type'>, 4)
[4]  -50+0x4                         int 'var_4C'   (<class 'int'>, 4)
[5]  -4c+0x4                         int 'var_48'   (<class 'int'>, 4)
...
[28]   4+0x4           tRECEIVE_SESSION* 'arg_0'    (<class 'type'>, 4)
[29]   8+0x2                      USHORT 'arg_4'    (<class 'int'>, 2)
       a+0x2                                        [None, 2]
[30]   c+0x4  tBASIC_DATA_PACKET_HEADER* 'arg_8'    (<class 'type'>, 4)
[31]  10+0x1                       UCHAR 'arg_C'    (<class 'int'>, 1)
      11+0x3                                        [None, 3]
[32]  14+0x4                      KIRQL* 'arg_10'   (<class 'type'>, 4)
[33]  18+0x4                      KIRQL* 'arg_14'   (<class 'type'>, 4)

Python>pp(db.get.struc(h(), func.frame()))
{' r': array('B', [132, 151, 9, 0]),
 ' s': array('B', [0, 133, 246, 15]),
 'Entry': 1354806799,
 'Irp': 475958103,
 ...
 'var_6': 255,
 'var_7': 133,
 'var_8': 196,
 'var_C': 2106194059}

When dealing with frames, a common operation is to lay them out contiguously in case a calculation need to be performed. This might be useful if a distance between two fields might need to be calculated or perhaps if one is attempting to discover overlapping fields between two different scopes of execution.

This can be done using either the structure.left or structure.right functions depending on whether you wish to lay out your fields and types along the highest or lowest address. By combining this with database.get.structure, this can be used to view what the frame for one function might look like if executing with a different piece of arbitrary data.

Python>[parent] = func.up()

# Get the frame of the parent and the current function.
Python>parent_frame = func.frame(parent)
Python>current_frame = func.frame()

Python>struc.right(0, [current_frame, parent_frame])
[<class 'structure' name='$ F1F974' offset=-0xc0 size=0x88>, <class 'structure' name='$ F203D8' offset=-0x38 size=0x38>]

# These functions actually support various types.
Python>struc.right(0, [current_frame, 8, parent_frame, 'int[4]'])
[<class 'structure' name='$ F1F974' offset=-0xd8 size=0x88>, 0x8, <class 'structure' name='$ F203D8' offset=-0x48 size=0x38>, 'int[4]']

Python>struc.right(0x7fffffffcf80, [current_frame.members, parent_frame.members])
[<class 'structure' name='$ F1F974' offset=0x7fffffffcec0 size=0x88>
     7fffffffcec0+0xc                                        [None, 12]
[0]  7fffffffcecc+0x4                         int 'var_5C'   (<class 'int'>, 4)
[1]  7fffffffced0+0x4                         int 'var_58'   (<class 'int'>, 4)
[2]  7fffffffced4+0x4                         int 'var_54'   (<class 'int'>, 4)
[3]  7fffffffced8+0x4                       void* 'Src'      (<class 'type'>, 4)
[4]  7fffffffcedc+0x4                         int 'var_4C'   (<class 'int'>, 4)
[5]  7fffffffcee0+0x4                         int 'var_48'   (<class 'int'>, 4)
...
     7fffffffcf36+0x2                                        [None, 2]
[30] 7fffffffcf38+0x4  tBASIC_DATA_PACKET_HEADER* 'arg_8'    (<class 'type'>, 4)
[31] 7fffffffcf3c+0x1                       UCHAR 'arg_C'    (<class 'int'>, 1)
     7fffffffcf3d+0x3                                        [None, 3]
[32] 7fffffffcf40+0x4                      KIRQL* 'arg_10'   (<class 'type'>, 4)
[33] 7fffffffcf44+0x4                      KIRQL* 'arg_14'   (<class 'type'>, 4), <class 'structure' name='$ F203D8' offset=0x7fffffffcf48 size=0x38>
     7fffffffcf48+0x10                                       [None, 16]
[0]  7fffffffcf58+0x4                 PKSPIN_LOCK 'var_14'   (<class 'type'>, 4)
[1]  7fffffffcf5c+0x4                 PKSPIN_LOCK 'SpinLock' (<class 'type'>, 4)
[2]  7fffffffcf60+0x4                    SEQ_TYPE 'var_C'    (<class 'int'>, 4)
[3]  7fffffffcf64+0x4           tADDRESS_CONTEXT* 'var_8'    (<class 'type'>, 4)
     7fffffffcf68+0x2                                        [None, 2]
[4]  7fffffffcf6a+0x1                       KIRQL 'var_2'    (<class 'int'>, 1)
[5]  7fffffffcf6b+0x1                       KIRQL 'NewIrql'  (<class 'int'>, 1)
[6]  7fffffffcf6c+0x4                     char[4] ' s'       [(<class 'int'>, 1), 4]
[7]  7fffffffcf70+0x4                     char[4] ' r'       [(<class 'int'>, 1), 4]
[8]  7fffffffcf74+0x4                    SEQ_TYPE 'arg_0'    (<class 'int'>, 4)
[9]  7fffffffcf78+0x4  tBASIC_DATA_PACKET_HEADER* 'arg_4'    (<class 'type'>, 4)
[10] 7fffffffcf7c+0x4                         int 'arg_8'    (<class 'int'>, 4)]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.