Skip to content
This repository was archived by the owner on Dec 24, 2025. It is now read-only.

Commit 3ce5b7f

Browse files
Arnaud DiederenArnaud Diederen
authored andcommitted
IDAPython for IDA 7.7
1 parent 839d93a commit 3ce5b7f

167 files changed

Lines changed: 278481 additions & 206565 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api_contents2.txt

Lines changed: 208 additions & 81 deletions
Large diffs are not rendered by default.

api_contents3.txt

Lines changed: 262 additions & 123 deletions
Large diffs are not rendered by default.

docs/bc695.md

Lines changed: 0 additions & 46 deletions
This file was deleted.

docs/bc695.sh

Lines changed: 0 additions & 11 deletions
This file was deleted.

docs/inject_pydoc.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
2+
# inject_pydoc.py
3+
4+
This tool is in charge extracting information from the C++ SDK
5+
headers, and inject it into IDAPython's documentation.
6+
7+
## Extracting the C++ SDK information/documentation
8+
9+
The IDAPython build system will run `doxygen` against the SDK headers,
10+
asking it to output all the information it could extract, as `XML`
11+
format for later use - by `inject_pydoc.py`, but not only…
12+
13+
## Applying the documentation
14+
15+
Then, we run `tools/inject_pydoc.py`, to process that `XML`
16+
content and extract documentation about the functions, classes,
17+
methods, variables, etc… that are present in the corresponding
18+
IDAPython module.
19+
20+
## Fixing the input parameters
21+
22+
We cannot blindly apply the SDK documentation, however: some C++
23+
function parameters will turn into output values when converted to
24+
Python, and thus it makes no sense to have those parameters as part of
25+
the IDAPython function documentation. For example, when wrapping
26+
27+
inline void get_registered_actions(qstrvec_t *out)
28+
29+
into `ida_kernwin.get_registered_actions`, the `out` parameter will
30+
turn into a `list(str)`, so it makes no sense to see the corresponding
31+
C++ header's documentation:
32+
33+
/// \param out the list of actions to be filled
34+
35+
into the IDAPython documentation.
36+
37+
Fortunately, we can rely on SWiG's help to do that, because even
38+
though SWiG doesn't know how to import C++ header's documentation, it
39+
*will* tell us what parameters are present in the wrapped prototype.
40+
41+
Therefore, we collect the SWiG-generated list of parameters from the
42+
prototype, and simply remove the non-relevant bits from the C++
43+
header's documentation before injecting that into IDAPython.
44+
45+
## Fixing the return values
46+
47+
Because all of that was too easy, IDAPython adds another layer of
48+
complexity (craziness?) for fixing the return values.
49+
50+
When it comes to the input parameters, it's actually _fairly_ easy to
51+
drop the irrelevant bits from the C++ SDK header's documentation: we
52+
know what parameters SWiG will keep & wrap.
53+
54+
But for the return values, it's another story entirely: SWiG doesn't
55+
know (and cannot always reliably know) what type a return value will
56+
have.
57+
In particular when it comes to custom code in `pywraps/` that returns
58+
a `PyObject *`.
59+
60+
Therefore, we have put a mechanism into place, that lets us put a
61+
"wrapper" around _all_ IDAPython functions/class methods, that will
62+
trace/keep track of the types of the values that were passed in, and
63+
the types of the values that were spit out by those functions.
64+
65+
We can then use that wrapper when running tests, collect information
66+
from the tests that were run, process that information, and save it
67+
into `tools/collected_traces.txt`, which will then be used by
68+
`inject_pydoc.py`.
69+
70+
It's worth pointing out that that information is not, and could not
71+
possibly, be generated at build-time: it must be done at another time
72+
(e.g., after running tests), which is very different than the
73+
mechanism used for fixing the parameters (that "simply" relies on the
74+
`doxygen`-parsed `XML`-formatted documentation.)
75+

docs/pydoc.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
# IDAPython runtime documentation
3+
4+
We define the "IDAPython runtime documentation" as the documentation
5+
that's available through Python's `help()` system, while the user is
6+
interacting with `IDAPython` during an IDA session.
7+
8+
That documentation is SWiG-generated + patched, or custom written.
9+
10+
## Custom-written documentation
11+
12+
Some functions have custom-written documentation (check for <pydoc>
13+
tags in the `pywraps/` directory), but the general case is that the
14+
documentation is automatically generated by SWiG, and then patched.
15+
16+
## SWiG-generated + patched documentation
17+
18+
Because SWiG doesn't know about the C++ SDK header's documentation, we
19+
need a way to "import" that documentation into IDAPython.
20+
21+
That's done by `tools/inject_pydoc.py` which deserves
22+
[its own documentation](inject_pydoc.md)

