Welcome to the Sark Documentation!¶
Sark (named after the notorious Tron villain) is an object-oriented scripting layer written on top of IDAPython. Sark is easy to use and provides tools for writing advanced scripts and plugins.
Sark actively supports IDA7.4 and Python3. For older versions, see Support.
Getting Started¶
Install Sark from the command line:
pip3 install -U git+https://github.com/tmr232/Sark.git#egg=Sark
Import inside IDA, and start having fun!
import sark
import idaapi
# Get the current function
func = sark.Function()
# Print all lines in the function
for line in func.lines:
idaapi.msg("{}\n".format(line))
# Mark all the lines containing xrefs outside the function
for xref in func.xrefs_from:
sark.Line(xref.frm).color = 0x8833FF
Table of Contents¶
Contents:
Introduction¶
Even with books like Alexander Hanel’s The Beginner’s Guide to
IDAPython, writing IDA scripts
still remains a daunting task. The need to dive into the IDA SDK’s
header files (all 54 of them), read idaapi.py
, idc.py
and
idautils.py
, and preferably some existing plugins as well, wards off
many researchers and keeps the script & plugin writing community small.
Being a researcher myself, I wanted to make scripting IDA a bit easier and more intuitive. I wanted to spend the majority of my (scripting) time writing code (be it in a code editor or an interactive shell) and not reading someone else’s (I prefer spending my reading efforts on assembly.) So I created Sark.
Sark, (named after the notorious Tron villain,) is an object-oriented scripting layer written on top of IDAPython to provide ease of use, as well as additional tools for writing advanced scripts and plugins.
This tutorial will show you the basics of Sark, to get you started right away.
Installation¶
For Sark Users¶
To get the bleeding edge version, use:
pip3 install -U git+https://github.com/tmr232/Sark.git#egg=Sark
For older versions of IDA (7.3 or lower), use:
pip2 install -U git+https://github.com/tmr232/Sark.git@IDA-6.x#egg=Sark
And see Support
Alternatively, you can install Sark directly from PyPI. The Sark version will match the IDA versions, more or less. So for 7.4 or higher, you’ll just want the latest:
pip3 install sark
But for 7.3 or earlier, you’ll want a version smaller than 7.4:
pip2 install "sark<7.4"
That said, installing from the repo is the only way to be sure you get the latest version of Sark.
To install the IDA Plugins (optional) download the entire repository from GitHub and read Installing Plugins.
Updates¶
To update Sark to the latest version, just run the installation command again.
For Sark Developers¶
If you want to help in the development of Sark, follow this.
Clone the Sark repository to get the latest version
git clone https://github.com/tmr232/Sark.git && cd Sark
pip3 install -e .
Updates¶
To update Sark to the latest version (including all installed codecs and plugins) simply pull the latest version from the repo
git pull
API¶
Tutorial Conventions¶
IDA IPython is used in the examples unless stated otherwise. For brevity, assume the following code precedes any example code.
import idaapi, idc, idautils
import sark
As Sark is a large, evolving library, the API documentation provided here will be partial and only include what is needed to get you started. However, the Sark code in itself is heavily documented.
Lines¶
Lines are the most basic and intuitive object in Sark. A line in the IDA-View is a line in Sark. Let’s have a look.
>>> my_line = sark.Line() # Same as `sark.Line(ea=idc.here())`
>>> print(my_line)
[00417401] mov ebp, esp
>>> my_line.comments.regular = "The line at 0x{:08X}".format(my_line.ea)
>>> print(my_line)
[00417401] mov ebp, esp ; The line at 0x00417401
The sark.Line
object encapsulates most of the line-relevant
functions of IDAPython. Some examples include:
Member | Usage |
---|---|
ea | line’s address |
comments | line comments |
name | the name of the line (if any) |
insn | assembly instruction |
xrefs_to | cross references to the line |
xrefs_from | cross references from the line |
bytes | the actual bytes in the line |
For the rest, I suggest reading the highly documented code, or using the
interactive shell to experiment with the sark.Line
object.
The line object contains 4 notable members: comments
, insn
and
the xrefs_*
pair.
Line Comments¶
The comments
member provides access to all comment types: - Regular
comments - Repeating comments - Anterior lines - Posterior lines
It allows you to get, as well as set comments. Each change to the comments will cause the UI to refresh.
>>> anterior = my_line.comments.anterior
>>> my_line.comments.regular = "My Regular Comment"
Line Xrefs¶
Provide access to Xref
objects describing the line’s cross
references. Xref
objects will be discussed later under Xrefs.
Instructions¶
Provide access to the line’s instructions, down to the single operand.
Instruction
objects will be discussed later under Instructions.
Getting Lines¶
There are several ways to get lines. Either directly or from other objects.
Method | Effect |
---|---|
A Single Line | |
sark.Line() |
Get the current line |
sark.Line(ea=my_address) |
Get the line at the given address |
sark.Line(name=some_name) |
Get the line with the given name |
Multiple Lines | |
sark.lines() |
Iterate all lines in the IDB |
sark.lines(start=start_ea, end=end_ea) |
Iterate all lines between start_ea and end_ea |
sark.lines(selection=True) |
Iterate all lines in current selection |
sark.lines(reverse=True) |
Iterate lines in reverse order |
Objects that contain lines, such as functions and code blocks, can
return their own set of lines. See sark.Function().lines
for an
example.
Functions¶
Functions are another basic object in Sark. Each one provides access to a single function in IDA.
>>> my_func = sark.Function() # The same arguments as `sark.Line`
>>> print(my_func)
Function(name="sub_417400", addr=0x00417400)
>>> my_func.name = "my_func"
>>> print(my_func)
Function(name="my_func", addr=0x00417400)
>>> for line in my_func.lines:
... print(line.disasm)
push ebp
mov ebp, esp
sub esp, 0DCh
push ebx
push esi
.
.
.
Like the sark.Line
objects, they encapsulate relevant API into a
single object. Some useful members are:
All similarly named members between sark.Line
and sark.Function
work similarly as well to avoid confusion.
Getting Functions¶
There are 2 ways to get functions:
- Using the
sark.Function
class, which accepts the same arguments assark.Line
; - Using
sark.functions
to iterate over functions. It is the same assark.lines
, but does not accept areverse
argument.
Footnotes
[1] | Xrefs from a function include only references with a target outside the function. So recursion will be ignored. |
Xrefs¶
Cross references are a core concept in IDA. They provide us with links between different objects and addresses throughout an IDB.
>>> for xref in sark.Line().xrefs_from:
... print(xref)
<Xref(frm=0x0041745B, to=0x0041745D, iscode=1, user=0, type='Ordinary_Flow')>
<Xref(frm=0x0041745B, to='loc_4174A4', iscode=1, user=0, type='Code_Near_Jump')>
>>> for xref in sark.Line().xrefs_from:
... if xref.type.is_jump:
... print(xref)
<Xref(frm=0x0041745B, to='loc_4174A4', iscode=1, user=0, type='Code_Near_Jump')>
Sark xrefs are pretty compact objects:
Member | Usage |
---|---|
frm | xref source address |
to | xref destination address |
iscode | is code xref |
user | is user defined xref |
type | XrefType object |
XrefType¶
To make querying the type of the xref as easy as possible, the
XrefType
object was created:
Member | Usage |
---|---|
name | a string representing the type, mainly for display |
type | the numeric type constant, as per IDA SDK |
is_call | is the xref a call |
is_jump | is the xref a jump |
is_* | predicates to check if a specific type applies. Includes all xref types. |
Usage is quite simple and looks like plain English (of sorts):
>>> if xref.type.is_jump:
... print("xref is jump.")
Getting Xrefs¶
Xrefs can be retrieved from lines or functions. Both objects have
xrefs_from
and xrefs_to
properties that allow retrieval of the
relevant xrefs.
Instructions¶
As promised - we arrive to discuss the instruction objects. Instruction objects represent the actual assembly code of each line.
>>> line = sark.Line()
>>> insn = line.insn
>>> print(line)
[00417555] mov ecx, [eax+8]
>>> print(insn.mnem)
mov
>>> print(insn.operands)
[<Operand(n=0, text='ecx')>, <Operand(n=1, text='[eax+8]')>]
Out of their members,
Member | Usage |
---|---|
operands | list of operands |
mnem | opcode mnemonic |
has_reg | is a reg used in the instruction |
regs | the registers used in the instruction |
Instruction.operands
is the most interesting one.
Operands¶
Each operand provides the means to analyze individual operands in the code.
>>> print(insn.operands[1])
<Operand(n=1, text='[eax+8]')>
>>> print("{0.reg} + {0.offset}".format(insn.operands[1]))
eax + 8
Member | Usage |
---|---|
n | operand index in instruction |
type | numeric type a-la IDA SDK |
size | data size of the operand |
is_read | is the operand read from |
is_write | is the operand written to |
reg | the register used in the operand |
text | the operand text, as displayed in IDA |
base | the base register in an
address-phrase of the form
[base + index * scale + offset] |
index | the index register in a phrase |
scale | the scale in a phrase |
offset | the offset in a phrase |
Getting Instructions¶
The best way to retrieve instruction objects is using the .insn
member of sark.Line
.
Code Blocks¶
If you ever looked at a function in the Graph-View, you know what code blocks are. They are the nodes in the function graph, sometimes referred to as a flowchart.
>>> block = sark.CodeBlock()
>>> print(list(block.next))
[<CodeBlock(start_ea=0x00417567, end_ea=0x00417570)>,
<CodeBlock(start_ea=0x0041759E, end_ea=0x004175D4)>]
Sark’s CodeBlock
object inherits from the idaapi.BasicBlock
objects, and adds a few handy members.
Member | Usage |
---|---|
lines | the lines in the block, as a generator |
next | successor nodes, as a generator |
prev | predecessor nodes, as a generator |
color | the background color of the node |
These members allow for easy traversal and analysis of nodes in a graph.
FlowChart¶
Sark’s flowchart, inheriting from idaapi.FlowChart
, is in every way
the same except for returning Sark CodeBlock
objects instead of
idaapi.BasicBlock
ones. It can be used to quickly fetch all the
blocks in a function graph.
Getting Codeblocks¶
Codeblocks are created using the sark.CodeBlock(ea)
class.
Flowcharts can be retrieved using the sark.FlowChart(ea)
class accordingly.
In some cases, you may want to go over more than one function. In those
cases, you can use the sark.codeblocks(start=None, end=None, full=True)
function.
The full
parameter controls the way the blocks are generated. With full=True
,
FlowChart
objects are generated per function, yielding fully capable CodeBlock
objects. With full=False
, a single FlowChart
is generated for the entire
address range. This results in faster iteration, but since the blocks are not associated
to their containing functions, it is not possible to get or set block colors (line color
will change, though.)
Advanced Usage¶
Since the function flowchart is actually a graph, it makes sense to use
it as one. To ease you into it, the sark.get_nx_graph(ea)
function
was added.
>>> sark.get_nx_graph(idc.here())
<networkx.classes.digraph.DiGraph at 0x85d6570>
The function returns a NetworkX
DiGraph
object representing the flowchart, with each node being the
start_ea
of a matching block. Using NetworkX’s functionality, it is
easy to trace routes in the graph.
>>> import networkx as nx
>>> func = sark.Function()
>>> graph = sark.get_nx_graph(func.ea)
>>> start_address = sark.get_block_start(func.start_ea) # The `get_block_start(ea)` is short for `get_codeblock(ea).start_ea`
>>> end_address = sark.get_block_start(func.end_ea - 1) # Remember, `end_ea` is outside the function!
>>> path = nx.shortest_path(graph, start_address, end_address)
>>> print("From {} to {}".format(hex(start_address), hex(end_address)))
From 0x417400L to 0x4176a6L
>>>print " -> ".join(map(hex, nx.shortest_path(graph, start, end)))
0x417400L -> 0x41745dL -> 0x417483L -> 0x417499L -> 0x4176a6L
Segments¶
Though not as popular as functions and lines, IDA segments include both. In Sark, Segment
objects allow access to underlying Function
and Line
objects.
>>> #
>>> # Reference Lister
>>> #
>>> # List all functions and all references to them in the current section.
>>> #
>>> # Implemented with Sark
>>> #
>>> # See reference implementation here: https://code.google.com/p/idapython/wiki/ExampleScripts
>>> #
>>> for function in sark.Segment().functions:
>>> print("Function %s at 0x%x" % (function.name, function.ea))
>>> for ref in function.crefs_to:
>>> print(" called from %s(0x%x)" % (sark.Function(ref).name, ref))
Like the sark.Line
objects, they encapsulate relevant API into a
single object. Some useful members are:
All similarly named members between sark.Line
and sark.Segment
work similarly as well to avoid confusion.
Getting Segments¶
There are 2 ways to get segments:
- Using the
sark.Segment
object, using an address in a segment, a segment name, or the index of a segment. - Using
sark.segments
to iterate over segments.
Switch¶
The switch-case is a common construct in compiled code, and IDA is doing a great job at analyzing it.
>>> switch = sark.Switch(idc.here())
>>> for case, target in switch:
... print("{} -> 0x{:08X}".format(case, target))
0 -> 0x004224C9
1 -> 0x0042249F
2 -> 0x0042251B
3 -> 0x0042251B
4 -> 0x00422475
5 -> 0x0042251B
6 -> 0x0042251B
7 -> 0x0042251B
8 -> 0x004224F3
9 -> 0x0042251B
10 -> 0x0042251B
11 -> 0x00422448
It provides the following members
Member | Usage |
---|---|
targets | switch target addresses |
cases | switch case values |
pairs | iterator of (case, target) pairs |
get_cases | get the cases matching a target |
The sark.Switch
object is similar to a Python dict
, mapping
cases to targets. switch[case]
returns the relevant target, and
iteration returning the cases.
Getting Switches¶
To check if an address is a switch address, use sark.is_switch(ea)
.
To get the switch, use sark.Switch(ea)
.
Enums¶
Enums in IDA are a great way to name numbers and bit-values for easier reading.
>>> for enum in sark.enums():
... print("{}:".format(enum.name))
... for member in enum.members:
... print(" {:<30} = {}".format(member.name, member.value))
... print()
POOL_TYPE:
NonPagedPool = 0
PagedPool = 1
NonPagedPoolMustSucceed = 2
DontUseThisType = 3
NonPagedPoolCacheAligned = 4
PagedPoolCacheAligned = 5
NonPagedPoolCacheAlignedMustS = 6
MaxPoolType = 7
CREATE_FILE_TYPE:
CreateFileTypeNone = 0
CreateFileTypeNamedPipe = 1
CreateFileTypeMailslot = 2
The Sark Enum
object provides the following members:
Member | Usage |
---|---|
name | the enum name |
comments | enum comments, similar to line comments |
eid | the enum-id of the enum |
bitfield | is the enum a bitfield |
members | the enum member constants |
Using the Enum
object you can easily enumerate and manipulate enums
in IDA.
Enum Members¶
The .members
member of sark.Enum
returns a members object. The
members object allows easy enumeration and manipulation of the members:
>>> my_enum = sark.add_enum("MyEnum")
>>> my_enum.members.add("first", 0)
>>> my_enum.members.add("second", 1)
>>> my_enum.members.add("third", 2)
>>> my_enum.members.remove("second")
>>> for member in my_enum.members:
... print("{} = {}".format(member.name, member.value))
first = 0
third = 2
Each member provides the following:
Member | Usage |
---|---|
name | the member name |
value | the member value |
comments | the member comments |
enum | the containing enum |
Getting Enums¶
There are several ways to get an enum. All are summed in the following table:
Code | Explanation |
---|---|
sark.enums() |
iterate all the enums in the IDB |
sark.Enum("EnumName") |
get an existing enum by name |
sark.Enum(eid=enum_id) |
get an enum using a known id |
sark.add_enum("NewEnumName") |
create a new enum |
IDB Graphs¶
Earlier we discussed codeblock graphs inside functions. Another interesting graph is the call graph connecting all the functions.
As we have already played with graphs earlier, we will not delve into the details.
Getting IDB Graphs¶
To get an IDB graph, use sark.graph.get_idb_graph()
. The function traverses
all xrefs from and to all functions to create a graph of the IDB, with
each node being the address of a function’s start_ea
.
UI¶
Sark provides some basic utilities and wrappers for IDA’s UI.
NXGraph¶
A natural extension to creating and analyzing graphs, is plotting them.
IDA provides a generic API via the idaapi.GraphViewer
interface. As
Sark mainly uses NetworkX digraphs, the sark.ui.NXGraph
class has
been created to provide an easy plotting solution.
>>> viewer = sark.ui.NXGraph(graph, title="My Graph", handler=sark.ui.AddressNodeHandler())
>>> viewer.Show()
The NXGraph
constructor takes several arguments:
Argument | Desctription |
---|---|
graph | the graph to plot |
title | (opt.) title for the graph |
handler | (opt.) a default handler for nodes |
padding | (opt.) visual padding of nodes |
After an NXGraph
is created, use .Show()
to display it.
Node Handlers¶
To allow different types of node data, NXGraph
uses node handlers.
Node handlers inherit from sark.ui.BasicNodeHandler
and implement
the callbacks required for them (all are optional).
Callback | Usage |
---|---|
on_get_text | returns the text to display for the node |
on_click | handles a click on the node. Return True to set the cursor on it. |
on_double_click | same as on_click |
on_hint | the hint to show |
on_bg_color | returns the background color for the node |
on_frame_color | returns the frame (border) color for the node |
There are 2 existing handlers you can use.
Handler | Info |
---|---|
BasicNodeHandler |
The most basic handler. Calls str to get node text, and nothing else. This is the default handler for NXGraph . |
AddressNodeHandler |
Assumes all nodes are IDB addresses. For node text, it shows the address’ name if it exists, or a hex address otherwise. On double click, it jumps to the clicked address. |
Miscellaneous¶
Sark also has a lot of functionality outside of the core objects.
Function | Description |
---|---|
sark.fix_addresses(start=None, end=None) |
returns a start, end pair, where None is replaced with the start-address and end-address of the IDB accordingly |
sark.is_same_function(ea1, ea2) |
checks if the addresses are in the same function |
sark.get_name_or_address(ea) |
returns the name of the address if it exists. Otherwise a hex representation is returned |
Examples¶
Capture Huge Screenshots¶
Code¶
import sark.qt
widget = sark.qt.get_widget("IDA View-A")
sark.qt.resize(widget, 7000, 18000)
# Move the view about a bit to capture the entire function
sark.qt.capture_widget(widget, "huge-screenshot.png")
# Crop the image to remove extra background.
Plotting a Call Graph¶
Usage¶
Using Windows 8.1 http.sys
. Before MS15-034
.
draw_call_graph(sark.Function(name="_UlpParseRange@32").ea, to=True, distance=4)
Code¶
import sark
import networkx as nx
def draw_call_graph(ea, distance=2, to=False):
# First, get the IDB graph (caching it might be a good idea
# as this operation can be time consuming on large IDBs)
idb_graph = sark.graph.get_idb_graph()
# Get the address of the function to use in the graph
func_ea = sark.Function(ea).ea
# Create the call graph
if to:
# If we want the calls to our function, we need to reverse
# the graph
idb_graph = idb_graph.reverse()
# Use NetworkX to limit the IDB graph
call_graph = nx.ego_graph(idb_graph, func_ea, distance)
# Paint the root node red
call_graph.node[func_ea][sark.ui.NXGraph.BG_COLOR] = 0x80
if to:
# If we reversed it before, we need to reverse it again
# to make the links point the right way
call_graph = call_graph.reverse()
# Create an NXGraph viewer
viewer = sark.ui.NXGraph(call_graph, handler=sark.ui.AddressNodeHandler())
# Show the graph
viewer.Show()
Plugins¶
Installing Plugins¶
The IDA Way¶
IDA provides a single way to install plugins - stick them in the plugins
subdirectory and you’re good to go.
While this is great for compiled plugins, as your build scripts can place the newly compiled plugin there for you, it
is not as comfortable when using scripted plugins. Forgetting to copy the latest version, or updating the code in the
plugins
directory instead of your repository can both lead to annoying problems and waste precious time.
Moreover, access to the plugins
directory requires root access.
The Sark Way¶
To combat the limitations of IDAs plugin loading mechanism, Sark provides the plugin_loader.py
plugin.
Once installed (in the classic IDA way) it allows you to define plugin-lists - a system-wide list and a user-specific
list - to be loaded automatically.
The lists are simple, consisting of full-paths and line-comments:
C:\Plugins\my_plugin.py
# This is a comment. Comments are whole lines.
C:\OtherPlugins\another_plugin.py
Both lists are named plugins.list
and are automatically created by IDA as empty lists at the following locations:
- System-Wide
- Under IDA’s
cfg
subdirectory. The path can be found usingidaapi.idadir(idaapi.CFG_SUBDIR)
. This list requires root access to modify as it is in IDA’s installation directory. - User-Specific
- Under IDA’s user-directory.
$HOME/.idapro
on Linux,%appdata/%HexRays/IDA Pro
on Windows. The path can be found usingidaapi.get_user_idadir()
. Each user can set his own plugins to load, thus eliminating the need for root access.
To install your plugins, just add them to one of the lists. This allows you to easily update plugins as you go without ever needing to copy them.
Show Meaningful¶
When reversing an executable, we often need to deal with a large amount of unknown code. To combat this, we usually look for strings and library functions, and use them as guides as we interpret the code. When those are ample, we hardly need to look at the assembly code to infer meaning. On the other hand, the need to constantly jump into functions, pan them around to see all the strings, then jump back out is quite time consuming and confusing.
Well, no more!
The “Meaningful” plugin allows you to get all the information you need with a simple hotkey.
Usage¶
Whenever inside a function, just press Alt + 0
to get a table of all the meaningful objects in it:
Since the output is at the Output window, a double click on an address will take you to it.
To make things even more agile, you can press Ctrl + Alt + 0
whenever on a line referencing a function
to get the values displayed for that function:
Quick Copy¶
The quick copy allows you to easily copy data from the IDB.
Usage¶
Copying Addresses¶
To copy the address of the current line, just press Ctrl + Alt + C
. It will get copied as a hex number, prefixed
with 0x
.
To copy the file offset of the current line, simply press Ctrl + Alt + O
. It will get copied as a hex
number (like line address copying), prefixed with 0x
.
Copying Bytes¶
Pressing Ctrl + Shift + C
copies the bytes of the current line or selection.
Copying the current line:
Will result in 8b 4d 08
, while copying a selection:
Will result in 8b 4d 08 8b 51 14
.
Autostruct¶
Creating and applying structs in IDA can be quite a hassle.
Go to the structure window, create a structure, define members at specific offsets, go to the disassembly to apply them, go back to the structure view to correct errors, apply other members… And on and on it goes.
The Autostruct plugin does all this work for you, without ever having to leave the IDA view. It automatically creates structs, defines member offsets, and applies them in the disassembly view.
Usage¶
- Select a line (or lines) containing struct references
- Press
Shift + T
- Set the struct name
- Choose the register holding the struct base for the selected code. Autostruct will automatically suggest the most likely candidate in the selection.
At this point, Autostruct will try and create a new struct, populate it with relevant offsets, and apply it to the selection.
- If a structure of the given name already exists, you will need to select whether to modify the existing structure, apply without modification, or cancel.
- Struct creation and modification happen seamlessly
Known Issues¶
- Misaligned Member Creation
- When attempting to create a member at an offset belonging to another member, Autostruct will fail. This usually happens when a previous definition was incorrect (wrong member size) or when members are unions. At this point, manual handling (redefining the large member as a smaller one) is required.
Xrefs Graph¶
The Xrefs-Graph is used to easily generate interactive xref graphs.
Usage¶
Anywhere within the IDA-View, just right-click [1], and select the desired option:
In the popup dialog, enter the distance (recursion level) desired from the source:
Once you press OK
, the plugin will generate an interactive xrefs graph:
A double-click on any block will take you to the relevant address. Also, names in the blocks will be updated as you rename functions.
Known Issues¶
- Node Groups
- While creation of node groups is possible via IDA’s GUI, it is not presently supported in the plugin. Creation of node groups may cause unpredictable errors.
Footnotes
[1] | In IDA 6.6 or earlier, use View/Graph/Xrefs from source or View/Graph/Xrefs to source , as context
menus cannot be augmented. |
Debugging IDAPython Scripts¶
While IDAPython is extremely useful, it can be a bit of a hassle to debug IDA Pro plugins. This tutorial will give get you started on debugging IDAPython scripts and plugins using Python Tools for Visual Studio.
And yes, it is completely free.
The Setup¶
For this tutorial, we will be using the following software:
Python Tools for Visual Studio, documentation can be found here.
Python’s
ptvsd
module. Install usingpip install ptvsd
.The following plugin:
# filename: ptvsd_enable.py import idaapi import ptvsd try: # Enable the debugger. Raises exception if called more than once. ptvsd.enable_attach(secret="IDA") except: pass class DebugPlugin(idaapi.plugin_t): flags = idaapi.PLUGIN_FIX comment = "PTVSD Debug Enable" help = "Enable debugging using PTVSD" wanted_name = "PTVSD" wanted_hotkey = "" def init(self): return idaapi.PLUGIN_KEEP def term(self): pass def run(self, arg): pass def PLUGIN_ENTRY(): return DebugPlugin()
For the purposes of this tutorial, you can try and debug this plugin:
# filename: sample_debuggee.py
import idaapi
def my_debugged_function():
# Set breakpoint here!
pass
class SamplePlugin(idaapi.plugin_t):
flags = idaapi.PLUGIN_PROC
comment = "Sample Debuggee"
help = "Sample Debuggee"
wanted_name = "Sample Debuggee"
wanted_hotkey = "Shift+D"
def init(self):
return idaapi.PLUGIN_KEEP
def term(self):
pass
def run(self, arg):
my_debugged_function()
def PLUGIN_ENTRY():
return SamplePlugin()
Debugging¶
Put
ptvsd_enable.py
(provided above) in IDA’s plugins directory. If you want to use the sample debuggee, put it in the plugins directory as well.Start IDA and load an IDB (otherwise weird issues arise)
Load the code you want to debug into Visual Studio and set breakpoints.
In Visual Studio (with the plugin file open), use
DEBUG->Attach to process
In the dialog, select
idaq.exe
and clickAttach
We are now attached. Once a breakpoint is hit, Visual Studio will break and let you debug.
Have fun debugging!
Important Notes¶
- When debugging (breaking and stepping), IDA will be frozen.
- Load your IDB prior to attaching the debugger.
- For easy debug-on-demand, keep
ptvsd_enable.py
in IDA’s plugins directory at all times. - To set breakpoints, make sure you load into VS the files that are actually loaded by IDA.
If you find any issues with the tutorial, please submit them here.
Support¶
IDA 7.4 & Python 3¶
Those versions are under active support and expected to receive updates.
IDA 7.4 made 2 significant changes that affect support
- Python 3 support
- Python 2: 6.95 compatibility APIs OFF by default
This means that all code breaks by default - be it Python 2 or 3. While adapting Sark to the new APIs & Python 3 I concluded that actively maintaining & developing multiple (IDA 7.4 and lower, Python 2 & 3) versions incurs an overhead that is too high for me. As a result - Sark will only support Python 3 on IDA 7.4. Older versions of IDA will get bugfixes. IDA 7.4 with Python 2 will receive no support.
If you need Python 2 support on IDA 7.4, please contact me or create an issue on this repo. I am not opposed to adding bugfix-based support.
Older Versions¶
Older versions of IDA (7.3 and lower) using Python 2 are still supported for bugfixes.
The older version is maintained on the IDA-6.x branch.
Installation of the older version follows the same flow as before:
pip2 install -U git+https://github.com/tmr232/Sark.git@IDA-6.x#egg=Sark
To develop locally, clone the repo & check-out the IDA-6.x
branch.
How To Contribute¶
The Sark project was created to provide an intuitive, easy to use scripting layer for IDA Pro. If something seems like the right-way to do something, it should probably be added to Sark.
If you have something you think is worth adding, either submit a new issue or (preferably) create a pull-request.
When contributing, try and follow these guidelines:
- Add yourself to the AUTHORS.rst file in alphabetical order. Every contribution shall be credited.
- Each new feature must have a reproducible test-case or usage. If it can’t be used, it will not get in.
- Obey PEP 8 and PEP 257.
- All code should be documented. Usage samples and references to the IDASDK headers are a bonus.
- Write meaningful commit messages.
- No change is too small. You are welcome to fix typos, convention violations, or plain ugly code. All contributions are welcome.
- When submitting a fix to a bug, describe the bug being fixed. Include both the bug and the desired results. Creating an issue for the bug is good practice.
- When reporting bugs, make sure you mention your OS and IDA version.
- Sark has tests (though the coverage is bad…). When adding features, it is advisable to add matching tests.
If you can’t adhere to the guidelines, submit your pull requests anyway. Maybe someone else can improve on it.
Thanks for contributing!