examples/core/colorize_disassembly.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@
55
This illustrates the setting/retrieval of background colours
66
using the IDC wrappers
77
8+
In order to do so, we'll be assigning colors to specific ranges
9+
(item, function, or segment). Those will be persisted in the
10+
database.
11+
812
category: disassembly
913
1014
keywords: coloring, idc
15+
16+
see_also: colorize_disassembly_on_the_fly
1117
"""
1218

1319
from __future__ import print_function
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
"""
2+
summary: an easy-to-use way to colorize lines
3+
4+
description:
5+
This builds upon the `ida_kernwin.UI_Hooks.get_lines_rendering_info`
6+
feature, to provide a quick & easy way to colorize disassembly
7+
lines.
8+
9+
Contrary to @colorize_disassembly, the coloring is not persisted in
10+
the database, and will therefore be lost after the session.
11+
12+
By triggering the action multiple times, the user can "carousel"
13+
across 4 predefined colors (and return to the "no color" state.)
14+
15+
keywords: coloring
16+
17+
see_also: colorize_disassembly
18+
"""
19+
20+
import ida_kernwin
21+
import ida_moves
22+
23+
class on_the_fly_coloring_hooks_t(ida_kernwin.UI_Hooks):
24+
25+
# We'll offer the users the ability to carousel around the
26+
# following colors. Well, note that these are in fact not
27+
# colors, but rather color "keys": each theme might have its
28+
# own values for those.
29+
AVAILABLE_COLORS = [
30+
ida_kernwin.CK_EXTRA5,
31+
ida_kernwin.CK_EXTRA6,
32+
ida_kernwin.CK_EXTRA7,
33+
ida_kernwin.CK_EXTRA8,
34+
]
35+
36+
def __init__(self):
37+
ida_kernwin.UI_Hooks.__init__(self)
38+
39+
# Each view can have on-the-fly coloring.
40+
# We'll store the custom colors keyed on the widget's title
41+
self.by_widget = {}
42+
43+
def get_lines_rendering_info(self, out, widget, rin):
44+
"""
45+
Called by IDA, at rendering-time.
46+
47+
We'll look in our set of marked lines, and for those that are
48+
found, will produce additional rendering information for IDA
49+
to use.
50+
"""
51+
title = ida_kernwin.get_widget_title(widget)
52+
assigned = self.by_widget.get(title, None)
53+
if assigned is not None:
54+
for section_lines in rin.sections_lines:
55+
for line in section_lines:
56+
for loc, color in assigned:
57+
if self._same_lines(widget, line.at, loc.place()):
58+
e = ida_kernwin.line_rendering_output_entry_t(line)
59+
e.bg_color = color
60+
out.entries.push_back(e)
61+
62+
def _same_lines(self, viewer, p0, p1):
63+
return ida_kernwin.get_custom_viewer_place_xcoord(viewer, p0, p1) != -1
64+
65+
def _find_loc_index(self, viewer, assigned, loc):
66+
for idx, tpl in enumerate(assigned):
67+
_loc = tpl[0]
68+
if self._same_lines(viewer, loc.place(), _loc.place()):
69+
return idx
70+
return -1
71+
72+
def carousel_color(self, viewer, title):
73+
"""
74+
This performs the work of iterating across the available
75+
colors (and the 'no-color' state.)
76+
"""
77+
loc = ida_moves.lochist_entry_t()
78+
if ida_kernwin.get_custom_viewer_location(loc, viewer):
79+
assigned = self.by_widget.get(title, [])
80+
new_color = None
81+
82+
idx = self._find_loc_index(viewer, assigned, loc)
83+
if idx > -1:
84+
prev_color = assigned[idx][1]
85+
prev_color_idx = self.AVAILABLE_COLORS.index(prev_color)
86+
new_color = None \
87+
if prev_color_idx >= (len(self.AVAILABLE_COLORS) - 1) \
88+
else self.AVAILABLE_COLORS[prev_color_idx + 1]
89+
else:
90+
new_color = self.AVAILABLE_COLORS[0]
91+
92+
if idx > -1:
93+
del assigned[idx]
94+
if new_color is not None:
95+
assigned.append((loc, new_color))
96+
97+
if assigned:
98+
self.by_widget[title] = assigned
99+
else:
100+
if title in self.by_widget:
101+
del self.by_widget[title]
102+
103+
104+
class carousel_color_ah_t():
105+
"""
106+
The action that will be invoked by IDA when the user
107+
activates its shortcut.
108+
"""
109+
def __init__(self, hooks):
110+
self.hooks = hooks
111+
112+
def activate(self, ctx):
113+
v = ida_kernwin.get_current_viewer()
114+
if v:
115+
self.hooks.carousel_color(v, ctx.widget_title)
116+
return 1 # will cause the widget to redraw
117+
118+
def update(self, ctx):
119+
return ida_kernwin.AST_ENABLE_FOR_WIDGET \
120+
if ida_kernwin.get_current_viewer() \
121+
else ida_kernwin.AST_DISABLE_FOR_WIDGET
122+
123+
124+
ACTION_NAME = "example:colorize_disassembly_on_the_fly"
125+
ACTION_LABEL = "Pick line color"
126+
ACTION_SHORTCUT = "!"
127+
ACTION_HELP = "Press %s to carousel around available colors (or remove a previously-set color)" % ACTION_SHORTCUT
128+
129+
otf_coloring = on_the_fly_coloring_hooks_t()
130+
if ida_kernwin.register_action(ida_kernwin.action_desc_t(
131+
ACTION_NAME,
132+
ACTION_LABEL,
133+
carousel_color_ah_t(otf_coloring),
134+
ACTION_SHORTCUT)):
135+
print("Registered action \"%s\". %s" % (ACTION_LABEL, ACTION_HELP))
136+
otf_coloring.hook()

examples/core/create_structure_programmatically.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
description:
55
Usage of the API to create & populate a structure with
66
members of different types.
7+
8+
author: Gergely Erdelyi (gergely.erdelyi@d-dome.net)
79
"""
810

911
from __future__ import print_function
@@ -12,8 +14,6 @@
1214
#
1315
# This script demonstrates how to create structures and populate them
1416
# with members of different types.
15-
#
16-
# Author: Gergely Erdelyi <gergely.erdelyi@d-dome.net>
1717
#---------------------------------------------------------------------
1818

1919
import ida_struct

examples/core/dump_extra_comments.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import ida_lines
1515
import ida_kernwin
1616

17-
1817
# -----------------------------------------------------------------------
1918
class dump_at_point_handler_t(ida_kernwin.action_handler_t):
2019
def __init__(self, anchor):
@@ -41,25 +40,43 @@ def compose_action_name(v):
4140
return "dump_extra_comments:%s" % v
4241

4342

43+
# --------------------------------------------------------
44+
# action variants
45+
46+
class action_previous_handler_t(dump_at_point_handler_t):
47+
ACTION_LABEL = "previous"
48+
ACTION_SHORTCUT = "Ctrl+Shift+Y"
49+
50+
def __init__(self):
51+
super(action_previous_handler_t, self).__init__(ida_lines.E_PREV)
52+
53+
class action_next_handler_t(dump_at_point_handler_t):
54+
ACTION_LABEL = "next"
55+
ACTION_SHORTCUT = "Ctrl+Shift+Z"
56+
57+
def __init__(self):
58+
super(action_next_handler_t, self).__init__(ida_lines.E_NEXT)
59+
60+
4461
# -----------------------------------------------------------------------
4562
# create actions (and attach them to IDA View-A's context menu if possible)
4663
widget_title = "IDA View-A"
4764
ida_view = ida_kernwin.find_widget(widget_title)
4865

49-
actions_variants = [
50-
("previous", ida_lines.E_PREV, "Ctrl+Shift+Y"),
51-
("next", ida_lines.E_NEXT, "Ctrl+Shift+Z"),
66+
action_variants = [
67+
action_previous_handler_t,
68+
action_next_handler_t,
5269
]
53-
for label, anchor, shortcut in actions_variants:
54-
actname = dump_at_point_handler_t.compose_action_name(label)
70+
for variant in action_variants:
71+
actname = dump_at_point_handler_t.compose_action_name(variant.ACTION_LABEL)
5572
if ida_kernwin.unregister_action(actname):
5673
print("Unregistered previously-registered action \"%s\"" % actname)
5774

5875
desc = ida_kernwin.action_desc_t(
5976
actname,
60-
"Dump %s extra comments" % label,
61-
dump_at_point_handler_t(anchor),
62-
shortcut)
77+
"Dump %s extra comments" % variant.ACTION_LABEL,
78+
variant(),
79+
variant.ACTION_SHORTCUT)
6380
if ida_kernwin.register_action(desc):
6481
print("Registered action \"%s\"" % actname)
6582

0 commit comments

Comments
 (0)