Merge branch 'master' into fix/idp-hook-example

This commit is contained in:
Elias Bachaalany
2023-05-05 21:47:01 -07:00
committed by GitHub
327 changed files with 390487 additions and 219974 deletions
+4 -1
View File
@@ -1,2 +1,5 @@
obj/
*.pyc
*.pyc
idapyswitch*
fuzzer/
.vscode/
+9 -5
View File
@@ -13,11 +13,10 @@ REQUIREMENTS
- IDA and IDA SDK [> 5.6]
http://www.hex-rays.com/idapro/
- Python [2.5.1, 2.6.1, 2.7]
- Python 3.x
http://www.python.org/
- Simplified Wrapper Interface Generator (SWIG)
- if you intend to build for Python 2.x: [4.0.1]
- if you intend to build for Python 3.x: [4.0.1, with support for -py3-limited-api]
Hex-Rays cannot guarantee support for IDAPython
@@ -31,8 +30,12 @@ REQUIREMENTS
On Windows, please refer to the following instructions:
`http://www.swig.org/Doc4.0/Windows.html#Windows_cygwin_mingw`
On Linux or OSX,
(On Ubuntu, you might want to install a couple of packages:
`sudo apt install libpcre3-dev yacc bison automake autotools-dev patchelf -y`)
`sh autogen.sh`, then
`configure --prefix=/my/swig-4.0.1-py3-install && make && make install`
`./configure --prefix=/my_path_to/swig-4.0.1-py3-install && make && make install`
- Unix utilities (GNU patch on Windows):
http://www.research.att.com/sw/tools/uwin/ or
@@ -77,13 +80,14 @@ Note: the path you unpack the IDA SDK into cannot contain white spaces,
Note: If you want to build for Python3 (let's say you are building for Python 3.8),
please set the following environment variables:
- export PYTHON_VERSION_MAJOR=3 ('set PYTHON_VERSION_MAJOR=3' on Windows)
- export PYTHON_VERSION_MAJOR=8 ('set PYTHON_VERSION_MINOR=8 on Windows)
- export PYTHON_VERSION_MINOR=8 ('set PYTHON_VERSION_MINOR=8 on Windows)
4. Build the plugin
python build.py --swig-home /my/swig-4.0.1-py3-install --with-hexrays --idc /path/to/ida74_install/idc/idc.idc
python build.py --swig-home /my_path_to/swig-4.0.1-py3-install --with-hexrays --ida-install /path/to/ida_install/
You can also run 'build.py --help' for more information.
5. Install the components as described in README.md
+2
View File
@@ -53,6 +53,8 @@ so we don't have to do it ourselves.
When it comes to other types of pull requests (e.g., documentation),
it should usually not be necessary to write a test.
See also [the best practices for tests & examples](examples/README.md)
### How to write tests?
+9 -7
View File
@@ -7,9 +7,9 @@ to the C++ SDK which will then be reflected in IDAPython, an *immensely*
useful rule-of-thumb is to perform a diff of the autogenerated SWiG wrappers.
Typically:
`cp -R obj/x64_linux_gcc_32/wrappers/ /tmp/wrappers-before`
`cp -R obj/x64_linux_gcc_32/3/wrappers/ /tmp/wrappers-before`
<recompile...>
`git diff /tmp/wrappers-before/ obj/x64_linux_gcc_32/wrappers/`
`git diff /tmp/wrappers-before/ obj/x64_linux_gcc_32/3/wrappers/`
It is always useful to make sure that SWiG did the right thing -- *especially*
when modifying typemaps, but not only.
@@ -28,17 +28,19 @@ We use the "zzz" placeholder for a module name in this "how-to".
2. add zzz to the `MODULES_NAMES` var in makefile
3. add a line to python/idc.py if you want to autoload this module
3. add zzz to `SDK_FILES` var in etc/sdk/sdk_files.mak
4. add a line to python/idc.py if you want to autoload this module
```
import ida_zzz
```
4. build
5. build
5. update the content of api_contents.txt
6. update the content of api_contents.txt
(from obj/.../api_contents.txt.new)
6. rebuild
7. rebuild
7. update the content of pydoc_injections.txt
8. update the content of pydoc_injections.txt
(from obj/.../pydoc_injections.txt)
+20 -21
View File
@@ -15,23 +15,22 @@ Latest stable versions of IDAPython are available from
## Resources
The full function cross-reference is readable online at
http://www.hex-rays.com/idapro/idapython_docs/
https://www.hex-rays.com/products/ida/support/idapython_docs/
Mailing list for the project is hosted by Google Groups at
http://groups.google.com/group/idapython
https://groups.google.com/g/idapython
## Installation from binaries
1. Install 2.6 or 2.7 from http://www.python.org/
1. Install latest Python 3.x version from https://www.python.org/
2. Copy the whole "python" directory to `%IDADIR%`
3. Copy the contents of the "plugins" directory to the `%IDADIR%\plugins\`
4. Copy "python.cfg" to `%IDADIR%\cfg`
3. Copy "idapython.cfg" to `%IDADIR%\cfg`
## Usage
- Run script: File / Script file (Alt-F7)
- Execute Python statement(s) (Ctrl-F3)
- Run previously executed script again: View / Recent Scripts (Alt+F9)
- Run script: File / Script file (`Alt+F7`)
- Execute Python statement(s) (`Shift+F2`)
- Run previously executed script again: View / Recent Scripts (`Alt+F9`)
### Batch mode execution:
@@ -48,7 +47,7 @@ or
-S"yourscript.py arg1 arg2 arg3"
```
(Please see http://www.hexblog.com/?p=128)
(Please see https://hex-rays.com/blog/running-scripts-from-the-command-line-with-idascript/)
If you want fully unattended execution mode, make sure your script
exits with a `qexit()` call.
@@ -65,7 +64,7 @@ Where N can be:
### User init file
You can place your custom settings to a file called 'idapythonrc.py'
You can place your custom settings to a file called `idapythonrc.py`
that should be placed to
```sh
${HOME}/.idapro/
@@ -76,27 +75,27 @@ or
```
The user init file is read and executed at the end of the init process.
Please note that IDAPython can be configured with "python.cfg" file.
Please note that IDAPython can be configured with `idapython.cfg` file.
### Invoking Python from IDC
The IDAPython plugin exposes a new IDC function `RunPythonStatement(string idc_code)` that allows execution
of Python code from IDC
The IDAPython plugin exposes a new IDC function `exec_python(string python_code)` that allows execution
of Python code from IDC.
### Invoking IDC from Python
It is possible to use the `idc.eval()` to evaluate IDC expressions from Python
It is possible to use the `idc.eval_idc()` to evaluate IDC expressions from Python.
### Making Python the default language
### Switching the default language between Python and IDC
By default, IDA will use IDC to evaluate expressions. It is possible to change the default language to use
Python instead of IDC.
By default, IDA will use IDC to evaluate expressions in dialog boxes and in `eval_expr()`.
It is possible to change the default language to Python.
In order to do that, please use the following IDC code:
In order to do that, use the following (IDC/Python) code:
```c
load_and_run_plugin("python", 3)
load_and_run_plugin("idapython", 3)
```
To disable Python language and revert back to IDC:
To go back to IDC, use the following code:
```c
load_and_run_plugin("python", 4)
load_and_run_plugin("idapython", 4)
```
+13 -36
View File
@@ -1,43 +1,20 @@
IDAPython requires a Python3.x installation in order to work.
IDAPython comes in two flavors:
Because different users might have different (and possibly multiple)
versions of Python3.x installed, IDA comes with a tool called `idapyswitch`
that can be run to select the desired Python3.x runtime.
* IDAPython-for-Python2
* IDAPython-for-Python3
If you selected IDAPython-for-Python3.x at the installation time,
the `idapyswitch` utility should already have been run and selected
the most appropriate Python3.x version.
# Switching between Python 2 and Python 3.
Depending on your choice at the install time, your "plugins" directory will have
one version of plugin installed as idapython.[dll|so|dylib] (and/or idapython64) and
the other will be present with the ".disabled" extension. To switch, just rename the
current version to .disabled and the disabled one back to the .dll/.so/.dylib
(depending on your OS)
For example, to swith from Python 3 to Python 2 on Windows:
1. rename idapython.dll to idapython.3.disabled and idapython64.dll to idapython64.3.disabled
2. rename idapython2.disabled to idapython.dll and idapython642.disabled to idapython64.dll
# Selecting a Python install to use
The situation for IDAPython-for-Python2 is simple: it uses Python 2.7,
and will expect that [lib]python2.7.[dll|so|dylib] is present in the system
library path so that IDA can find it.
When it comes to IDAPython-for-Python3, it gets more complex: because
different users might have different (and possibly multiple) versions
of Python3 installed, IDA comes with a tool called `idapyswitch`, that
can be run to select the desired Python3 runtime to tailor
IDAPython-for-Python3 to.
If you selected IDAPython-for-Python3 at installation-time,
`idapyswitch` utility should already have been run, and selected
the most appropriate Python3 version.
Should you want to switch to another Python3 install after installation,
please run `idapyswitch` from IDA's directory. It will scan for Python
installs present in the system's standard locations and offer you to choose one.
Should you want to switch to another Python3.x install after installation,
please run `idapyswitch` from the IDA directory. It will scan for Python
installs present in the system's standard locations and offer you to choose one.
It also supports optional command-line switches to handle non-standard installs.
Run `idapyswitch -h` to see them.
On Windows, you may need to run it as administrator
so it can patch sip.pyd (library required for PyQt bindings).
+1 -1
View File
@@ -4,7 +4,7 @@ A script that tries to determine the call stack
Run the application with the debugger, suspend the debugger, select a thread and finally run the script.
Copyright (c) 1990-2019 Hex-Rays
Copyright (c) 1990-2023 Hex-Rays
ALL RIGHTS RESERVED.
"""
import ida_ua
+1 -1
View File
@@ -2,7 +2,7 @@
A script to demonstrate how to send commands to the debugger and then parse and use the output in IDA
Copyright (c) 1990-2019 Hex-Rays
Copyright (c) 1990-2023 Hex-Rays
ALL RIGHTS RESERVED.
"""
+1 -1
View File
@@ -2,7 +2,7 @@
This script shows how to send debugger commands and use the result in IDA
Copyright (c) 1990-2019 Hex-Rays
Copyright (c) 1990-2023 Hex-Rays
ALL RIGHTS RESERVED.
"""
+1 -1
View File
@@ -13,7 +13,7 @@ The general syntax is:
* To specify in which context the instructions should be assembled, pass asm_where=ea:
find("jmp dword ptr [esp]", asm_where=here())
Copyright (c) 1990-2019 Hex-Rays
Copyright (c) 1990-2023 Hex-Rays
ALL RIGHTS RESERVED.
"""
from __future__ import print_function
+1 -1
View File
@@ -4,7 +4,7 @@ A script that graphs all the exception handlers in a given process
It will be easy to see what thread uses what handler and what handlers are commonly used between threads
Copyright (c) 1990-2019 Hex-Rays
Copyright (c) 1990-2023 Hex-Rays
ALL RIGHTS RESERVED.
"""
from __future__ import print_function
+1 -1
View File
@@ -2,7 +2,7 @@
This script shows how to send debugger commands and use the result in IDA
Copyright (c) 1990-2019 Hex-Rays
Copyright (c) 1990-2023 Hex-Rays
ALL RIGHTS RESERVED.
"""
+1 -1
View File
@@ -2,7 +2,7 @@ from __future__ import print_function
# -----------------------------------------------------------------------
# VirusTotal IDA Plugin
# By Elias Bachaalany <elias at hex-rays.com>
# (c) Hex-Rays 2011-2019
# (c) Hex-Rays 2011-2023
#
# Special thanks:
# - VirusTotal team
+1863 -539
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+13 -11
View File
@@ -21,20 +21,20 @@ had to build/install it yourself, you will have to specify '--swig-home'.
What follows, are example build commands
### Windows (assume SWiG is installed in C:\swigwin-2.0.12, and IDA is in C:\Program Files\IDA7)
### Windows (assume SWiG is installed in C:\swigwin-4.0.1, and IDA is in C:\Program Files\IDA8)
python2 build.py \\
python3 build.py \\
--with-hexrays \\
--swig-home C:/swigwin-2.0.12 \\
--idc "c:/Program\ Files/IDA_7.0-171130-tests/idc/idc.idc"
--swig-home C:/swigwin-4.0.1 \\
--ida-install "c:/Program\ Files/IDA_8.0"
### Linux/OSX (assume SWiG is installed in /opt/swiglinux-2.0.12, and IDA is in /opt/my-ida-install)
### Linux/OSX (assume SWiG is installed in /opt/swiglinux-4.0.1, and IDA is in /opt/my-ida-install)
python2 build.py \\
python3 build.py \\
--with-hexrays \\
--swig-home /opt/swiglinux-2.0.12 \\
--idc /opt/my-ida-install/idc/idc.idc
--swig-home /opt/swiglinux-4.0.1 \\
--ida-install /opt/my-ida-install
""",
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("--swig-home", type=str, help="Path to the SWIG installation", default=None)
@@ -42,10 +42,11 @@ parser.add_argument("--with-hexrays", help="Build Hex-Rays decompiler bindings (
parser.add_argument("--debug", help="Build debug version of the plugin", default=False, action="store_true")
parser.add_argument("-j", "--parallel", action="store_true", help="Build in parallel", default=False)
parser.add_argument("-v", "--verbose", help="Verbose mode", default=False, action="store_true")
parser.add_argument("-I", "--idc", required=True, help="IDA's idc.idc file (necessary for generating 6.95 compat API layer)", type=str)
parser.add_argument("-I", "--ida-install", required=True, help="IDA's installation directory", type=str)
args = parser.parse_args()
_probe = os.path.join("..", "..", "include", "pro.h")
sdk_relpath = os.path.join("..", "..")
_probe = os.path.join(sdk_relpath, "include", "pro.h")
assert os.path.exists(_probe), "Could not find IDA SDK include path (looked for: \"%s\")" % _probe
@@ -76,7 +77,8 @@ def main():
env["NDEBUG"] = "1"
if args.verbose:
argv.append("-d")
env["IDC_BC695_IDC_SOURCE"] = args.idc.replace('\\', '/')
env["IDA_INSTALL"] = args.ida_install.replace('\\', '/')
env["SDK_BIN_PATH"] = os.path.abspath(os.path.join(sdk_relpath, "bin")).replace('\\', '/')
for ea64 in [True, False]:
if ea64:
env["__EA64__"] = "1"
-46
View File
@@ -1,46 +0,0 @@
IDAPython for IDA 7.00: backward-compatibility with 6.95 APIs
=============================================================
Availability of the backward-compatibility code
-----------------------------------------------
* Backward-compatibility is provided by python.cfg's
`AUTOIMPORT_COMPAT_IDA695` directive. This directive is
currently turned on by default, but in the future it will:
1. be turned off by default
1. eventually disappear, and all the corresponding code will be removed
* Consequently IDAPython script/plugin writers should try and port
their code, with the help of the porting guide XXX FIXME URL? XXX.
* Once you have done the porting, please check that your code works
with `AUTOIMPORT_COMPAT_IDA695` set to `NO`.
* If anything proves too hard or confusing, please let us know about
it on <support@hexrays.com>, and we will help you, either by fixing
IDAPython if needed, or by improving the documentation.
Coverage of the backward-compatibility code
-------------------------------------------
* We did what was reasonably feasible, to provide an IDAPython API
that's as backward-compatible as possible with the IDA 6.95 API
* However, we considered it unreasonable for some parts of the API
to be ported. Most notably:
* the "processor module" API: existing processor modules will
have to be ported to the new API. Please see the SDK's
`module/script/proctemplate.py` (or any other `*.py` file in
that directory) for examples how to use the new API.
* processor module-related notifications: some of those have either
been renamed, or have possibly changed signature
* Most (all?) of the renamed functions, properties, etc... should be
covered, in all modules: `idaapi`, `idc`, ...
If something doesn't work/isn't there anymore, it's likely an
omission from our side.
* Please let us know about any missing bits & pieces, that you
believe should be there and that we might have forgotten!
-11
View File
@@ -1,11 +0,0 @@
#!/bin/sh
TARGET_DIR=../../../../www/www.hex-rays.com/public_html/hex-rays/products/ida/7.0/docs
TARGET_FILE=$TARGET_DIR/idapython_backward_compat_695.html
p4 edit $TARGET_FILE
echo \<html\>\<head\> > $TARGET_FILE
echo \<link type="text/css" rel="stylesheet" href="../../../../style.css" /\> >> $TARGET_FILE
echo \<link type="text/css" rel="stylesheet" href="style.css" /\> >> $TARGET_FILE
echo \</head\>\<body\> >> $TARGET_FILE
markdown bc695.md >> $TARGET_FILE
echo \</body\>\</html\> >> $TARGET_FILE
p4 revert -a $TARGET_FILE
+90
View File
@@ -0,0 +1,90 @@
This is an FAQ-style repository of SWiG incantations that are
regularly needed.
# Remove an argument from a prototype (because it doesn't make sense in the context of IDAPython)
%typemap(in,numinputs=0) int length
{
$1 = -1; // always -1 in IDAPython
}
# Add a parameter to a C++ notification
Assume a notification named `struc_renamed`, to which you just
added a `bool success` parameter (to carry along information about the
operation's success so far):
struc_renamed, ///< A structure type has been renamed.
///< \param sptr (::struc_t *)
///< \param success (bool) // <--------- new
That new parameter will be picked up by the hooks-producing code
automatically (great!), which means existing hooks are now broken
(bad.)
We need to add support for calling into "old-style" hooks. That's done
through the `patch_codegen.py` mechanism, and in particular the
`director_method_call_arity_cap` rule. Something like this should do:
"SwigDirector_IDB_Hooks::struc_renamed" : [
("director_method_call_arity_cap", (
False, # add GIL lock
"struc_renamed",
"(method ,(PyObject *)obj0,(__argcnt < 3 ? nullptr : (PyObject *)obj1), nullptr)",
"(swig_get_self(), (PyObject *) swig_method_name ,(PyObject *)obj0,(__argcnt < 3 ? nullptr : (PyObject *)obj1), nullptr)")
)),
],
# Handle a virtual method's "output" parameter in a director
Assume you have a virtual method taking an 'output' argument:
void merge_node_helper_t::get_column_headers(qstrvec_t *out, ...)
The neat thing to do here is to let Python implementations do the
following:
def get_column_headers(self, ...):
# ...
return ["Name", "Address"]
To achieve that, you want to use the `directorargout` typemap:
%typemap(directorargout) qstrvec_t * (qstrvec_t tmp)
{ // %typemap(directorargout) qstrvec_t *
if ( PyW_PyListToStrVec(&tmp, $result) >= 0 )
{
$1->swap(tmp);
}
else
{
Swig::DirectorTypeMismatchException::raise(
SWIG_ErrorType(SWIG_TypeError),
"in output value of type 'qstrvec_t' in method '$symname'");
}
}
# "intercept" access to a structure/class field, in order to perform extra work
For example, `idainfo.lflags` bits should be set using proper setters,
because they can have side-effects.
. tell swig to consider the member as unreachable:
%ignore idainfo::lflags;
. extend the type to "manually" provide the field:
%extend idainfo
{
// ...
uint32 _get_lflags() const { return $self->lflags; }
void _set_lflags(uint32 _f)
{
// do the job here
}
%pythoncode {
lflags = property(_get_lflags, _set_lflags)
+75
View File
@@ -0,0 +1,75 @@
# inject_pydoc.py
This tool is in charge extracting information from the C++ SDK
headers, and inject it into IDAPython's documentation.
## Extracting the C++ SDK information/documentation
The IDAPython build system will run `doxygen` against the SDK headers,
asking it to output all the information it could extract, as `XML`
format for later use - by `inject_pydoc.py`, but not only…
## Applying the documentation
Then, we run `tools/inject_pydoc.py`, to process that `XML`
content and extract documentation about the functions, classes,
methods, variables, etc… that are present in the corresponding
IDAPython module.
## Fixing the input parameters
We cannot blindly apply the SDK documentation, however: some C++
function parameters will turn into output values when converted to
Python, and thus it makes no sense to have those parameters as part of
the IDAPython function documentation. For example, when wrapping
inline void get_registered_actions(qstrvec_t *out)
into `ida_kernwin.get_registered_actions`, the `out` parameter will
turn into a `list(str)`, so it makes no sense to see the corresponding
C++ header's documentation:
/// \param out the list of actions to be filled
into the IDAPython documentation.
Fortunately, we can rely on SWiG's help to do that, because even
though SWiG doesn't know how to import C++ header's documentation, it
*will* tell us what parameters are present in the wrapped prototype.
Therefore, we collect the SWiG-generated list of parameters from the
prototype, and simply remove the non-relevant bits from the C++
header's documentation before injecting that into IDAPython.
## Fixing the return values
Because all of that was too easy, IDAPython adds another layer of
complexity (craziness?) for fixing the return values.
When it comes to the input parameters, it's actually _fairly_ easy to
drop the irrelevant bits from the C++ SDK header's documentation: we
know what parameters SWiG will keep & wrap.
But for the return values, it's another story entirely: SWiG doesn't
know (and cannot always reliably know) what type a return value will
have.
In particular when it comes to custom code in `pywraps/` that returns
a `PyObject *`.
Therefore, we have put a mechanism into place, that lets us put a
"wrapper" around _all_ IDAPython functions/class methods, that will
trace/keep track of the types of the values that were passed in, and
the types of the values that were spit out by those functions.
We can then use that wrapper when running tests, collect information
from the tests that were run, process that information, and save it
into `tools/collected_traces.txt`, which will then be used by
`inject_pydoc.py`.
It's worth pointing out that that information is not, and could not
possibly, be generated at build-time: it must be done at another time
(e.g., after running tests), which is very different than the
mechanism used for fixing the parameters (that "simply" relies on the
`doxygen`-parsed `XML`-formatted documentation.)
+22
View File
@@ -0,0 +1,22 @@
# IDAPython runtime documentation
We define the "IDAPython runtime documentation" as the documentation
that's available through Python's `help()` system, while the user is
interacting with `IDAPython` during an IDA session.
That documentation is SWiG-generated + patched, or custom written.
## Custom-written documentation
Some functions have custom-written documentation (check for <pydoc>
tags in the `pywraps/` directory), but the general case is that the
documentation is automatically generated by SWiG, and then patched.
## SWiG-generated + patched documentation
Because SWiG doesn't know about the C++ SDK header's documentation, we
need a way to "import" that documentation into IDAPython.
That's done by `tools/inject_pydoc.py` which deserves
[its own documentation](inject_pydoc.md)
+37
View File
@@ -0,0 +1,37 @@
# IDAPython examples
This directory contains a variety of examples demonstrating
how IDA can be scripted using IDAPython.
## Adding a new example (for Hex-Rays developers)
When adding an example, the author (i.e., a Hex-Rays developer)
must add a corresponding test, making sure the example is properly
tested, and doesn't regress over time (see `idapython-examples`,
and `idapython_hr-examples` test suites.) This has the added benefit
that this ensures our APIs remain stable.
Also, any significant addition to IDAPython APIs should come
with one or many examples, and those should be also put under test
(in other words: it's better if a test relies on a real example,
rather than if it consists of a bunch of IDAPython code our
users will never see, and cannot be inspired from.)
## Helping our customers, teaching IDAPython in the process
In addition, when a customer asks for help on support@ (or the forums)
and we end up sending a significant body of IDAPython code as a reply,
since that body of code should be tested anyway, it's better to make
a real example out of it ... and, of course, put that example under
test as well.
## Maintaining quality
There should be no such thing as a non-tested example.
## Updating the examples index
All examples are automatically integrated in the examples index.
In order to show user-friendly & relevant information, a proper
header (docstring) needs to be present.
+107
View File
@@ -0,0 +1,107 @@
"""
summary: dump (some) information about the current function.
description:
Dump some of the most interesting bits of information about
the function we are currently looking at.
"""
import binascii
import ida_kernwin
import ida_funcs
def dump_flags(fn):
"dump some flags of the func_t object"
print("Function flags: %08X" % fn.flags)
if fn.is_far():
print(" Far function")
if not fn.does_return():
print(" Function does not return")
if fn.flags & ida_funcs.FUNC_FRAME:
print(" Function uses frame pointer")
if fn.flags & ida_funcs.FUNC_THUNK:
print(" Thunk function")
if fn.flags & ida_funcs.FUNC_LUMINA:
print(" Function info is provided by Lumina")
if fn.flags & ida_funcs.FUNC_OUTLINE:
print(" Outlined code, not a real function")
def dump_regvars(pfn):
"dump renamed registers information"
assert ida_funcs.is_func_entry(pfn)
print("Function has %d renamed registers" % pfn.regvarqty)
for rv in pfn.regvars:
print("%08X..%08X '%s'->'%s'" % (rv.start_ea, rv.end_ea, rv.canon, rv.user))
def dump_regargs(pfn):
"dump register arguments information"
assert ida_funcs.is_func_entry(pfn)
print("Function has %d register arguments" % pfn.regargqty)
for ra in pfn.regargs:
print(" register #=%d, argument name=\"%s\", (serialized) type=\"%s\"" % (
ra.reg,
ra.name,
binascii.hexlify(ra.type)))
def dump_tails(pfn):
"dump function tails for entry chunk pfn"
assert ida_funcs.is_func_entry(pfn)
print("Function has %d tails" % pfn.tailqty)
for i in range(pfn.tailqty):
ft = pfn.tails[i]
print(" tail %i: %08X..%08X" % (i, ft.start_ea, ft.end_ea))
def dump_stkpnts(pfn):
"dump function stack points"
print("Function has %d stack points" % pfn.pntqty)
for i in range(pfn.pntqty):
pnt = pfn.points[i]
print(" stkpnt %i @%08X: %d" % (i, pnt.ea, pnt.spd))
def dump_frame(fn):
"dump function frame info"
assert ida_funcs.is_func_entry(fn)
print("frame structure id: %08X" % fn.frame)
print("local variables area size: %8X" % fn.frsize)
print("saved registers area size: %8X" % fn.frregs)
print("bytes purged on return : %8X" % fn.argsize)
print("frame pointer delta : %8X" % fn.fpd)
def dump_parents(fn):
"dump parents of a function tail"
assert ida_funcs.is_func_tail(fn)
print("owner function: %08X" % fn.owner)
print("tail has %d referers" % fn.refqty)
for i in range(fn.refqty):
print(" referer %i: %08X" % (i, fn.referers[i]))
def dump_func_info(ea):
"dump info about function chunk at address 'ea'"
pfn = ida_funcs.get_fchunk(ea)
if pfn is None:
print("No function at %08X!" % ea)
return
print("current chunk boundaries: %08X..%08X" % (pfn.start_ea, pfn.end_ea))
dump_flags(pfn)
if (ida_funcs.is_func_entry(pfn)):
print ("This is an entry chunk")
dump_tails(pfn)
dump_frame(pfn)
dump_regvars(pfn)
dump_regargs(pfn)
dump_stkpnts(pfn)
elif (ida_funcs.is_func_tail(pfn)):
print ("This is a tail chunk")
dump_parents(pfn)
ea = ida_kernwin.get_screen_ea()
dump_func_info(ea)
+41 -16
View File
@@ -1,19 +1,44 @@
from __future__ import print_function
import idaapi
"""
summary: custom actions, with icons & tooltips
class SayHi(idaapi.action_handler_t):
description:
How to create user actions, that once created can be
inserted in menus, toolbars, context menus, ...
Those actions, when triggered, will be passed a 'context'
that contains some of the most frequently needed bits of
information.
In addition, custom actions can determine when they want
to be available (through their
`ida_kernwin.action_handler_t.update` callback)
keywords: actions
see_also: add_hotkey
"""
from __future__ import print_function
import ida_kernwin
class SayHi(ida_kernwin.action_handler_t):
def __init__(self, message):
idaapi.action_handler_t.__init__(self)
ida_kernwin.action_handler_t.__init__(self)
self.message = message
def activate(self, ctx):
print("Hi, %s" % (self.message))
# print("context fields: %s" % dir(ctx))
print(" cur_ea %08X" % ctx.cur_ea)
print(" cur_value: %08X" % ctx.cur_value)
print(" cur_extracted_ea %08X" % ctx.cur_extracted_ea)
return 1
# You can implement update(), to inform IDA when:
# * your action is enabled
# * update() should queried again
# E.g., returning 'idaapi.AST_ENABLE_FOR_WIDGET' will
# E.g., returning 'ida_kernwin.AST_ENABLE_FOR_WIDGET' will
# tell IDA that this action is available while the
# user is in the current widget, and that update()
# must be queried again once the user gives focus
@@ -25,7 +50,7 @@ class SayHi(idaapi.action_handler_t):
# querying update() anymore until the user has moved
# to another view..
def update(self, ctx):
return idaapi.AST_ENABLE_FOR_WIDGET if ctx.widget_type == idaapi.BWN_DISASM else idaapi.AST_DISABLE_FOR_WIDGET
return ida_kernwin.AST_ENABLE_FOR_WIDGET if ctx.widget_type == ida_kernwin.BWN_DISASM else ida_kernwin.AST_DISABLE_FOR_WIDGET
print("Creating a custom icon from raw data!")
@@ -49,12 +74,12 @@ icon_data = b"".join([
b"\xF6\xC1\xED\x52\xB8\x77\xAB\x98\x3A\xCD\xC4\x73\x9D\x7C\x6F\xDE\xF9\xCF\x53\x0E\xFE\xA9\xCD\xAE\xB3\x87\xCE\x75\x35\x54\xE1\xD0\xCB\x47\x38\x39\x36\x88\xFF\x4D\xF8\x57\x41\x33",
b"\xF1\xA4\x93\x0F\x00\x36\xAD\x3E\x4C\x6B\xC5\xC9\x5D\x77\x6A\x2F\xB4\x31\xA3\xC4\x40\x4F\x21\x0F\xD1\x4C\x3C\xE9\x2B\xE1\xF5\x0B\xD6\x90\xC8\x90\x4C\xE6\x35\xD0\xCC\x79\x5E\xFF",
b"\x2E\xF8\x0B\x2F\x3D\xE5\xC3\x97\x06\xCF\xCF\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82"])
act_icon = idaapi.load_custom_icon(data=icon_data, format="png")
act_icon = ida_kernwin.load_custom_icon(data=icon_data, format="png")
hooks = None
act_name = "example:add_action"
if idaapi.register_action(idaapi.action_desc_t(
if ida_kernwin.register_action(ida_kernwin.action_desc_t(
act_name, # Name. Acts as an ID. Must be unique.
"Say hi!", # Label. That's what users see.
SayHi("developer"), # Handler. Called when activated, and for updating
@@ -64,13 +89,13 @@ if idaapi.register_action(idaapi.action_desc_t(
print("Action registered. Attaching to menu.")
# Insert the action in the menu
if idaapi.attach_action_to_menu("Edit/Export data", act_name, idaapi.SETMENU_APP):
if ida_kernwin.attach_action_to_menu("Edit/Export data", act_name, ida_kernwin.SETMENU_APP):
print("Attached to menu.")
else:
print("Failed attaching to menu.")
# Insert the action in a toolbar
if idaapi.attach_action_to_toolbar("AnalysisToolBar", act_name):
if ida_kernwin.attach_action_to_toolbar("AnalysisToolBar", act_name):
print("Attached to toolbar.")
else:
print("Failed attaching to toolbar.")
@@ -81,7 +106,7 @@ if idaapi.register_action(idaapi.action_desc_t(
# To do that, we could in theory retrieve a reference to "IDA View-A", and
# then request to "permanently" attach the action to it, using something
# like this:
# idaapi.attach_action_to_popup(ida_view_a, None, act_name, None)
# ida_kernwin.attach_action_to_popup(ida_view_a, None, act_name, None)
#
# but alas, that won't do: widgets in IDA are very "volatile", and
# can be deleted & re-created on some occasions (e.g., starting a
@@ -92,17 +117,17 @@ if idaapi.register_action(idaapi.action_desc_t(
# Instead, we can opt for a different method: attach our action on-the-fly,
# when the popup for "IDA View-A" is being populated, right before
# it is displayed.
class Hooks(idaapi.UI_Hooks):
class Hooks(ida_kernwin.UI_Hooks):
def finish_populating_widget_popup(self, widget, popup):
# We'll add our action to all "IDA View-*"s.
# If we wanted to add it only to "IDA View-A", we could
# also discriminate on the widget's title:
#
# if idaapi.get_widget_title(widget) == "IDA View-A":
# if ida_kernwin.get_widget_title(widget) == "IDA View-A":
# ...
#
if idaapi.get_widget_type(widget) == idaapi.BWN_DISASM:
idaapi.attach_action_to_popup(widget, popup, act_name, None)
if ida_kernwin.get_widget_type(widget) == ida_kernwin.BWN_DISASM:
ida_kernwin.attach_action_to_popup(widget, popup, act_name, None)
hooks = Hooks()
hooks.hook()
@@ -110,7 +135,7 @@ else:
print("Action found; unregistering.")
# No need to call detach_action_from_menu(); it'll be
# done automatically on destruction of the action.
if idaapi.unregister_action(act_name):
if ida_kernwin.unregister_action(act_name):
print("Unregistered.")
else:
print("Failed to unregister action.")
+22 -9
View File
@@ -1,24 +1,37 @@
"""
summary: triggering bits of code by pressing a shortcut
description:
`ida_kernwin.add_hotkey` is a simpler, but much less flexible
alternative to `ida_kernwin.register_action` (though it does
use the same mechanism under the hood.)
It's particularly useful during prototyping, but note that the
actions that are created cannot be inserted in menus, toolbars
or cannot provide a custom `ida_kernwin.action_handler_t.update`
callback.
keywords: actions
see_also: actions
"""
from __future__ import print_function
#---------------------------------------------------------------------
# This script demonstrates the usage of hotkeys.
#
#
# Author: IDAPython team
#---------------------------------------------------------------------
import idaapi
import ida_kernwin
def hotkey_pressed():
print("hotkey pressed!")
try:
hotkey_ctx
if idaapi.del_hotkey(hotkey_ctx):
if ida_kernwin.del_hotkey(hotkey_ctx):
print("Hotkey unregistered!")
del hotkey_ctx
else:
print("Failed to delete hotkey!")
except:
hotkey_ctx = idaapi.add_hotkey("Shift-A", hotkey_pressed)
hotkey_ctx = ida_kernwin.add_hotkey("Shift-A", hotkey_pressed)
if hotkey_ctx is None:
print("Failed to register hotkey!")
del hotkey_ctx
-25
View File
@@ -1,25 +0,0 @@
from __future__ import print_function
#---------------------------------------------------------------------
# This script demonstrates the usage of hotkeys.
#
# Note: Hotkeys only work with the GUI version of IDA and not in
# text mode.
#
# Author: Gergely Erdelyi <gergely.erdelyi@d-dome.net>
#---------------------------------------------------------------------
import idaapi
def foo():
print("Hotkey activated!")
# IDA binds hotkeys to IDC functions so a trampoline IDC function
# must be created
idaapi.compile_idc_text('static key_2() { RunPythonStatement("foo()"); }')
# Add the hotkey
add_idc_hotkey("2", 'key_2')
# Press 2 to activate foo()
# The hotkey can be removed with
# del_idc_hotkey('2')
+32
View File
@@ -0,0 +1,32 @@
"""
summary: triggering bits of code by pressing a shortcut (older version)
description:
This is a somewhat ancient way of registering actions & binding
shortcuts. It's still here for reference, but "fresher" alternatives
should be preferred.
keywords: actions
see_also: actions, add_hotkey
"""
from __future__ import print_function
import ida_expr
import ida_kernwin
def say_hi():
print("Hotkey activated!")
# IDA binds hotkeys to IDC functions so a trampoline IDC function must be created
ida_expr.compile_idc_text('static key_2() { RunPythonStatement("say_hi()"); }')
# Add the hotkey
ida_kernwin.add_idc_hotkey("2", 'key_2')
# Press 2 to activate foo()
# The hotkey can be removed with
# ida_kernwin.del_idc_hotkey('2')
+26 -20
View File
@@ -1,3 +1,28 @@
"""
summary: better integrating custom widgets in the desktop layout
description:
This is an example demonstrating how one can create widgets from a plugin,
and have them re-created automatically at IDA startup-time or at desktop load-time.
This example should be placed in the 'plugins' directory of the
IDA installation, for it to work.
There are 2 ways to use this example:
1) reloading an IDB, where the widget was opened
- open the widget ('View > Open subview > ...')
- save this IDB, and close IDA
- restart IDA with this IDB
=> the widget will be visible
2) reloading a desktop, where the widget was opened
- open the widget ('View > Open subview > ...')
- save the desktop ('Windows > Save desktop...') under, say, the name 'with_auto'
- start another IDA instance with some IDB, and load that desktop
=> the widget will be visible
keywords: desktop
"""
import ida_idaapi
import ida_kernwin
@@ -13,26 +38,7 @@ class auto_inst_t(ida_kernwin.simplecustviewer_t):
if not ida_kernwin.simplecustviewer_t.Create(self, title):
return False
text = r"""
This is an example demonstrating how one can create widgets from a plugin,
and have them re-created automatically at IDA startup-time or at desktop load-time.
This example should be placed in the 'plugins' directory of the
IDA installation, for it to work.
There are 2 ways to use this example:
1) reloading an IDB, where the widget was opened
- open the widget ('View > Open subview > """ + title + """')
- save this IDB, and close IDA
- restart IDA with this IDB
=> the widget will be visible
2) reloading a desktop, where the widget was opened
- open the widget ('View > Open subview > """ + title + """')
- save the desktop ('Windows > Save desktop...') under, say, the name 'with_auto'
- start another IDA instance with some IDB, and load that desktop
=> the widget will be visible
"""
text = __doc__
for l in text.split("\n"):
self.AddLine(l)
return True
+97
View File
@@ -0,0 +1,97 @@
"""
summary: showcasing `ida_bytes.bin_search`
description:
IDAPython's ida_bytes.bin_search function is pretty powerful,
but can be tough to figure out at first. This example introduces
* `ida_bytes.bin_search`, and
* `ida_bytes.parse_binpat_str`
in order to implement a simple replacement for the
'Search > Sequence of bytes...' dialog, that lets users
search for sequences of bytes that compose string literals
in the binary file (either in the default 1-byte-per-char
encoding, or as UTF-16.)
"""
from __future__ import print_function
import ida_kernwin
import ida_bytes
import ida_ida
import ida_idaapi
import ida_nalt
class search_strlit_form_t(ida_kernwin.Form):
def __init__(self):
ida_kernwin.Form.__init__(
self,
r"""Please enter string literal
<Text: {Text}>
<#UTF16-BE if file is big-endian, UTF16-LE otherwise#As UTF-16: {UTF16}>{Encoding}>
""",
{
"Text" : ida_kernwin.Form.StringInput(),
"Encoding" : ida_kernwin.Form.ChkGroupControl(("UTF16",)),
})
class search_strlit_ah_t(ida_kernwin.action_handler_t):
def __init__(self):
ida_kernwin.action_handler_t.__init__(self)
def activate(self, ctx):
f = search_strlit_form_t()
f, args = f.Compile()
ok = f.Execute()
if ok:
current_ea = ida_kernwin.get_screen_ea()
patterns = ida_bytes.compiled_binpat_vec_t()
encoding = ida_nalt.get_default_encoding_idx(
ida_nalt.BPU_2B if f.Encoding.value else ida_nalt.BPU_1B)
# string literals must be quoted. That's how parse_binpat_str
# recognizes them (we want to be careful though: the user
# might type in something like 'L"hello"', which should
# decode to the IDB-specific wide-char set of bytes)
text = f.Text.value
if text.find('"') < 0:
text = '"%s"' % text
err = ida_bytes.parse_binpat_str(
patterns,
current_ea,
text,
10, # radix (not that it matters though, since we're all about string literals)
encoding)
if not err:
ea = ida_bytes.bin_search(
current_ea,
ida_ida.inf_get_max_ea(),
patterns,
ida_bytes.BIN_SEARCH_FORWARD
| ida_bytes.BIN_SEARCH_NOBREAK
| ida_bytes.BIN_SEARCH_NOSHOW)
ok = ea != ida_idaapi.BADADDR
if ok:
ida_kernwin.jumpto(ea)
else:
print("Failed parsing binary pattern: \"%s\"" % err)
return ok
def update(self, ctx):
return ida_kernwin.AST_ENABLE_FOR_WIDGET \
if ctx.widget_type == ida_kernwin.BWN_DISASM \
else ida_kernwin.AST_DISABLE_FOR_WIDGET
ACTION_NAME = "bin_search:search"
ACTION_SHORTCUT = "Ctrl+Shift+S"
if ida_kernwin.register_action(
ida_kernwin.action_desc_t(
ACTION_NAME,
"Search for string literal",
search_strlit_ah_t(),
ACTION_SHORTCUT)):
print("Please use \"%s\" to search for string literals" % ACTION_SHORTCUT)
+34
View File
@@ -0,0 +1,34 @@
"""
summary: change background colours
description:
This illustrates the setting/retrieval of background colours
using the IDC wrappers
In order to do so, we'll be assigning colors to specific ranges
(item, function, or segment). Those will be persisted in the
database.
category: disassembly
keywords: coloring, idc
see_also: colorize_disassembly_on_the_fly
"""
from __future__ import print_function
BG_BLUE = 0xc02020
BG_GREEN = 0x208020
BG_RED = 0x2020c0
import idc
ea = idc.here()
idc.set_color(ea, idc.CIC_SEGM, BG_BLUE)
idc.set_color(ea, idc.CIC_FUNC, BG_GREEN)
idc.set_color(ea, idc.CIC_ITEM, BG_RED)
print("Segment: %x" % idc.get_color(ea, idc.CIC_SEGM))
print("Function: %x" % idc.get_color(ea, idc.CIC_FUNC))
print("Item: %x" % idc.get_color(ea, idc.CIC_ITEM))
@@ -0,0 +1,136 @@
"""
summary: an easy-to-use way to colorize lines
description:
This builds upon the `ida_kernwin.UI_Hooks.get_lines_rendering_info`
feature, to provide a quick & easy way to colorize disassembly
lines.
Contrary to @colorize_disassembly, the coloring is not persisted in
the database, and will therefore be lost after the session.
By triggering the action multiple times, the user can "carousel"
across 4 predefined colors (and return to the "no color" state.)
keywords: coloring
see_also: colorize_disassembly
"""
import ida_kernwin
import ida_moves
class on_the_fly_coloring_hooks_t(ida_kernwin.UI_Hooks):
# We'll offer the users the ability to carousel around the
# following colors. Well, note that these are in fact not
# colors, but rather color "keys": each theme might have its
# own values for those.
AVAILABLE_COLORS = [
ida_kernwin.CK_EXTRA5,
ida_kernwin.CK_EXTRA6,
ida_kernwin.CK_EXTRA7,
ida_kernwin.CK_EXTRA8,
]
def __init__(self):
ida_kernwin.UI_Hooks.__init__(self)
# Each view can have on-the-fly coloring.
# We'll store the custom colors keyed on the widget's title
self.by_widget = {}
def get_lines_rendering_info(self, out, widget, rin):
"""
Called by IDA, at rendering-time.
We'll look in our set of marked lines, and for those that are
found, will produce additional rendering information for IDA
to use.
"""
title = ida_kernwin.get_widget_title(widget)
assigned = self.by_widget.get(title, None)
if assigned is not None:
for section_lines in rin.sections_lines:
for line in section_lines:
for loc, color in assigned:
if self._same_lines(widget, line.at, loc.place()):
e = ida_kernwin.line_rendering_output_entry_t(line)
e.bg_color = color
out.entries.push_back(e)
def _same_lines(self, viewer, p0, p1):
return ida_kernwin.get_custom_viewer_place_xcoord(viewer, p0, p1) != -1
def _find_loc_index(self, viewer, assigned, loc):
for idx, tpl in enumerate(assigned):
_loc = tpl[0]
if self._same_lines(viewer, loc.place(), _loc.place()):
return idx
return -1
def carousel_color(self, viewer, title):
"""
This performs the work of iterating across the available
colors (and the 'no-color' state.)
"""
loc = ida_moves.lochist_entry_t()
if ida_kernwin.get_custom_viewer_location(loc, viewer):
assigned = self.by_widget.get(title, [])
new_color = None
idx = self._find_loc_index(viewer, assigned, loc)
if idx > -1:
prev_color = assigned[idx][1]
prev_color_idx = self.AVAILABLE_COLORS.index(prev_color)
new_color = None \
if prev_color_idx >= (len(self.AVAILABLE_COLORS) - 1) \
else self.AVAILABLE_COLORS[prev_color_idx + 1]
else:
new_color = self.AVAILABLE_COLORS[0]
if idx > -1:
del assigned[idx]
if new_color is not None:
assigned.append((loc, new_color))
if assigned:
self.by_widget[title] = assigned
else:
if title in self.by_widget:
del self.by_widget[title]
class carousel_color_ah_t():
"""
The action that will be invoked by IDA when the user
activates its shortcut.
"""
def __init__(self, hooks):
self.hooks = hooks
def activate(self, ctx):
v = ida_kernwin.get_current_viewer()
if v:
self.hooks.carousel_color(v, ctx.widget_title)
return 1 # will cause the widget to redraw
def update(self, ctx):
return ida_kernwin.AST_ENABLE_FOR_WIDGET \
if ida_kernwin.get_current_viewer() \
else ida_kernwin.AST_DISABLE_FOR_WIDGET
ACTION_NAME = "example:colorize_disassembly_on_the_fly"
ACTION_LABEL = "Pick line color"
ACTION_SHORTCUT = "!"
ACTION_HELP = "Press %s to carousel around available colors (or remove a previously-set color)" % ACTION_SHORTCUT
otf_coloring = on_the_fly_coloring_hooks_t()
if ida_kernwin.register_action(ida_kernwin.action_desc_t(
ACTION_NAME,
ACTION_LABEL,
carousel_color_ah_t(otf_coloring),
ACTION_SHORTCUT)):
print("Registered action \"%s\". %s" % (ACTION_LABEL, ACTION_HELP))
otf_coloring.hook()
-20
View File
@@ -1,20 +0,0 @@
from __future__ import print_function
#---------------------------------------------------------------------
# Colour test
#
# This script demonstrates the usage of background colours.
#
# Author: Gergely Erdelyi <gergely.erdelyi@d-dome.net>
#---------------------------------------------------------------------
# Set the colour of the current segment to BLUE
set_color(here(), CIC_SEGM, 0xc02020)
# Set the colour of the current function to GREEN
set_color(here(), CIC_FUNC, 0x208020)
# Set the colour of the current item to RED
set_color(here(), CIC_ITEM, 0x2020c0)
# Print the colours just set
print("%x" % get_color(here(), CIC_SEGM))
print("%x" % get_color(here(), CIC_FUNC))
print("%x" % get_color(here(), CIC_ITEM))
@@ -1,50 +1,68 @@
"""
summary: programmatically create & populate a structure
description:
Usage of the API to create & populate a structure with
members of different types.
author: Gergely Erdelyi (gergely.erdelyi@d-dome.net)
"""
from __future__ import print_function
#---------------------------------------------------------------------
# Structure test
#
# This script demonstrates how to create structures and populate them
# with members of different types.
#
# Author: Gergely Erdelyi <gergely.erdelyi@d-dome.net>
#---------------------------------------------------------------------
from idaapi import stroffflag, offflag
sid = get_struc_id("mystr1")
import ida_struct
import ida_idaapi
import ida_bytes
import ida_nalt
import idc
sid = ida_struct.get_struc_id("mystr1")
if sid != -1:
del_struc(sid)
sid = add_struc(-1, "mystr1", 0)
idc.del_struc(sid)
sid = ida_struct.add_struc(ida_idaapi.BADADDR, "mystr1", 0)
print("%x" % sid)
# Test simple data types
simple_types = [ FF_BYTE, FF_WORD, FF_DWORD, FF_QWORD, FF_TBYTE, FF_OWORD, FF_FLOAT, FF_DOUBLE, FF_PACKREAL ]
simple_sizes = [ 1, 2, 4, 8, 10, 16, 4, 8, 10 ]
i = 0
for t,nsize in zip(simple_types, simple_sizes):
print("t%x:"% ((t|FF_DATA)&0xFFFFFFFF), add_struc_member(sid, "t%02d"%i, BADADDR, (t|FF_DATA )&0xFFFFFFFF, -1, nsize))
i+=1
simple_types_data = [
(ida_bytes.FF_BYTE, 1),
(ida_bytes.FF_WORD, 2),
(ida_bytes.FF_DWORD, 4),
(ida_bytes.FF_QWORD, 8),
(ida_bytes.FF_TBYTE, 10),
(ida_bytes.FF_OWORD, 16),
(ida_bytes.FF_FLOAT, 4),
(ida_bytes.FF_DOUBLE, 8),
(ida_bytes.FF_PACKREAL, 10),
]
for i, tpl in enumerate(simple_types_data):
t, nsize = tpl
print("t%x:"% ((t|ida_bytes.FF_DATA) & 0xFFFFFFFF),
idc.add_struc_member(sid, "t%02d"%i, ida_idaapi.BADADDR, (t|ida_bytes.FF_DATA )&0xFFFFFFFF, -1, nsize))
# Test ASCII type
print("ASCII:", add_struc_member(sid, "tascii", -1, FF_STRLIT|FF_DATA, STRTYPE_C, 8))
# Test enum type - Add a defined enum name or load MACRO_WMI from a type library.
#eid = get_enum("MACRO_WMI")
#print("Enum:", add_struc_member(sid, "tenum", BADADDR, FF_0ENUM|FF_DATA|FF_DWORD, eid, 4))
print("ASCII:", idc.add_struc_member(sid, "tascii", -1, ida_bytes.FF_STRLIT|ida_bytes.FF_DATA, ida_nalt.STRTYPE_C, 8))
# Test struc member type
msid = get_struc_id("mystr2")
msid = ida_struct.get_struc_id("mystr2")
if msid != -1:
del_struc(msid)
msid = add_struc(-1, "mystr2", 0)
print(add_struc_member(msid, "member1", -1, (FF_DWORD|FF_DATA )&0xFFFFFFFF, -1, 4))
print(add_struc_member(msid, "member2", -1, (FF_DWORD|FF_DATA )&0xFFFFFFFF, -1, 4))
idc.del_struc(msid)
msid = idc.add_struc(-1, "mystr2", 0)
print(idc.add_struc_member(msid, "member1", -1, (ida_bytes.FF_DWORD|ida_bytes.FF_DATA )&0xFFFFFFFF, -1, 4))
print(idc.add_struc_member(msid, "member2", -1, (ida_bytes.FF_DWORD|ida_bytes.FF_DATA )&0xFFFFFFFF, -1, 4))
msize = get_struc_size(msid)
print("Struct:", add_struc_member(sid, "tstruct", -1, FF_STRUCT|FF_DATA, msid, msize))
print("Stroff:", add_struc_member(sid, "tstroff", -1, stroffflag()|FF_DWORD, msid, 4))
msize = ida_struct.get_struc_size(msid)
print("Struct:", idc.add_struc_member(sid, "tstruct", -1, ida_bytes.FF_STRUCT|ida_bytes.FF_DATA, msid, msize))
print("Stroff:", idc.add_struc_member(sid, "tstroff", -1, ida_bytes.stroff_flag()|ida_bytes.FF_DWORD, msid, 4))
# Test offset types
print("Offset:", add_struc_member(sid, "toffset", -1, offflag()|FF_DATA|FF_DWORD, 0, 4))
print("Offset:", set_member_type(sid, 0, offflag()|FF_DATA|FF_DWORD, 0, 4))
print("Offset:", idc.add_struc_member(sid, "toffset", -1, ida_bytes.off_flag()|ida_bytes.FF_DATA|ida_bytes.FF_DWORD, 0, 4))
print("Offset:", idc.set_member_type(sid, 0, ida_bytes.off_flag()|ida_bytes.FF_DATA|ida_bytes.FF_DWORD, 0, 4))
print("Done")
+36 -47
View File
@@ -1,77 +1,65 @@
"""
summary: a custom command-line interpreter
description:
Illustrates how one can add command-line interpreters to IDA
This custom interpreter doesn't actually run any code; it's
there as a 'getting started'.
It provides an example tab completion support.
"""
from __future__ import print_function
# -----------------------------------------------------------------------
# This is an example illustrating how to implement a CLI
# (c) Hex-Rays
#
from idaapi import NW_OPENIDB, NW_CLOSEIDB, NW_TERMIDA, NW_REMOVE, COLSTR, cli_t
# A trivial example is also provided for tab completion. To try it,
# type "bon" in the input field, and then press <Tab> multiple times.
#
# (c) Hex-Rays
class mycli_t(cli_t):
import ida_kernwin
import ida_idaapi
class mycli_t(ida_kernwin.cli_t):
flags = 0
sname = "pycli"
lname = "Python CLI"
hint = "pycli hint"
def OnExecuteLine(self, line):
"""
The user pressed Enter. The CLI is free to execute the line immediately or ask for more lines.
This callback is mandatory.
@param line: typed line(s)
@return Boolean: True-executed line, False-ask for more lines
"""
print("OnExecute:", line)
return True
def OnKeydown(self, line, x, sellen, vkey, shift):
"""
A keyboard key has been pressed
This is a generic callback and the CLI is free to do whatever it wants.
This callback is optional.
@param line: current input line
@param x: current x coordinate of the cursor
@param sellen: current selection length (usually 0)
@param vkey: virtual key code. if the key has been handled, it should be returned as zero
@param shift: shift state
@return:
None - Nothing was changed
tuple(line, x, sellen, vkey): if either of the input line or the x coordinate or the selection length has been modified.
It is possible to return a tuple with None elements to preserve old values. Example: tuple(new_line, None, None, None) or tuple(new_line)
"""
print("Onkeydown: line=%s x=%d sellen=%d vkey=%d shift=%d" % (line, x, sellen, vkey, shift))
return None
completions = [
"bonnie & clyde",
"bonfire of the vanities",
"bongiorno",
]
def OnCompleteLine(self, prefix, n, line, prefix_start):
"""
The user pressed Tab. Find a completion number N for prefix PREFIX
This callback is optional.
@param prefix: Line prefix at prefix_start (string)
@param n: completion number (int)
@param line: the current line (string)
@param prefix_start: the index where PREFIX starts in LINE (int)
@return: None if no completion could be generated otherwise a String with the completion suggestion
"""
print("OnCompleteLine: prefix=%s n=%d line=%s prefix_start=%d" % (prefix, n, line, prefix_start))
if prefix == "bon":
if n < len(self.completions):
return self.completions[n]
return None
# -----------------------------------------------------------------------
def nw_handler(code, old=0):
if code == NW_OPENIDB:
if code == ida_idaapi.NW_OPENIDB:
print("nw_handler(): installing CLI")
mycli.register()
elif code == NW_CLOSEIDB:
elif code == ida_idaapi.NW_CLOSEIDB:
print("nw_handler(): removing CLI")
mycli.unregister()
elif code == NW_TERMIDA:
elif code == ida_idaapi.NW_TERMIDA:
print("nw_handler(): uninstalled nw handler")
idaapi.notify_when(NW_TERMIDA | NW_OPENIDB | NW_CLOSEIDB | NW_REMOVE, nw_handler)
when = ida_idaapi.NW_TERMIDA | ida_idaapi.NW_OPENIDB | ida_idaapi.NW_CLOSEIDB | ida_idaapi.NW_REMOVE
ida_idaapi.notify_when(when, nw_handler)
# -----------------------------------------------------------------------
@@ -82,7 +70,7 @@ try:
mycli.unregister()
del mycli
# remove previous handler
nw_handler(NW_TERMIDA)
nw_handler(ida_idaapi.NW_TERMIDA)
except:
pass
finally:
@@ -92,7 +80,8 @@ finally:
if mycli.register():
print("CLI installed")
# install new handler
idaapi.notify_when(NW_TERMIDA | NW_OPENIDB | NW_CLOSEIDB, nw_handler)
when = ida_idaapi.NW_TERMIDA | ida_idaapi.NW_OPENIDB | ida_idaapi.NW_CLOSEIDB
ida_idaapi.notify_when(when, nw_handler)
else:
del mycli
print("Failed to install CLI")
+54 -37
View File
@@ -1,9 +1,24 @@
"""
summary: using custom data types & printers
description:
IDA can be extended to support certain data types that it
does not know about out-of-the-box.
A 'custom data type' provide information about the type &
size of a piece of data, while a 'custom data format' is in
charge of formatting that data (there can be more than
one format for a specific 'custom data type'.)
"""
from __future__ import print_function
# -----------------------------------------------------------------------
# This is an example illustrating how to use custom data types in Python
# (c) Hex-Rays
#
from idaapi import data_type_t, data_format_t, NW_OPENIDB, NW_CLOSEIDB, NW_TERMIDA, NW_REMOVE, COLSTR
import ida_bytes
import ida_idaapi
import ida_lines
import ida_struct
import ida_netnode
import ida_nalt
import sys
import struct
@@ -11,9 +26,9 @@ import ctypes
import platform
# -----------------------------------------------------------------------
class pascal_data_type(data_type_t):
class pascal_data_type(ida_bytes.data_type_t):
def __init__(self):
data_type_t.__init__(
ida_bytes.data_type_t.__init__(
self,
"py_pascal_string",
2,
@@ -24,11 +39,11 @@ class pascal_data_type(data_type_t):
def calc_item_size(self, ea, maxsize):
# Custom data types may be used in structure definitions. If this case
# ea is a member id. Check for this situation and return 1
if idaapi.is_member_id(ea):
if ida_struct.is_member_id(ea):
return 1
# get the length byte
n = idaapi.get_byte(ea)
n = ida_bytes.get_byte(ea)
# string too big?
if n > maxsize:
@@ -36,10 +51,10 @@ class pascal_data_type(data_type_t):
# ok, accept the string
return n + 1
class pascal_data_format(data_format_t):
class pascal_data_format(ida_bytes.data_format_t):
FORMAT_NAME = "py_pascal_string_pstr"
def __init__(self):
data_format_t.__init__(
ida_bytes.data_format_t.__init__(
self,
pascal_data_format.FORMAT_NAME)
@@ -57,7 +72,7 @@ class pascal_data_format(data_format_t):
return "".join(o)
# -----------------------------------------------------------------------
class simplevm_data_type(data_type_t):
class simplevm_data_type(ida_bytes.data_type_t):
ASM_KEYWORD = "svm_emit"
def __init__(
self,
@@ -65,7 +80,7 @@ class simplevm_data_type(data_type_t):
value_size=1,
menu_name="SimpleVM",
asm_keyword=ASM_KEYWORD):
data_type_t.__init__(
ida_bytes.data_type_t.__init__(
self,
name,
value_size,
@@ -74,22 +89,22 @@ class simplevm_data_type(data_type_t):
asm_keyword)
def calc_item_size(self, ea, maxsize):
if idaapi.is_member_id(ea):
if ida_struct.is_member_id(ea):
return 1
# get the opcode and see if it has an imm
n = 5 if (idaapi.get_byte(ea) & 3) == 0 else 1
n = 5 if (ida_bytes.get_byte(ea) & 3) == 0 else 1
# string too big?
if n > maxsize:
return 0
# ok, accept
return n
class simplevm_data_format(data_format_t):
class simplevm_data_format(ida_bytes.data_format_t):
def __init__(
self,
name="py_simple_vm_format",
menu_name="SimpleVM"):
data_format_t.__init__(
ida_bytes.data_format_t.__init__(
self,
name,
0,
@@ -116,9 +131,9 @@ class simplevm_data_format(data_format_t):
imm = None
sz = 1
text = "%s %s, %s" % (
COLSTR(simplevm_data_format.INST[op], idaapi.SCOLOR_INSN),
COLSTR(simplevm_data_format.REGS[r1], idaapi.SCOLOR_REG),
COLSTR("0x%08X" % imm, idaapi.SCOLOR_NUMBER) if imm is not None else COLSTR(simplevm_data_format.REGS[r2], idaapi.SCOLOR_REG))
ida_lines.COLSTR(simplevm_data_format.INST[op], ida_lines.SCOLOR_INSN),
ida_lines.COLSTR(simplevm_data_format.REGS[r1], ida_lines.SCOLOR_REG),
ida_lines.COLSTR("0x%08X" % imm, ida_lines.SCOLOR_NUMBER) if imm is not None else ida_lines.COLSTR(simplevm_data_format.REGS[r2], ida_lines.SCOLOR_REG))
return (sz, text)
def printf(self, value, current_ea, operand_num, dtid):
@@ -131,9 +146,9 @@ class simplevm_data_format(data_format_t):
# -----------------------------------------------------------------------
# This format will display DWORD values as MAKE_DWORD(0xHI, 0xLO)
class makedword_data_format(data_format_t):
class makedword_data_format(ida_bytes.data_format_t):
def __init__(self):
data_format_t.__init__(
ida_bytes.data_format_t.__init__(
self,
"py_makedword",
4,
@@ -156,14 +171,14 @@ class makedword_data_format(data_format_t):
#
# The get_rsrc_string() is not optimal since it loads/unloads the
# DLL each time for a new string. It can be improved in many ways.
class rsrc_string_format(data_format_t):
class rsrc_string_format(ida_bytes.data_format_t):
def __init__(self):
data_format_t.__init__(
ida_bytes.data_format_t.__init__(
self,
"py_w32rsrcstring",
1,
"Resource string")
self.cache_node = idaapi.netnode("$ py_w32rsrcstring", 0, 1)
self.cache_node = ida_netnode.netnode("$ py_w32rsrcstring", 0, 1)
def get_rsrc_string(self, fn, id):
"""
@@ -188,8 +203,8 @@ class rsrc_string_format(data_format_t):
# Not cached?
if val == None:
# Retrieve it
num = idaapi.struct_unpack(value)
val = self.get_rsrc_string(idaapi.get_input_file_path(), num)
num = ida_idaapi.struct_unpack(value)
val = self.get_rsrc_string(ida_nalt.get_input_file_path(), num)
# Cache it
self.cache_node.supset(current_ea, val)
@@ -197,7 +212,7 @@ class rsrc_string_format(data_format_t):
if val == "" or val == "\x00":
return None
# Return the format
return "RSRC_STR(\"%s\")" % COLSTR(val, idaapi.SCOLOR_IMPNAME)
return "RSRC_STR(\"%s\")" % ida_lines.COLSTR(val, ida_lines.SCOLOR_IMPNAME)
# -----------------------------------------------------------------------
# Table of formats and types to be registered/unregistered
@@ -219,21 +234,23 @@ except:
# -----------------------------------------------------------------------
def nw_handler(code, old=0):
# delete notifications
if code == NW_OPENIDB:
if not idaapi.register_data_types_and_formats(new_formats):
if code == ida_idaapi.NW_OPENIDB:
if not ida_bytes.register_data_types_and_formats(new_formats):
print("Failed to register types!")
elif code == NW_CLOSEIDB:
idaapi.unregister_data_types_and_formats(new_formats)
elif code == NW_TERMIDA:
idaapi.notify_when(NW_TERMIDA | NW_OPENIDB | NW_CLOSEIDB | NW_REMOVE, nw_handler)
elif code == ida_idaapi.NW_CLOSEIDB:
ida_bytes.unregister_data_types_and_formats(new_formats)
elif code == ida_idaapi.NW_TERMIDA:
f = ida_idaapi.NW_TERMIDA | ida_idaapi.NW_OPENIDB | ida_idaapi.NW_CLOSEIDB | ida_idaapi.NW_REMOVE
ida_idaapi.notify_when(f, nw_handler)
# -----------------------------------------------------------------------
# Check if already installed
if idaapi.find_custom_data_type(pascal_data_format.FORMAT_NAME) == -1:
if not idaapi.register_data_types_and_formats(new_formats):
if ida_bytes.find_custom_data_type(pascal_data_format.FORMAT_NAME) == -1:
if not ida_bytes.register_data_types_and_formats(new_formats):
print("Failed to register types!")
else:
idaapi.notify_when(NW_TERMIDA | NW_OPENIDB | NW_CLOSEIDB, nw_handler)
f = ida_idaapi.NW_TERMIDA | ida_idaapi.NW_OPENIDB | ida_idaapi.NW_CLOSEIDB
ida_idaapi.notify_when(f, nw_handler)
print("Formats installed!")
else:
print("Formats already installed!")
+84
View File
@@ -0,0 +1,84 @@
"""
summary: retrieve extra comments
description:
Use the `ida_lines.get_extra_cmt` API to retrieve anterior
and posterior extra comments.
This script registers two actions, that can be used to dump
the previous and next extra comments.
"""
from __future__ import print_function
import ida_lines
import ida_kernwin
# -----------------------------------------------------------------------
class dump_at_point_handler_t(ida_kernwin.action_handler_t):
def __init__(self, anchor):
ida_kernwin.action_handler_t.__init__(self)
self.anchor = anchor
def activate(self, ctx):
ea = ida_kernwin.get_screen_ea()
index = self.anchor
while True:
cmt = ida_lines.get_extra_cmt(ea, index)
if cmt is None:
break
print("Got: '%s'" % cmt)
index += 1
def update(self, ctx):
return ida_kernwin.AST_ENABLE_FOR_WIDGET \
if ctx.widget_type == ida_kernwin.BWN_DISASM \
else ida_kernwin.AST_DISABLE_FOR_WIDGET
@staticmethod
def compose_action_name(v):
return "dump_extra_comments:%s" % v
# --------------------------------------------------------
# action variants
class action_previous_handler_t(dump_at_point_handler_t):
ACTION_LABEL = "previous"
ACTION_SHORTCUT = "Ctrl+Shift+Y"
def __init__(self):
super(action_previous_handler_t, self).__init__(ida_lines.E_PREV)
class action_next_handler_t(dump_at_point_handler_t):
ACTION_LABEL = "next"
ACTION_SHORTCUT = "Ctrl+Shift+Z"
def __init__(self):
super(action_next_handler_t, self).__init__(ida_lines.E_NEXT)
# -----------------------------------------------------------------------
# create actions (and attach them to IDA View-A's context menu if possible)
widget_title = "IDA View-A"
ida_view = ida_kernwin.find_widget(widget_title)
action_variants = [
action_previous_handler_t,
action_next_handler_t,
]
for variant in action_variants:
actname = dump_at_point_handler_t.compose_action_name(variant.ACTION_LABEL)
if ida_kernwin.unregister_action(actname):
print("Unregistered previously-registered action \"%s\"" % actname)
desc = ida_kernwin.action_desc_t(
actname,
"Dump %s extra comments" % variant.ACTION_LABEL,
variant(),
variant.ACTION_SHORTCUT)
if ida_kernwin.register_action(desc):
print("Registered action \"%s\"" % actname)
if ida_view and ida_kernwin.attach_action_to_popup(ida_view, None, actname):
print("Permanently attached action \"%s\" to \"%s\"" % (actname, widget_title))
+53 -28
View File
@@ -1,44 +1,69 @@
# -*- coding: utf-8 -*-
"""
summary: dump function flowchart
description:
Dumps the current function's flowchart, using 2 methods:
* the low-level `ida_gdl.qflow_chart_t` type
* the somewhat higher-level, and slightly more pythonic
`ida_gdl.FlowChart` type.
"""
from __future__ import print_function
import idaapi
import ida_gdl
import ida_funcs
import ida_kernwin
def out(p, msg):
if p:
print(msg)
def out_succ(p, start_ea, end_ea):
out(p, " SUCC: %x - %x" % (start_ea, end_ea))
def out_pred(p, start_ea, end_ea):
out(p, " PRED: %x - %x" % (start_ea, end_ea))
# -----------------------------------------------------------------------
# Using raw IDAAPI
def raw_main(p=True):
f = idaapi.get_func(here())
# Using ida_gdl.qflow_chart_t
def using_qflow_chart_t(ea, p=True):
f = ida_funcs.get_func(ea)
if not f:
return
q = idaapi.qflow_chart_t("The title", f, 0, 0, idaapi.FC_PREDS)
for n in range(0, q.size()):
q = ida_gdl.qflow_chart_t("The title", f, 0, 0, 0)
for n in range(q.size()):
b = q[n]
if p:
print("%x - %x [%d]:" % (b.start_ea, b.end_ea, n))
out(p, "%x - %x [%d]:" % (b.start_ea, b.end_ea, n))
for ns in range(q.nsucc(n)):
b2 = q[q.succ(n, ns)]
out_succ(p, b2.start_ea, b2.end_ea)
for ns in range(0, q.nsucc(n)):
if p:
print("SUCC: %d->%d" % (n, q.succ(n, ns)))
for ns in range(0, q.npred(n)):
if p:
print("PRED: %d->%d" % (n, q.pred(n, ns)))
for ns in range(q.npred(n)):
b2 = q[q.pred(n, ns)]
out_pred(p, b2.start_ea, b2.end_ea)
# -----------------------------------------------------------------------
# Using the class
def cls_main(p=True):
f = idaapi.FlowChart(idaapi.get_func(here()))
# Using ida_gdl.FlowChart
def using_FlowChart(ea, p=True):
f = ida_gdl.FlowChart(ida_funcs.get_func(ea))
for block in f:
if p:
print("%x - %x [%d]:" % (block.start_ea, block.end_ea, block.id))
out(p, "%x - %x [%d]:" % (block.start_ea, block.end_ea, block.id))
for succ_block in block.succs():
if p:
print(" %x - %x [%d]:" % (succ_block.start_ea, succ_block.end_ea, succ_block.id))
out_succ(p, succ_block.start_ea, succ_block.end_ea)
for pred_block in block.preds():
if p:
print(" %x - %x [%d]:" % (pred_block.start_ea, pred_block.end_ea, pred_block.id))
out_pred(p, pred_block.start_ea, pred_block.end_ea)
q = None
f = None
raw_main(False)
cls_main(True)
ea = ida_kernwin.get_screen_ea()
print(">>> Dumping flow chart using ida_gdl.qflow_chart_t")
using_qflow_chart_t(ea)
print(">>> Dumping flow chart using the higher-level ida_gdl.FlowChart")
using_FlowChart(ea)
+73
View File
@@ -0,0 +1,73 @@
"""
summary: retrieve & dump current selection
description:
Shows how to retrieve the selection from a listing
widget ("IDA View-A", "Hex View-1", "Pseudocode-A", ...) as
two "cursors", and from there retrieve (in fact, generate)
the corresponding text.
After running this script:
* select some text in one of the listing widgets (i.e.,
"IDA View-*", "Enums", "Structures", "Pseudocode-*")
* press Ctrl+Shift+S to dump the selection
"""
from __future__ import print_function
import ida_kernwin
import ida_lines
class dump_selection_handler_t(ida_kernwin.action_handler_t):
def activate(self, ctx):
if ctx.has_flag(ida_kernwin.ACF_HAS_SELECTION):
tp0, tp1 = ctx.cur_sel._from, ctx.cur_sel.to
ud = ida_kernwin.get_viewer_user_data(ctx.widget)
lnar = ida_kernwin.linearray_t(ud)
lnar.set_place(tp0.at)
lines = []
while True:
cur_place = lnar.get_place()
first_line_ref = ida_kernwin.l_compare2(cur_place, tp0.at, ud)
last_line_ref = ida_kernwin.l_compare2(cur_place, tp1.at, ud)
if last_line_ref > 0: # beyond last line
break
line = ida_lines.tag_remove(lnar.down())
if last_line_ref == 0: # at last line
line = line[0:tp1.x]
elif first_line_ref == 0: # at first line
line = ' ' * tp0.x + line[tp0.x:]
lines.append(line)
for line in lines:
print(line)
return 1
def update(self, ctx):
ok_widgets = [
ida_kernwin.BWN_DISASM,
ida_kernwin.BWN_STRUCTS,
ida_kernwin.BWN_ENUMS,
ida_kernwin.BWN_PSEUDOCODE,
]
return ida_kernwin.AST_ENABLE_FOR_WIDGET \
if ctx.widget_type in ok_widgets \
else ida_kernwin.AST_DISABLE_FOR_WIDGET
# -----------------------------------------------------------------------
# create actions (and attach them to IDA View-A's context menu if possible)
ACTION_NAME = "dump_selection"
ACTION_SHORTCUT = "Ctrl+Shift+S"
if ida_kernwin.unregister_action(ACTION_NAME):
print("Unregistered previously-registered action \"%s\"" % ACTION_NAME)
if ida_kernwin.register_action(
ida_kernwin.action_desc_t(
ACTION_NAME,
"Dump selection",
dump_selection_handler_t(),
ACTION_SHORTCUT)):
print("Registered action \"%s\"" % ACTION_NAME)
+22 -16
View File
@@ -1,21 +1,27 @@
"""
summary: add functions to the IDC runtime from IDAPython
description:
You can add IDC functions to IDA, whose "body" consists of
IDAPython statements!
We'll register a 'pow' function, available to all IDC code,
that when invoked will call back into IDAPython, and execute
the provided function body.
After running this script, try switching to the IDC interpreter
(using the button on the lower-left corner of IDA) and executing
`pow(3, 7)`
"""
from __future__ import print_function
# -----------------------------------------------------------------------
# This is an example illustrating how to extend IDC from Python
# (c) Hex-Rays
#
from idaapi import add_idc_func
def py_power(n, e):
return n ** e
import ida_expr
desc = ext_idcfunc_t
desc.name = "pow"
desc.func = py_power,
desc.args = (idaapi.VT_LONG, idaapi.VT_LONG),
desc.defvals = ()
desc.flags = 0
ok = add_idc_func(desc)
if ok:
print("Now the pow() will be present IDC!")
if ida_expr.add_idc_func(
"pow",
lambda n, e: n ** e,
(ida_expr.VT_LONG, ida_expr.VT_LONG)):
print("The pow() function is now available in IDC")
else:
print("Failed to register pow() IDC function")
+18 -10
View File
@@ -1,19 +1,27 @@
#---------------------------------------------------------------------
# Example user initialisation script: idapythonrc.py
#
# Place this script to ~/.idapro/ or to
# %APPDATA%\Hex-Rays\IDA Pro
#---------------------------------------------------------------------
import idaapi
"""
summary: code to be run right after IDAPython initialization
description:
The `idapythonrc.py` file:
* %APPDATA%\Hex-Rays\IDA Pro\idapythonrc.py (on Windows)
* ~/.idapro/idapythonrc.py (on Linux & Mac)
can contain any IDAPython code that will be run as soon as
IDAPython is done successfully initializing.
"""
# Add your favourite script to ScriptBox for easy access
# scriptbox.addscript("/here/is/my/favourite/script.py")
# Uncomment if you want to set Python as default interpreter in IDA
# idaapi.enable_extlang_python(True)
# import ida_idaapi
# ida_idaapi.enable_extlang_python(True)
# Disable the Python from interactive command-line
# idaapi.enable_python_cli(False)
# import ida_idaapi
# ida_idaapi.enable_python_cli(False)
# Set the timeout for the script execution cancel dialog
# idaapi.set_script_timeout(10)
# import ida_idaapi
# ida_idaapi.set_script_timeout(10)
+30 -22
View File
@@ -1,41 +1,49 @@
"""
summary: inserting information into disassembly prefixes
description:
By default, disassembly line prefixes contain segment + address
information (e.g., '.text:08047718'), but it is possible to
"inject" other bits of information in there, thanks to the
`ida_lines.user_defined_prefix_t` helper type.
"""
from __future__ import print_function
import idaapi
PREFIX = idaapi.SCOLOR_INV + ' ' + idaapi.SCOLOR_INV
import ida_lines
import ida_idaapi
class prefix_plugin_t(idaapi.plugin_t):
flags = 0
comment = "This is a user defined prefix sample plugin"
help = "This is help"
wanted_name = "user defined prefix"
wanted_hotkey = ""
def user_prefix(self, ea, lnnum, indent, line, bufsize):
#print("ea=%x lnnum=%d indent=%d line=%s bufsize=%d" % (ea, lnnum, indent, line, bufsize))
PREFIX = ida_lines.SCOLOR_INV + ' ' + ida_lines.SCOLOR_INV
class my_user_prefix_t(ida_lines.user_defined_prefix_t):
def get_user_defined_prefix(self, ea, insn, lnnum, indent, line):
if (ea % 2 == 0) and indent == -1:
return PREFIX
else:
return ""
class prefix_plugin_t(ida_idaapi.plugin_t):
flags = 0
comment = "This is a user defined prefix sample plugin"
help = "This is help"
wanted_name = "user defined prefix"
wanted_hotkey = ""
def __init__(self):
self.prefix = None
def init(self):
self.prefix_installed = idaapi.set_user_defined_prefix(8, self.user_prefix)
if self.prefix_installed:
print("prefix installed")
return idaapi.PLUGIN_KEEP
self.prefix = my_user_prefix_t(8)
print("prefix installed")
return ida_idaapi.PLUGIN_KEEP
def run(self, arg):
pass
def term(self):
if self.prefix_installed:
idaapi.set_user_defined_prefix(0, None)
print("prefix uninstalled!")
self.prefix = None
print("prefix uninstalled!")
def PLUGIN_ENTRY():
+102
View File
@@ -0,0 +1,102 @@
"""
summary: showcases (a few of) the iterators available on a function
description:
This demonstrates how to use some of the iterators available on the func_t type.
This example will focus on:
* `func_t[.__iter__]`: the default iterator; iterates on instructions
* `func_t.data_items`: iterate on data items contained within a function
* `func_t.head_items`: iterate on 'heads' (i.e., addresses containing
the start of an instruction, or a data item.
* `func_t.addresses`: iterate on all addresses within function (code
and data, beginning of an item or not)
Type `help(ida_funcs.func_t)` for a full list of iterators.
In addition, one can use:
* `func_tail_iterator_t`: iterate on all the chunks (including
the main one) of the function
* `func_parent_iterator_t`: iterate on all the parent functions,
that include this chunk
keywords: funcs iterator
"""
import ida_bytes
import ida_kernwin
import ida_funcs
import ida_ua
class logger_t(object):
class section_t(object):
def __init__(self, logger, header):
self.logger = logger
self.logger.log(header)
def __enter__(self):
self.logger.indent += 2
return self
def __exit__(self, tp, value, traceback):
self.logger.indent -= 2
if value:
return False # Re-raise
def __init__(self):
self.indent = 0
def log(self, *args):
print(" " * self.indent + "".join(args))
def log_ea(self, ea):
F = ida_bytes.get_flags(ea)
parts = ["0x%08x" % ea, ": "]
if ida_bytes.is_code(F):
parts.append("instruction (%s)" % ida_ua.print_insn_mnem(ea))
if ida_bytes.is_data(F):
parts.append("data")
if ida_bytes.is_tail(F):
parts.append("tail")
if ida_bytes.is_unknown(F):
parts.append("unknown")
if ida_funcs.get_func(ea) != ida_funcs.get_fchunk(ea):
parts.append(" (in function chunk)")
self.log(*parts)
def main():
# Get current ea
ea = ida_kernwin.get_screen_ea()
pfn = ida_funcs.get_func(ea)
if pfn is None:
print("No function defined at 0x%x" % ea)
return
func_name = ida_funcs.get_func_name(pfn.start_ea)
logger = logger_t()
logger.log("Function %s at 0x%x" % (func_name, ea))
with logger_t.section_t(logger, "Code items:"):
for item in pfn:
logger.log_ea(item)
with logger_t.section_t(logger, "'head' items:"):
for item in pfn.head_items():
logger.log_ea(item)
with logger_t.section_t(logger, "Addresses:"):
for item in pfn.addresses():
logger.log_ea(item)
with logger_t.section_t(logger, "Function chunks:"):
for chunk in ida_funcs.func_tail_iterator_t(pfn):
logger.log("%s chunk: 0x%08x..0x%08x" % (
"Main" if chunk.start_ea == pfn.start_ea else "Tail",
chunk.start_ea,
chunk.end_ea))
if __name__ == '__main__':
main()
+22 -19
View File
@@ -1,30 +1,33 @@
"""
summary: enumerate file imports
description:
Using the API to enumerate file imports.
"""
from __future__ import print_function
# -----------------------------------------------------------------------
# This is an example illustrating how to enumerate imports
# (c) Hex-Rays
#
import idaapi
def imp_cb(ea, name, ord):
if not name:
print("%08x: ord#%d" % (ea, ord))
else:
print("%08x: %s (ord#%d)" % (ea, name, ord))
# True -> Continue enumeration
# False -> Stop enumeration
return True
import ida_nalt
nimps = idaapi.get_import_module_qty()
nimps = ida_nalt.get_import_module_qty()
print("Found %d import(s)..." % nimps)
for i in range(0, nimps):
name = idaapi.get_import_module_name(i)
for i in range(nimps):
name = ida_nalt.get_import_module_name(i)
if not name:
print("Failed to get import module name for #%d" % i)
continue
name = "<unnamed>"
print("Walking-> %s" % name)
idaapi.enum_import_names(i, imp_cb)
print("Walking imports for module %s" % name)
def imp_cb(ea, name, ordinal):
if not name:
print("%08x: ordinal #%d" % (ea, ordinal))
else:
print("%08x: %s (ordinal #%d)" % (ea, name, ordinal))
# True -> Continue enumeration
# False -> Stop enumeration
return True
ida_nalt.enum_import_names(i, imp_cb)
print("All done...")
+14 -8
View File
@@ -1,9 +1,15 @@
from __future__ import print_function
# -------------------------------------------------------------------------
# This is an example illustrating how to visit all patched bytes in Python
# (c) Hex-Rays
"""
summary: enumerate patched bytes
import idaapi
description:
Using the API to iterate over all the places in the file,
that were patched using IDA.
"""
from __future__ import print_function
import ida_bytes
import ida_idaapi
# -------------------------------------------------------------------------
class patched_bytes_visitor(object):
@@ -14,7 +20,7 @@ class patched_bytes_visitor(object):
def __call__(self, ea, fpos, o, v, cnt=()):
if fpos == -1:
self.skip += 1
print(" ea: %x o: %x v: %x...skipped" % (ea, fpos, o, v))
print(" ea: %x o: %x v: %x...skipped" % (ea, o, v))
else:
self.patch += 1
print(" ea: %x fpos: %x o: %x v: %x" % (ea, fpos, o, v))
@@ -25,7 +31,7 @@ class patched_bytes_visitor(object):
def main():
print("Visiting all patched bytes:")
v = patched_bytes_visitor()
r = idaapi.visit_patched_bytes(0, idaapi.BADADDR, v)
r = ida_bytes.visit_patched_bytes(0, ida_idaapi.BADADDR, v)
if r != 0:
print("visit_patched_bytes() returned %d" % r)
else:
@@ -34,4 +40,4 @@ def main():
# -------------------------------------------------------------------------
if __name__ == '__main__':
main()
main()
+37
View File
@@ -0,0 +1,37 @@
"""
summary: enumerate problems
description:
Using the API to list all problem[atic situation]s that IDA
encountered during analysis.
"""
import ida_ida
import ida_idaapi
import ida_problems
for ptype in [
ida_problems.PR_NOBASE,
ida_problems.PR_NONAME,
ida_problems.PR_NOFOP,
ida_problems.PR_NOCMT,
ida_problems.PR_NOXREFS,
ida_problems.PR_JUMP,
ida_problems.PR_DISASM,
ida_problems.PR_HEAD,
ida_problems.PR_ILLADDR,
ida_problems.PR_MANYLINES,
ida_problems.PR_BADSTACK,
ida_problems.PR_ATTN,
ida_problems.PR_FINAL,
ida_problems.PR_ROLLED,
ida_problems.PR_COLLISION,
ida_problems.PR_DECIMP,
]:
plistdesc = ida_problems.get_problem_name(ptype)
ea = ida_ida.inf_get_min_ea()
while True:
ea = ida_problems.get_problem(ptype, ea+1)
if ea == ida_idaapi.BADADDR:
break
print("0x%08x: %s" % (ea, plistdesc))
+28 -13
View File
@@ -1,3 +1,15 @@
"""
summary: list all functions (and xrefs) in segment
description:
List all the functions in the current segment, as well as
all the cross-references to them.
keywords: xrefs
see_also: list_segment_functions_using_idautils
"""
from __future__ import print_function
#
# Reference Lister
@@ -6,36 +18,39 @@ from __future__ import print_function
#
# Implemented using direct IDA Plugin API calls
#
from idaapi import *
import ida_kernwin
import ida_segment
import ida_funcs
import ida_xref
import ida_idaapi
def main():
# Get current ea
ea = get_screen_ea()
ea = ida_kernwin.get_screen_ea()
# Get segment class
seg = getseg(ea)
seg = ida_segment.getseg(ea)
# Loop from segment start to end
func_ea = seg.startEA
func_ea = seg.start_ea
# Get a function at the start of the segment (if any)
func = get_func(func_ea)
func = ida_funcs.get_func(func_ea)
if func is None:
# No function there, try to get the next one
func = get_next_func(func_ea)
func = ida_funcs.get_next_func(func_ea)
seg_end = seg.end_ea
while func is not None and func.start_ea < seg_end:
funcea = func.start_ea
print("Function %s at 0x%x" % (get_func_name(funcea), funcea))
print("Function %s at 0x%x" % (ida_funcs.get_func_name(funcea), funcea))
ref = get_first_cref_to(funcea)
xb = ida_xref.xrefblk_t()
for ref in xb.crefs_to(funcea):
print(" called from %s(0x%x)" % (ida_funcs.get_func_name(ref), ref))
while ref != BADADDR:
print(" called from %s(0x%x)" % (get_func_name(ref), ref))
ref = get_next_cref_to(funcea, ref)
func = get_next_func(funcea)
func = ida_funcs.get_next_func(funcea)
main()
@@ -1,3 +1,18 @@
"""
summary: list all functions (and xrefs) in segment
description:
List all the functions in the current segment, as well as
all the cross-references to them.
Contrary to @list_segment_functions, this uses the somewhat
higher-level `idautils` module.
keywords: xrefs
see_also: list_segment_functions
"""
from __future__ import print_function
#
# Reference Lister
@@ -6,23 +21,32 @@ from __future__ import print_function
#
# Implemented with the idautils module
#
from idautils import *
import ida_kernwin
import ida_idaapi
import ida_segment
import ida_funcs
import idautils
def main():
# Get current ea
ea = get_screen_ea()
if ea == idaapi.BADADDR:
ea = ida_kernwin.get_screen_ea()
if ea == ida_idaapi.BADADDR:
print("Could not get get_screen_ea()")
return
# Loop from start to end in the current segment
for funcea in Functions(get_segm_start(ea), get_segm_end(ea)):
print("Function %s at 0x%x" % (get_func_name(funcea), funcea))
# Find all code references to funcea
for ref in CodeRefsTo(funcea, 1):
print(" called from %s(0x%x)" % (get_func_name(ref), ref))
seg = ida_segment.getseg(ea)
if seg:
# Loop from start to end in the current segment
for funcea in idautils.Functions(seg.start_ea, seg.end_ea):
print("Function %s at 0x%x" % (ida_funcs.get_func_name(funcea), funcea))
# Find all code references to funcea
for ref in idautils.CodeRefsTo(funcea, 1):
print(" called from %s(0x%x)" % (ida_funcs.get_func_name(ref), ref))
else:
print("Please position the cursor within a segment")
if __name__=='__main__':
main()
main()
+64
View File
@@ -0,0 +1,64 @@
"""
summary: list all xrefs to a function stack variable
description:
Contrary to (in-memory) data & code xrefs, retrieving stack variables
xrefs requires a bit more work than just using ida_xref's first_to(),
next_to() (or higher level utilities such as idautils.XrefsTo)
keywords: xrefs
"""
ACTION_NAME = "list_stkvar_xrefs:list"
ACTION_SHORTCUT = "Ctrl+Shift+F7"
import ida_bytes
import ida_frame
import ida_funcs
import ida_ida
import ida_kernwin
import ida_struct
import ida_ua
class list_stkvar_xrefs_ah_t(ida_kernwin.action_handler_t):
def activate(self, ctx):
cur_ea = ida_kernwin.get_screen_ea()
pfn = ida_funcs.get_func(cur_ea)
if pfn:
v = ida_kernwin.get_current_viewer()
result = ida_kernwin.get_highlight(v)
if result:
stkvar_name, _ = result
frame = ida_frame.get_frame(cur_ea)
sptr = ida_struct.get_struc(frame.id)
mptr = ida_struct.get_member_by_name(sptr, stkvar_name)
if mptr:
for ea in pfn:
F = ida_bytes.get_flags(ea)
for n in range(ida_ida.UA_MAXOP):
if not ida_bytes.is_stkvar(F, n):
continue
insn = ida_ua.insn_t()
if not ida_ua.decode_insn(insn, ea):
continue
v = ida_frame.calc_stkvar_struc_offset(pfn, insn, n)
if v >= mptr.soff and v < mptr.eoff:
print("Found xref at 0x%08x, operand #%d" % (ea, n))
else:
print("No stack variable named \"%s\"" % stkvar_name)
else:
print("Please position the cursor within a function")
def update(self, ctx):
return ida_kernwin.AST_ENABLE_FOR_WIDGET \
if ctx.widget_type == ida_kernwin.BWN_DISASM \
else ida_kernwin.AST_DISABLE_FOR_WIDGET
adesc = ida_kernwin.action_desc_t(
ACTION_NAME,
"List stack variable xrefs",
list_stkvar_xrefs_ah_t(),
ACTION_SHORTCUT)
if ida_kernwin.register_action(adesc):
print("Action registered. Please press \"%s\" to use" % ACTION_SHORTCUT)
+14 -2
View File
@@ -1,10 +1,22 @@
"""
summary: retrieve the strings that are present in the IDB
description:
This uses `idautils.Strings` to iterate over the string literals
that are present in the IDB. Contrary to @show_selected_strings,
this will not require that the "Strings" window is opened & available.
see_also: show_selected_strings
"""
from __future__ import print_function
import ida_nalt
import idautils
s = idautils.Strings(False)
s.setup(strtypes=Strings.STR_UNICODE | Strings.STR_C)
s.setup(strtypes=[ida_nalt.STRTYPE_C, ida_nalt.STRTYPE_C_16])
for i, v in enumerate(s):
if v is None:
print("Failed to retrieve string index %d" % i)
else:
print("%x: len=%d type=%d index=%d-> '%s'" % (v.ea, v.length, v.type, i, str(v)))
print("%x: len=%d type=0x%x index=%d-> '%s'" % (v.ea, v.length, v.strtype, i, str(v)))
+37
View File
@@ -0,0 +1,37 @@
"""
summary: decompile entire file
description:
automate IDA to perform auto-analysis on a file and,
once that is done, produce a .c file containing the
decompilation of all the functions in that file.
Run like so:
ida -A "-S...path/to/produce_c_file.py" <binary-file>
where:
* -A instructs IDA to run in non-interactive mode
* -S holds a path to the script to run (note this is a single token;
there is no space between '-S' and its path.)
"""
import ida_pro
import ida_auto
import ida_loader
import ida_hexrays
# derive output file name
idb_path = ida_loader.get_path(ida_loader.PATH_TYPE_IDB)
c_path = "%s.c" % idb_path
ida_auto.auto_wait() # wait for end of auto-analysis
ida_hexrays.decompile_many( # generate .c file
c_path,
None,
ida_hexrays.VDRUN_NEWFILE
|ida_hexrays.VDRUN_SILENT
|ida_hexrays.VDRUN_MAYSTOP)
ida_pro.qexit(0)
+42
View File
@@ -0,0 +1,42 @@
"""
summary: produce listing
description:
automate IDA to perform auto-analysis on a file and,
once that is done, produce a .lst file with the disassembly.
Run like so:
ida -A "-S...path/to/produce_lst_file.py" <binary-file>
where:
* -A instructs IDA to run in non-interactive mode
* -S holds a path to the script to run (note this is a single token;
there is no space between '-S' and its path.)
"""
import ida_auto
import ida_fpro
import ida_ida
import ida_loader
import ida_pro
# derive output file name
idb_path = ida_loader.get_path(ida_loader.PATH_TYPE_IDB)
lst_path = "%s.lst" % idb_path
ida_auto.auto_wait() # wait for end of auto-analysis
fptr = ida_fpro.qfile_t() # FILE * wrapper
if fptr.open(lst_path, "wt"):
try:
ida_loader.gen_file( # generate .lst file
ida_loader.OFILE_LST,
fptr.get_fp(),
ida_ida.inf_get_min_ea(),
ida_ida.inf_get_max_ea(),
0)
finally:
fptr.close()
ida_pro.qexit(0)
+14 -10
View File
@@ -1,15 +1,19 @@
from __future__ import print_function
# -------------------------------------------------------------------------
# This is an example illustrating how to use timers
# (c) Hex-Rays
"""
summary: using timers for delayed execution
import idaapi
description:
Register (possibly repeating) timers.
"""
from __future__ import print_function
import ida_kernwin
# -------------------------------------------------------------------------
class timercallback_t(object):
def __init__(self):
self.interval = 1000
self.obj = idaapi.register_timer(self.interval, self)
self.obj = ida_kernwin.register_timer(self.interval, self)
if self.obj is None:
raise RuntimeError("Failed to register timer")
self.times = 5
@@ -21,19 +25,19 @@ class timercallback_t(object):
return -1 if self.times == 0 else self.interval
def __del__(self):
print("Timer object disposed %s" % id(self))
print("Timer object disposed %s" % self)
# -------------------------------------------------------------------------
def main():
try:
t = timercallback_t()
# No need to unregister the timer.
# It will unregister itself in the callback when it returns -1
# No need to unregister the timer.
# It will unregister itself in the callback when it returns -1
except Exception as e:
print("Error: %s" % e)
# -------------------------------------------------------------------------
if __name__ == '__main__':
main()
main()
@@ -1,12 +1,23 @@
"""
summary: execute existing actions programmatically
description:
It's possible to invoke any action programmatically, by using
either of those two:
* ida_kernwin.execute_ui_requests()
* ida_kernwin.process_ui_action()
Ideally, this script should be run through the "File > Script file..."
menu, so as to keep focus on "IDA View-A" and have the
'ProcessUiActions' part work as intended.
keywords: actions
"""
from __future__ import print_function
# -----------------------------------------------------------------------
# This is an example illustrating how to use the execute_ui_requests()
# and the idautils.ProcessUiActions()
# (c) Hex-Rays
#
import idaapi
import idautils
import idc
import ida_kernwin
# --------------------------------------------------------------------------
class __process_ui_actions_helper(object):
@@ -34,9 +45,9 @@ class __process_ui_actions_helper(object):
return False
# Execute one action
idaapi.process_ui_action(
self.__action_list[self.__idx],
self.__flags)
aname = self.__action_list[self.__idx]
print("executing: %s (flags=0x%x)" % (aname, self.__flags))
print("=> %s" % ida_kernwin.process_ui_action(aname, self.__flags))
# Move to next action
self.__idx += 1
@@ -55,7 +66,7 @@ def ProcessUiActions(actions, flags=0):
# Instantiate a helper
helper = __process_ui_actions_helper(actions, flags)
return False if len(helper) < 1 else idaapi.execute_ui_requests((helper,))
return False if len(helper) < 1 else ida_kernwin.execute_ui_requests((helper,))
# --------------------------------------------------------------------------
@@ -63,14 +74,15 @@ class print_req_t(object):
def __init__(self, s):
self.s = s
def __call__(self):
idaapi.msg("%s" % self.s)
ida_kernwin.msg("%s" % self.s)
return False # Don't reschedule
if idc.ask_yn(1,("HIDECANCEL\nDo you want to run execute_ui_requests() example?\n"
"Press NO to execute ProcessUiActions() example\n")):
idaapi.execute_ui_requests(
(print_req_t("Hello"), print_req_t(" world\n")) )
if ida_kernwin.ask_yn(
1, ("HIDECANCEL\nDo you want to run execute_ui_requests() example?\n"
"Press NO to execute ProcessUiActions() example\n")):
ida_kernwin.execute_ui_requests(
(print_req_t("Hello"),
print_req_t(" world\n")) )
else:
ProcessUiActions("JumpQ;JumpName")
ProcessUiActions("JumpQ;Breakpoints")
@@ -0,0 +1,46 @@
import ida_dbg
import ida_idaapi
import ida_idd
import ida_kernwin
import ida_typeinf
import ida_name
def log(msg):
print(">>> %s" % msg)
class appcall_hooks_t(ida_dbg.DBG_Hooks):
def __init__(self, name_funcs=[]):
ida_dbg.DBG_Hooks.__init__(self) # important
for ea, func_name in name_funcs:
log("Renaming 0x%08x to \"%s\"" % (ea, func_name))
ida_name.set_name(ea, func_name)
for func_name, func_proto in [
("ref4", "int ref4(int *);"),
("ref8", "int ref8(long long int *);"),
]:
log("Setting '%s's prototype" % func_name)
func_ea = ida_name.get_name_ea(ida_idaapi.BADADDR, func_name)
assert(ida_typeinf.apply_cdecl(None, func_ea, func_proto))
def dbg_run_to(self, pid, tid, ea):
log("'run_to' reached its target location. Performing appcalls.")
for func_name in ["ref4", "ref8"]:
int_value = ida_idd.Appcall.int64(5)
int_ptr = ida_idd.Appcall.byref(int_value)
if ida_idd.Appcall[func_name](int_ptr):
log("Appcall (%s) succeeded: int_value.value=%s, int_ptr.value=%s" % (
func_name,
int_value.value,
int_ptr.value))
else:
log("Appcall (%s) failed" % func_name)
def run(self):
log("Running program up to current address, and letting the hooks do the rest")
assert(ida_dbg.run_to(ida_kernwin.get_screen_ea()))
@@ -0,0 +1,32 @@
"""
summary: executing code into the application being debugged (on Linux)
description:
Using the `ida_idd.Appcall` utility to execute code in
the process being debugged.
This example will run the test program and stop wherever
the cursor currently is, and then perform an appcall to
execute the `ref4` and `ref8` functions.
To use this example:
* run `ida64` on test program `simple_appcall_linux64`, or
`ida` on test program `simple_appcall_linux32`, and wait for
auto-analysis to finish
* select the 'linux debugger' (either local, or remote)
* run this script
Note: the real body of code is in `simple_appcall_common.py`.
"""
from __future__ import print_function
import os
import sys
sys.path.append(os.path.dirname(__file__))
import simple_appcall_common
appcall_hooks = simple_appcall_common.appcall_hooks_t()
appcall_hooks.hook()
appcall_hooks.run()
@@ -0,0 +1,48 @@
"""
summary: executing code into the application being debugged (on Windows)
description:
Using the `ida_idd.Appcall` utility to execute code in
the process being debugged.
This example will run the test program and stop wherever
the cursor currently is, and then perform an appcall to
execute the `ref4` and `ref8` functions.
To use this example:
* run `ida64` on test program `simple_appcall_win64.exe`, or
`ida` on test program `simple_appcall_win32.exe`, and wait for
auto-analysis to finish
* select the 'windows debugger' (either local, or remote)
* run this script
Note: the real body of code is in `simple_appcall_common.py`.
"""
from __future__ import print_function
import os
import sys
sys.path.append(os.path.dirname(__file__))
# Windows binaries don't have any symbols, thus we'll have
# to assign names to addresses of interest before we can
# appcall them by name.
import ida_ida
if ida_ida.inf_is_64bit():
ref4_ea = 0x140001000
ref8_ea = 0x140001060
else:
ref4_ea = 0x401000
ref8_ea = 0x401050
import simple_appcall_common
appcall_hooks = simple_appcall_common.appcall_hooks_t(
name_funcs=[
(ref4_ea, "ref4"),
(ref8_ea, "ref8"),
])
appcall_hooks.hook()
appcall_hooks.run()
@@ -0,0 +1,47 @@
ifdef __NT__
EA32_TARGET:=simple_appcall_win32.exe
EA64_TARGET:=simple_appcall_win64.exe
else
ifdef __LINUX__
EA32_TARGET:=simple_appcall_linux32
EA64_TARGET:=simple_appcall_linux64
else
$(error Not implemented for OSX)
endif
endif
all: $(EA32_TARGET) $(EA64_TARGET)
simple_appcall_win32.exe: simple_appcall_win32.obj
C:/PROGRA~2/MIB055~1/2017/PROFES~1/VC/Tools/MSVC/1415~1.267/bin/HostX86/x86/link.exe \
/LIBPATH:C:/PROGRA~2/MIB055~1/2017/PROFES~1/VC/Tools/MSVC/1415~1.267/lib/x86 \
/LIBPATH:C:/PROGRA~2/WI3CF2~1/10/Lib/100171~1.0/ucrt/x86 \
/LIBPATH:C:/idasrc/THIRD_~1/mssdk/8.1/Lib/x86 \
/OUT:$@ $<
simple_appcall_win32.obj: simple_appcall.c
C:/PROGRA~2/MIB055~1/2017/PROFES~1/VC/Tools/MSVC/1415~1.267/bin/HostX86/x86/cl.exe \
/IC:/PROGRA~2/MIB055~1/2017/PROFES~1/VC/Tools/MSVC/1415~1.267/Include \
/IC:/PROGRA~2/WI3CF2~1/10/Include/100171~1.0/ucrt \
/Zi /D__NT__ /DNDEBUG /DWIN32 /D_CONSOLE /D__VC__ /c /MD $< /Fo$@
simple_appcall_win64.exe: simple_appcall_win64.obj
C:/PROGRA~2/MIB055~1/2017/PROFES~1/VC/Tools/MSVC/1415~1.267/bin/HostX86/x86/link.exe \
/LIBPATH:C:/PROGRA~2/MIB055~1/2017/PROFES~1/VC/Tools/MSVC/1415~1.267/lib/x64 \
/LIBPATH:C:/PROGRA~2/WI3CF2~1/10/Lib/100171~1.0/ucrt/x64 \
/LIBPATH:C:/idasrc/THIRD_~1/mssdk/8.1/Lib/x64 \
/OUT:$@ $<
simple_appcall_win64.obj: simple_appcall.c
C:/PROGRA~2/MIB055~1/2017/PROFES~1/VC/Tools/MSVC/1415~1.267/bin/HostX64/x64/cl.exe \
/IC:/PROGRA~2/MIB055~1/2017/PROFES~1/VC/Tools/MSVC/1415~1.267/Include \
/IC:/PROGRA~2/WI3CF2~1/10/Include/100171~1.0/ucrt \
/Zi /D__NT__ /DNDEBUG /DWIN32 /D_CONSOLE /D__VC__ /c /MD $< /Fo$@
simple_appcall_linux32: simple_appcall.c
gcc -m32 -o $@ $<
simple_appcall_linux64: simple_appcall.c
gcc -m64 -o $@ $<
@@ -0,0 +1,35 @@
#include <stdio.h>
typedef int int32;
int ref4(int32 *a)
{
if (a == NULL)
{
printf("ref4: no number passed!");
return -1;
}
printf("ref4: entered with %d\n", *a);
(*a)++;
return 1;
}
typedef long long int int64;
int ref8(int64 *a)
{
if (a == NULL)
{
printf("ref8: no number passed!");
return -1;
}
printf("ref8: entered with %lld\n", *a);
(*a)++;
return 1;
}
int main()
{
int32 x;
int res = ref4(&x);
int64 y;
return res + ref8(&y);
}
+48 -45
View File
@@ -1,42 +1,48 @@
from __future__ import print_function
#---------------------------------------------------------------------
# Debug notification hook test
#
# This script start the executable and steps through the first five
# instructions. Each instruction is disassembled after execution.
#
# Original Author: Gergely Erdelyi <gergely.erdelyi@d-dome.net>
#
# Maintained By: IDAPython Team
#
#---------------------------------------------------------------------
from idaapi import *
"""
summary: programmatically drive a debugging session
class MyDbgHook(DBG_Hooks):
description:
Start a debugging session, step through the first five
instructions. Each instruction is disassembled after
execution.
"""
from __future__ import print_function
import ida_dbg
import ida_ida
import ida_lines
class MyDbgHook(ida_dbg.DBG_Hooks):
""" Own debug hook class that implementd the callback functions """
def __init__(self):
ida_dbg.DBG_Hooks.__init__(self) # important
self.steps = 0
def log(self, msg):
print(">>> %s" % msg)
def dbg_process_start(self, pid, tid, ea, name, base, size):
print("Process started, pid=%d tid=%d name=%s" % (pid, tid, name))
self.log("Process started, pid=%d tid=%d name=%s" % (pid, tid, name))
def dbg_process_exit(self, pid, tid, ea, code):
print("Process exited pid=%d tid=%d ea=0x%x code=%d" % (pid, tid, ea, code))
self.log("Process exited pid=%d tid=%d ea=0x%x code=%d" % (pid, tid, ea, code))
def dbg_library_unload(self, pid, tid, ea, info):
print("Library unloaded: pid=%d tid=%d ea=0x%x info=%s" % (pid, tid, ea, info))
return 0
self.log("Library unloaded: pid=%d tid=%d ea=0x%x info=%s" % (pid, tid, ea, info))
def dbg_process_attach(self, pid, tid, ea, name, base, size):
print("Process attach pid=%d tid=%d ea=0x%x name=%s base=%x size=%x" % (pid, tid, ea, name, base, size))
self.log("Process attach pid=%d tid=%d ea=0x%x name=%s base=%x size=%x" % (pid, tid, ea, name, base, size))
def dbg_process_detach(self, pid, tid, ea):
print("Process detached, pid=%d tid=%d ea=0x%x" % (pid, tid, ea))
return 0
self.log("Process detached, pid=%d tid=%d ea=0x%x" % (pid, tid, ea))
def dbg_library_load(self, pid, tid, ea, name, base, size):
print("Library loaded: pid=%d tid=%d name=%s base=%x" % (pid, tid, name, base))
self.log("Library loaded: pid=%d tid=%d name=%s base=%x" % (pid, tid, name, base))
def dbg_bpt(self, tid, ea):
print("Break point at 0x%x pid=%d" % (ea, tid))
self.log("Break point at 0x%x pid=%d" % (ea, tid))
# return values:
# -1 - to display a breakpoint warning dialog
# if the process is suspended.
@@ -45,11 +51,11 @@ class MyDbgHook(DBG_Hooks):
return 0
def dbg_suspend_process(self):
print("Process suspended")
self.log("Process suspended")
def dbg_exception(self, pid, tid, ea, exc_code, exc_can_cont, exc_ea, exc_info):
print("Exception: pid=%d tid=%d ea=0x%x exc_code=0x%x can_continue=%d exc_ea=0x%x exc_info=%s" % (
pid, tid, ea, exc_code & idaapi.BADADDR, exc_can_cont, exc_ea, exc_info))
self.log("Exception: pid=%d tid=%d ea=0x%x exc_code=0x%x can_continue=%d exc_ea=0x%x exc_info=%s" % (
pid, tid, ea, exc_code & ida_idaapi.BADADDR, exc_can_cont, exc_ea, exc_info))
# return values:
# -1 - to display an exception warning dialog
# if the process is suspended.
@@ -58,30 +64,32 @@ class MyDbgHook(DBG_Hooks):
return 0
def dbg_trace(self, tid, ea):
print("Trace tid=%d ea=0x%x" % (tid, ea))
self.log("Trace tid=%d ea=0x%x" % (tid, ea))
# return values:
# 1 - do not log this trace event;
# 0 - log it
return 0
def dbg_step_into(self):
print("Step into")
self.log("Step into")
self.dbg_step_over()
def dbg_run_to(self, pid, tid=0, ea=0):
print("Runto: tid=%d" % tid)
idaapi.continue_process()
self.log("Runto: tid=%d, ea=%x" % (tid, ea))
ida_dbg.request_step_over()
def dbg_step_over(self):
eip = get_reg_value("EIP")
print("0x%x %s" % (eip, GetDisasm(eip)))
eip = ida_dbg.get_reg_val("EIP")
disasm = ida_lines.tag_remove(
ida_lines.generate_disasm_line(
eip))
self.log("Step over: EIP=0x%x, disassembly=%s" % (eip, disasm))
self.steps += 1
if self.steps >= 5:
request_exit_process()
ida_dbg.request_exit_process()
else:
request_step_over()
ida_dbg.request_step_over()
# Remove an existing debug hook
@@ -95,14 +103,9 @@ except:
# Install the debug hook
debughook = MyDbgHook()
debughook.hook()
debughook.steps = 0
# Stop at the entry point
ep = get_inf_attr(INF_START_IP)
request_run_to(ep)
# Step one instruction
request_step_over()
# Start debugging
run_requests()
ep = ida_ida.inf_get_start_ip()
if ida_dbg.request_run_to(ep): # Request stop at entry point
ida_dbg.run_requests() # Launch process
else:
print("Impossible to prepare debugger requests. Is a debugger selected?")
+116
View File
@@ -0,0 +1,116 @@
"""
summary: using the low-level tracing hook
description:
This script demonstrates using the low-level tracing hook
(ida_dbg.DBG_Hooks.dbg_trace). It can be run like so:
ida[t].exe -B -Sdbg_trace.py -Ltrace.log file.exe
"""
import time
import ida_dbg
import ida_ida
import ida_pro
import ida_ua
from ida_allins import NN_callni, NN_call, NN_callfi
from ida_lines import generate_disasm_line, GENDSM_FORCE_CODE, GENDSM_REMOVE_TAGS
# Note: this try/except block below is just there to
# let us (at Hex-Rays) test this script in various
# situations.
try:
import idc
print(idc.ARGV[1])
under_test = bool(idc.ARGV[1])
except:
under_test = False
class TraceHook(ida_dbg.DBG_Hooks):
def __init__(self):
ida_dbg.DBG_Hooks.__init__(self)
self.traces = 0
self.epReached = False
def _log(self, msg):
print(">>> %s" % msg)
def dbg_trace(self, tid, ea):
# Log all traced addresses
if ea < ida_ida.inf_get_min_ea() or ea > ida_ida.inf_get_max_ea():
raise Exception(
"Received a trace callback for an address outside this database!"
)
self._log("trace %08X" % ea)
self.traces += 1
insn = ida_ua.insn_t()
insnlen = ida_ua.decode_insn(insn, ea)
# log disassembly and ESP for call instructions
if insnlen > 0 and insn.itype in [NN_callni, NN_call, NN_callfi]:
self._log(
"call insn: %s"
% generate_disasm_line(ea, GENDSM_FORCE_CODE | GENDSM_REMOVE_TAGS)
)
self._log("ESP=%08X" % ida_dbg.get_reg_val("ESP"))
return 1
def dbg_run_to(self, pid, tid=0, ea=0):
# this hook is called once execution reaches temporary breakpoint set by run_to(ep) below
if not self.epReached:
ida_dbg.refresh_debugger_memory()
self._log("reached entry point at 0x%X" % ida_dbg.get_reg_val("EIP"))
self._log("current step trace options: %x" % ida_dbg.get_step_trace_options())
self.epReached = True
# enable step tracing (single-step the program and generate dbg_trace events)
ida_dbg.request_enable_step_trace(1)
# change options to only "over debugger segments" (i.e. library functions will be traced)
ida_dbg.request_set_step_trace_options(ida_dbg.ST_OVER_DEBUG_SEG)
ida_dbg.request_continue_process()
ida_dbg.run_requests()
def dbg_process_exit(self, pid, tid, ea, code):
self._log("process exited with %d" % code)
self._log("traced %d instructions" % self.traces)
return 0
def do_trace(then_quit_ida=True):
debugHook = TraceHook()
debugHook.hook()
# Start tracing when entry point is hit
ep = ida_ida.inf_get_start_ip()
ida_dbg.enable_step_trace(1)
ida_dbg.set_step_trace_options(ida_dbg.ST_OVER_DEBUG_SEG | ida_dbg.ST_OVER_LIB_FUNC)
print("Running to %x" % ep)
ida_dbg.run_to(ep)
while ida_dbg.get_process_state() != 0:
ida_dbg.wait_for_next_event(1, 0)
if not debugHook.epReached:
raise Exception("Entry point wasn't reached!")
if not debugHook.unhook():
raise Exception("Error uninstalling hooks!")
del debugHook
if then_quit_ida:
# we're done; exit IDA
ida_pro.qexit(0)
# load the debugger module depending on the file type
if ida_ida.inf_get_filetype() == ida_ida.f_PE:
ida_dbg.load_debugger("win32", 0)
elif ida_ida.inf_get_filetype() == ida_ida.f_ELF:
ida_dbg.load_debugger("linux", 0)
elif ida_ida.inf_get_filetype() == ida_ida.f_MACHO:
ida_dbg.load_debugger("mac", 0)
if not under_test:
do_trace()
@@ -0,0 +1,59 @@
"""
summary: print call stack (on Linux)
description: print the return addresses from the call stack at a breakpoint.
(and print also the module and the debug name from debugger)
To use this example:
* run `ida64` on test program `simple_appcall_linux64`, or
`ida` on test program `simple_appcall_linux32`, and wait for
auto-analysis to finish
* put a breakpoint where you want to see the call stack
* select the 'linux debugger' (either local, or remote)
* start debugging
* Press Shift+C at the breakpoint
"""
import os
import ida_idaapi
import ida_idd
import ida_dbg
import ida_kernwin
import ida_name
def log(msg):
print(">>> %s" % msg)
class print_call_stack_ah_t():
def activate(self, ctx):
log("=== start of call stack impression ===")
tid = ida_dbg.get_current_thread()
trace = ida_idd.call_stack_t()
if ida_dbg.collect_stack_trace(tid, trace):
for frame in trace:
mi = ida_idd.modinfo_t()
if ida_dbg.get_module_info(frame.callea, mi):
module = os.path.basename(mi.name)
name = ida_name.get_nice_colored_name(
frame.callea,
ida_name.GNCN_NOCOLOR|ida_name.GNCN_NOLABEL|ida_name.GNCN_NOSEG|ida_name.GNCN_PREFDBG)
log("Return address: " + hex(frame.callea) + " from: " + module + " with debug name: " + name)
else:
log("Return address: " + hex(frame.callea))
log("=== end of call stack impression ===")
def update(self, ctx):
return ida_kernwin.AST_ENABLE_ALWAYS
ACTION_NAME = "example:print_call_stack"
ACTION_LABEL = "Print call stack"
ACTION_SHORTCUT = "Shift+C"
ACTION_HELP = "Press %s to dump the call stack" % ACTION_SHORTCUT
if ida_kernwin.register_action(ida_kernwin.action_desc_t(
ACTION_NAME,
ACTION_LABEL,
print_call_stack_ah_t(),
ACTION_SHORTCUT)):
print("Registered action \"%s\". %s" % (ACTION_LABEL, ACTION_HELP))
@@ -0,0 +1,53 @@
"""
summary: print all registers, for all threads
description: iterate over the list of threads in the program being
debugged, and dump all registers contents
To use this example:
* run `ida64` on test program `simple_appcall_linux64`, or
`ida` on test program `simple_appcall_linux32`, and wait for
auto-analysis to finish
* put a breakpoint somewhere in the code
* select the 'linux debugger' (either local, or remote)
* start debugging
* Press Alt+Shift+C at the breakpoint
"""
import ida_idd
import ida_dbg
def log(msg):
print(">>> %s" % msg)
class print_registers_ah_t():
def activate(self, ctx):
log("=== registers ===")
dbg = ida_idd.get_dbg()
for tidx in range(ida_dbg.get_thread_qty()):
tid = ida_dbg.getn_thread(tidx)
log(" Thread #%d" % tid)
regvals = ida_dbg.get_reg_vals(tid)
for ridx, rv in enumerate(regvals):
rinfo = dbg.regs(ridx)
rval = rv.pyval(rinfo.dtype)
if isinstance(rval, int):
rval = "0x%x" % rval
log(" %s: %s" % (rinfo.name, rval))
log("=== end of registers ===")
def update(self, ctx):
return ida_kernwin.AST_ENABLE_ALWAYS
ACTION_NAME = "example:print_registers"
ACTION_LABEL = "Print registers"
ACTION_SHORTCUT = "Alt+Shift+C"
ACTION_HELP = "Press %s to print the registers" % ACTION_SHORTCUT
if ida_kernwin.register_action(ida_kernwin.action_desc_t(
ACTION_NAME,
ACTION_LABEL,
print_registers_ah_t(),
ACTION_SHORTCUT)):
print("Registered action \"%s\". %s" % (ACTION_LABEL, ACTION_HELP))
@@ -0,0 +1,58 @@
"""
summary: adding actions to the "registers" widget(s)
description:
It's possible to add actions to the context menu of
pretty much all widgets in IDA.
This example shows how to do just that for
registers-displaying widgets (e.g., "General registers")
"""
import ida_dbg
import ida_idd
import ida_kernwin
import ida_ua
ACTION_NAME = "registers_context_menu:dump_reg"
class dump_reg_ah_t(ida_kernwin.action_handler_t):
def activate(self, ctx):
name = ctx.regname
value = ida_dbg.get_reg_val(name)
rtype = "integer"
rinfo = ida_idd.register_info_t()
if ida_dbg.get_dbg_reg_info(name, rinfo):
if rinfo.dtype == ida_ua.dt_byte:
value = "0x%02x" % value
elif rinfo.dtype == ida_ua.dt_word:
value = "0x%04x" % value
elif rinfo.dtype == ida_ua.dt_dword:
value = "0x%08x" % value
elif rinfo.dtype == ida_ua.dt_qword:
value = "0x%016x" % value
else:
rtype = "float"
print("> Register %s (of type %s): %s" % (name, rtype, value))
def update(self, ctx):
return ida_kernwin.AST_ENABLE_FOR_WIDGET \
if ctx.widget_type == ida_kernwin.BWN_CPUREGS \
else ida_kernwin.AST_DISABLE_FOR_WIDGET
if ida_kernwin.register_action(
ida_kernwin.action_desc_t(
ACTION_NAME,
"Dump register info",
dump_reg_ah_t())):
class registers_hooks_t(ida_kernwin.UI_Hooks):
def finish_populating_widget_popup(self, form, popup):
if ida_kernwin.get_widget_type(form) == ida_kernwin.BWN_CPUREGS:
ida_kernwin.attach_action_to_popup(form, popup, ACTION_NAME)
hooks = registers_hooks_t()
hooks.hook()
else:
print("Failed to register action")
-35
View File
@@ -1,35 +0,0 @@
from __future__ import print_function
from tempo import *;
def test_getmeminfo():
L = tempo.getmeminfo()
out = []
# start_ea end_ea name sclass sbase bitness perm
for (start_ea, end_ea, name, sclass, sbase, bitness, perm) in L:
out.append("%x: %x name=<%s> sclass=<%s> sbase=%x bitness=%2x perm=%2x" % (start_ea, end_ea, name, sclass, sbase, bitness, perm))
f = file(r"d:\temp\out.log", "w")
f.write(("\n".join(out)).encode("UTF-8"))
f.close()
print("dumped meminfo!")
def test_getregs():
# name flags class dtype bit_strings bit_strings_default_mask
L = tempo.getregs()
out = []
for (name, flags, cls, dtype, bit_strings, bit_strings_default_mask) in L:
out.append("name=<%s> flags=%x class=%x dtype=%x bit_strings_mask=%x" % (name, flags, cls, dtype, bit_strings_default_mask))
if bit_strings:
for s in bit_strings:
out.append(" %s" % s)
f = file(r"d:\temp\out.log", "w")
f.write(("\n".join(out)).encode("UTF-8"))
f.close()
print("dumped regs!")
+17 -4
View File
@@ -1,15 +1,28 @@
"""
summary: retrieving & dumping debuggee symbols
description:
Queries the debugger (possibly remotely) for the list of
symbols that the process being debugged, provides.
"""
from __future__ import print_function
import idaapi
import ida_dbg
import ida_ida
import ida_name
def main():
if not idaapi.is_debugger_on():
if not ida_dbg.is_debugger_on():
print("Please run the process first!")
return
if idaapi.get_process_state() != -1:
if ida_dbg.get_process_state() != -1:
print("Please suspend the debugger first!")
return
dn = idaapi.get_debug_names(idaapi.cvar.inf.min_ea, idaapi.cvar.inf.max_ea)
dn = ida_name.get_debug_names(
ida_ida.inf_get_min_ea(),
ida_ida.inf_get_max_ea())
for i in dn:
print("%08x: %s" % (i, dn[i]))
@@ -0,0 +1,102 @@
"""
summary: interactively color certain pseudocode lines
description:
Provides an action that can be used to dynamically alter the
lines background rendering for pseudocode listings (as opposed to
using `ida_hexrays.cfunc_t.pseudocode[N].bgcolor`)
After running this script, pressing 'M' on a line in a
"Pseudocode-?" widget, will cause that line to be rendered
with a special background color.
keywords: colors
"""
import ida_kernwin
import ida_hexrays
import ida_moves
import ida_idaapi
class pseudo_line_t(object):
def __init__(self, func_ea, line_nr):
self.func_ea = func_ea
self.line_nr = line_nr
def __hash__(self):
return hash((self.func_ea, self.line_nr))
def __eq__(self, r):
return self.func_ea == r.func_ea \
and self.line_nr == r.line_nr
def _place_to_line_number(p):
return ida_kernwin.place_t.as_simpleline_place_t(p).n
class pseudocode_lines_rendering_hooks_t(ida_kernwin.UI_Hooks):
def __init__(self):
ida_kernwin.UI_Hooks.__init__(self)
self.marked_lines = {}
def get_lines_rendering_info(self, out, widget, rin):
vu = ida_hexrays.get_widget_vdui(widget)
if vu:
entry_ea = vu.cfunc.entry_ea
for section_lines in rin.sections_lines:
for line in section_lines:
coord = pseudo_line_t(
entry_ea,
_place_to_line_number(line.at))
color = self.marked_lines.get(coord, None)
if color is not None:
e = ida_kernwin.line_rendering_output_entry_t(line)
e.bg_color = color
out.entries.push_back(e)
class toggle_line_marked_ah_t(ida_kernwin.action_handler_t):
"""
We could very well use an ARGB value, but instead let's go
go with a color 'key': those can be altered by the user/theme,
and therefore have a better chance of being appropriate (or at
least expected.)
"""
COLOR_KEY = ida_kernwin.CK_EXTRA11
def __init__(self, hooks):
ida_kernwin.action_handler_t.__init__(self)
self.hooks = hooks
def activate(self, ctx):
vu = ida_hexrays.get_widget_vdui(ctx.widget)
if vu:
loc = ida_moves.lochist_entry_t()
if ida_kernwin.get_custom_viewer_location(loc, ctx.widget):
coord = pseudo_line_t(
vu.cfunc.entry_ea,
_place_to_line_number(loc.place()))
if coord in self.hooks.marked_lines.keys():
del self.hooks.marked_lines[coord]
else:
self.hooks.marked_lines[coord] = self.COLOR_KEY
ida_kernwin.refresh_custom_viewer(ctx.widget)
def update(self, ctx):
return ida_kernwin.AST_ENABLE_FOR_WIDGET \
if ctx.widget_type == ida_kernwin.BWN_PSEUDOCODE \
else ida_kernwin.AST_DISABLE_FOR_WIDGET
hooks = pseudocode_lines_rendering_hooks_t()
act_name = "example:colorize_pseudocode_line"
act_shortcut = "M"
if ida_kernwin.register_action(ida_kernwin.action_desc_t(
act_name,
"Mark pseudocode line",
toggle_line_marked_ah_t(hooks),
act_shortcut)):
hooks.hook()
print("Action registered. Please press '%s' in a pseudocode window to mark a line" % act_shortcut)
+90
View File
@@ -0,0 +1,90 @@
"""
summary: a focus on the 'curpos' hook, printing additional details about user input
description:
Shows how user input information can be retrieved during
processing of a notification triggered by that input
see_also: vds_hooks
"""
import ida_hexrays
import ida_kernwin
class curpos_details_t(ida_hexrays.Hexrays_Hooks):
def curpos(self, v):
parts = ["cpos={lnnum=%d, x=%d, y=%d}" % (v.cpos.lnnum, v.cpos.x, v.cpos.y)]
uie = ida_kernwin.input_event_t()
if ida_kernwin.get_user_input_event(uie):
kind_str = {
ida_kernwin.iek_shortcut : "shortcut",
ida_kernwin.iek_key_press : "key_press",
ida_kernwin.iek_key_release : "key_release",
ida_kernwin.iek_mouse_button_press : "mouse_button_press",
ida_kernwin.iek_mouse_button_release : "mouse_button_release",
ida_kernwin.iek_mouse_wheel : "mouse_wheel",
}[uie.kind]
#
# Retrieve input kind-specific information
#
if uie.kind == ida_kernwin.iek_shortcut:
payload_str = "shortcut={action_name=%s}" % uie.shortcut.action_name
elif uie.kind in [
ida_kernwin.iek_key_press,
ida_kernwin.iek_key_release]:
payload_str = "keyboard={key=%d, text=%s}" % (uie.keyboard.key, uie.keyboard.text)
else:
payload_str = "mouse={x=%d, y=%d, button=%d}" % (
uie.mouse.x,
uie.mouse.y,
uie.mouse.button)
#
# And while at it, retrieve a few extra bits from the
# source QEvent as well, why not
#
qevent = uie.get_source_QEvent()
qevent_str = str(qevent)
from PyQt5 import QtCore
if qevent.type() in [
QtCore.QEvent.KeyPress,
QtCore.QEvent.KeyRelease]:
qevent_str="{count=%d}" % qevent.count()
elif qevent.type() in [
QtCore.QEvent.MouseButtonPress,
QtCore.QEvent.MouseButtonRelease]:
qevent_str="{globalX=%d, globalY=%d, flags=%s}" % (
qevent.globalX(),
qevent.globalY(),
qevent.flags())
elif qevent.type() == QtCore.QEvent.Wheel:
qevent_str="{angleDelta={x=%s, y=%s}, phase=%s}" % (
qevent.angleDelta().x(),
qevent.angleDelta().y(),
qevent.phase())
#
# If the target QWidget is a scroll area's viewport,
# pick up the parent
#
from PyQt5 import QtWidgets
qwidget = uie.get_target_QWidget()
if qwidget:
parent = qwidget.parentWidget()
if parent and isinstance(parent, QtWidgets.QAbstractScrollArea):
qwidget = parent
parts.append("user_input_event={kind=%s, modifiers=0x%x, target={metaObject={className=%s}, windowTitle=%s}, source=%s, %s, source-as-qevent=%s}" % (
kind_str,
uie.modifiers,
qwidget.metaObject().className(),
qwidget.windowTitle(),
uie.source,
payload_str,
qevent_str))
print("### curpos: %s" % ", ".join(parts))
return 0
curpos_details = curpos_details_t()
curpos_details.hook()
+65 -27
View File
@@ -1,11 +1,16 @@
"""
summary: automatic decompilation of functions
description:
Attempts to load a decompiler plugin corresponding to the current
architecture (and address size) right after auto-analysis is performed,
and then tries to decompile the function at the first entrypoint.
It is particularly suited for use with the '-S' flag, for example:
idat -Ldecompile.log -Sdecompile_entry_points.py -c file
"""
from __future__ import print_function
#
# This example tries to load a decompiler plugin corresponding to the current
# architecture (and address size) right after auto-analysis is performed,
# and then tries to decompile the function at the first entrypoint.
#
# It is particularly suited for use with the '-S' flag.
#
import ida_ida
import ida_auto
@@ -13,29 +18,62 @@ import ida_loader
import ida_hexrays
import ida_idp
import ida_entry
import ida_kernwin
ida_auto.auto_wait()
ALL_DECOMPILERS = {
ida_idp.PLFM_386 : ("hexrays", "hexx64"),
ida_idp.PLFM_ARM : ("hexarm", "hexarm64"),
ida_idp.PLFM_PPC : ("hexppc", "hexppc64"),
}
pair = ALL_DECOMPILERS.get(ida_idp.ph.id, None)
if pair:
decompiler = pair[1 if ida_ida.cvar.inf.is_64bit() else 0]
# because the -S script runs very early, we need to load the decompiler
# manually if we want to use it
def init_hexrays():
ALL_DECOMPILERS = {
ida_idp.PLFM_386: "hexrays",
ida_idp.PLFM_ARM: "hexarm",
ida_idp.PLFM_PPC: "hexppc",
ida_idp.PLFM_MIPS: "hexmips",
}
cpu = ida_idp.ph.id
decompiler = ALL_DECOMPILERS.get(cpu, None)
if not decompiler:
print("No known decompilers for architecture with ID: %d" % ida_idp.ph.id)
return False
if ida_ida.inf_is_64bit():
if cpu == ida_idp.PLFM_386:
decompiler = "hexx64"
else:
decompiler += "64"
if ida_loader.load_plugin(decompiler) and ida_hexrays.init_hexrays_plugin():
return True
else:
print('Couldn\'t load or initialize decompiler: "%s"' % decompiler)
return False
def decompile_func(ea, outfile):
ida_kernwin.msg("Decompiling at: %X..." % ea)
cf = ida_hexrays.decompile(ea)
if cf:
ida_kernwin.msg("OK\n")
outfile.write(str(cf) + "\n")
else:
ida_kernwin.msg("failed!\n")
outfile.write("decompilation failure at %X!\n" % ea)
def main():
print("Waiting for autoanalysis...")
ida_auto.auto_wait()
if init_hexrays():
eqty = ida_entry.get_entry_qty()
if eqty:
ea = ida_entry.get_entry(ida_entry.get_entry_ordinal(0))
print("Decompiling at: %X" % ea)
cf = ida_hexrays.decompile(ea)
if cf:
print(cf)
else:
print("Decompilation failed")
idbpath = idc.get_idb_path()
cpath = idbpath[:-4] + ".c"
with open(cpath, "w") as outfile:
print("writing results to '%s'..." % cpath)
for i in range(eqty):
ea = ida_entry.get_entry(ida_entry.get_entry_ordinal(i))
decompile_func(ea, outfile)
else:
print("No known entrypoint. Cannot decompile.")
else:
print("Couldn't load or initialize decompiler: \"%s\"" % decompiler)
else:
print("No known decompilers for architecture with ID: %d" % ida_idp.ph.id)
if ida_kernwin.cvar.batch:
print("All done, exiting.")
ida_pro.qexit(0)
main()
+14 -6
View File
@@ -1,25 +1,33 @@
"""
summary: decompile & print current function.
"""
from __future__ import print_function
import idaapi
import ida_hexrays
import ida_lines
import ida_funcs
import ida_kernwin
def main():
if not idaapi.init_hexrays_plugin():
if not ida_hexrays.init_hexrays_plugin():
return False
print("Hex-rays version %s has been detected" % idaapi.get_hexrays_version())
print("Hex-rays version %s has been detected" % ida_hexrays.get_hexrays_version())
f = idaapi.get_func(idaapi.get_screen_ea());
f = ida_funcs.get_func(ida_kernwin.get_screen_ea());
if f is None:
print("Please position the cursor within a function")
return True
cfunc = idaapi.decompile(f);
cfunc = ida_hexrays.decompile(f);
if cfunc is None:
print("Failed to decompile!")
return True
sv = cfunc.get_pseudocode();
for sline in sv:
print(idaapi.tag_remove(sline.line));
print(ida_lines.tag_remove(sline.line));
return True
+39 -21
View File
@@ -1,27 +1,28 @@
#
# Hex-Rays Decompiler project
# Copyright (c) 2007-2019 by Hex-Rays, support@hex-rays.com
# ALL RIGHTS RESERVED.
#
# Sample plugin for Hex-Rays Decompiler.
# It installs a custom microcode optimization rule:
# call !DbgRaiseAssertionFailure <fast:>.0
# =>
# call !DbgRaiseAssertionFailure <fast:"char *" "assertion text">.0
#
# To see this plugin in action please use arm64_brk.i64, in the hexrays sdk
#
# This is a rewrite in Python of the vds10 example that comes with hexrays sdk.
#
"""
summary: a custom microcode instruction optimization rule
description:
Installs a custom microcode instruction optimization rule,
to transform:
call !DbgRaiseAssertionFailure <fast:>.0
into
call !DbgRaiseAssertionFailure <fast:"char *" "assertion text">.0
To see this plugin in action please use arm64_brk.i64
"""
import ida_bytes
import ida_range
import ida_kernwin
import ida_hexrays
import ida_typeinf
import ida_idaapi
class nt_assert_optimizer_t(ida_hexrays.optinsn_t):
def func(self, blk, ins):
def func(self, blk, ins, optflags):
if self.handle_nt_assert(ins):
return 1
return 0
@@ -55,10 +56,27 @@ class nt_assert_optimizer_t(ida_hexrays.optinsn_t):
fa.size = fa.type.get_size()
return True
# --------------------------------------------------------------------------
# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
flags = ida_idaapi.PLUGIN_HIDE
wanted_name = "Optimize DbgRaiseAssertionFailure (IDAPython)"
wanted_hotkey = ""
comment = "Sample plugin10 for Hex-Rays decompiler"
help = ""
def init(self):
if ida_hexrays.init_hexrays_plugin():
self.optimizer = nt_assert_optimizer_t()
self.optimizer.install()
return ida_idaapi.PLUGIN_KEEP # keep us in the memory
def term(self):
self.optimizer.remove()
def run(self, arg):
if arg == 1:
return self.optimizer.remove()
elif arg == 2:
return self.optimizer.install()
if ida_hexrays.init_hexrays_plugin():
optimizer = nt_assert_optimizer_t()
optimizer.install()
else:
print('vds10: Hex-rays is not available.')
def PLUGIN_ENTRY():
return my_plugin_t()
+43 -23
View File
@@ -1,27 +1,29 @@
#
# Hex-Rays Decompiler project
# Copyright (c) 2007-2019 by Hex-Rays, support@hex-rays.com
# ALL RIGHTS RESERVED.
#
# Sample plugin for Hex-Rays Decompiler.
# It installs a custom block optimization rule:
#
# goto L1 => goto L2
# ...
# L1:
# goto L2
#
# In other words we fix a goto target if it points to a chain of gotos.
# This improves the decompiler output in some cases.
#
# This is a rewrite in Python of the vds11 example that comes with hexrays sdk.
#
"""
summary: a custom microcode block optimization rule (resolve `goto` chains)
description:
Installs a custom microcode block optimization rule,
to transform:
goto L1
...
L1:
goto L2
into
goto L2
In other words we fix a goto target if it points to a chain of gotos.
This improves the decompiler output in some cases.
"""
import ida_bytes
import ida_range
import ida_kernwin
import ida_hexrays
import ida_typeinf
import ida_idaapi
class goto_optimizer_t(ida_hexrays.optblock_t):
def func(self, blk):
@@ -71,9 +73,27 @@ class goto_optimizer_t(ida_hexrays.optblock_t):
mba.verify(True);
return True
# --------------------------------------------------------------------------
# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
flags = ida_idaapi.PLUGIN_HIDE
wanted_name = "Optimize goto chains (IDAPython)"
wanted_hotkey = ""
comment = "Sample plugin11 for Hex-Rays decompiler"
help = ""
def init(self):
if ida_hexrays.init_hexrays_plugin():
self.optimizer = goto_optimizer_t()
self.optimizer.install()
return ida_idaapi.PLUGIN_KEEP # keep us in the memory
def term(self):
self.optimizer.remove()
def run(self, arg):
if arg == 1:
return self.optimizer.remove()
elif arg == 2:
return self.optimizer.install()
def PLUGIN_ENTRY():
return my_plugin_t()
if ida_hexrays.init_hexrays_plugin():
optimizer = goto_optimizer_t()
optimizer.install()
else:
print('vds11: Hex-rays is not available.')
+8 -12
View File
@@ -1,14 +1,10 @@
#
# Hex-Rays Decompiler project
# Copyright (c) 2007-2019 by Hex-Rays, support@hex-rays.com
# ALL RIGHTS RESERVED.
#
# Sample plugin for Hex-Rays Decompiler.
# It shows list of direct references to a register from the current
# instruction.
#
# This is a rewrite in Python of the vds12 example that comes with hexrays sdk.
#
"""
summary: list instruction registers
description:
Shows a list of direct references to a register from the
current instruction.
"""
import ida_pro
import ida_hexrays
@@ -107,7 +103,7 @@ if ida_hexrays.init_hexrays_plugin():
mbr,
hf,
None,
ida_hexrays.DECOMP_WARNINGS,
ida_hexrays.DECOMP_WARNINGS | ida_hexrays.DECOMP_NO_CACHE,
ida_hexrays.MMAT_PREOPTIMIZED)
if mba:
merr = mba.build_graph()
+6 -10
View File
@@ -1,13 +1,9 @@
#
# Hex-Rays Decompiler project
# Copyright (c) 2007-2019 by Hex-Rays, support@hex-rays.com
# ALL RIGHTS RESERVED.
#
# Sample plugin for Hex-Rays Decompiler.
# It generates microcode for selection and dumps it to the output window.
#
# This is a rewrite in Python of the vds13 example that comes with hexrays sdk.
#
"""
summary: generates microcode for selection
description:
Generates microcode for selection and dumps it to the output window.
"""
import ida_bytes
import ida_range
+43 -24
View File
@@ -1,20 +1,21 @@
#
# Hex-Rays Decompiler project
# Copyright (c) 2007-2019 by Hex-Rays, support@hex-rays.com
# ALL RIGHTS RESERVED.
#
# Sample plugin for Hex-Rays Decompiler.
# It shows how to use "Select offsets" widget (select_udt_by_offset() call).
# This plugin repeats the Alt-Y functionality.
# Usage: place cursor on the union field and press Shift-T
#
# This is a rewrite in Python of the vds17 example that comes with hexrays sdk.
#
"""
summary: using the "Select offsets" widget
description:
Registers an action opens the "Select offsets" widget
(select_udt_by_offset() call).
This effectively repeats the functionality already available
through Alt+Y.
Place cursor on the union field and press Shift+T
"""
import ida_idaapi
import ida_hexrays
import ida_lines
import ida_typeinf
import ida_kernwin
# --------------------------------------------------------------------------
class func_stroff_ah_t(ida_kernwin.action_handler_t):
@@ -26,14 +27,16 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t):
vu = ida_hexrays.get_widget_vdui(ctx.widget)
vu.get_current_item(ida_hexrays.USE_KEYBOARD)
# REGION1, will be referenced latter
# REGION1, will be referenced later
# check that the current item is a union field
if not vu.item.is_citem():
ida_kernwin.warning("Please position the cursor on a union member")
return 0
e = vu.item.e
while True:
op = e.op
if op != ida_hexrays.cot_memptr and op != ida_hexrays.cot_memref:
ida_kernwin.warning("Please position the cursor on a union member")
return 0
e = e.x
if op == ida_hexrays.cot_memptr:
@@ -43,6 +46,7 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t):
if ida_typeinf.remove_pointer(e.type).is_union():
break
if not e.type.is_udt():
ida_kernwin.warning("Please position the cursor on a union member")
return 0
# END REGION1
@@ -98,6 +102,7 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t):
# the item itself may be unaddressable.
# TODO: find its addressable parent
if ea == ida_idaapi.BADADDR:
ida_kernwin.warning("Sorry, the current item is not addressable")
return 0
# END REGION4
@@ -126,7 +131,7 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t):
def apply(self, opnum, path, top_tif, spath):
typename = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, top_tif, '', '')
idaapi.msg("User selected %s of type %s\n" % (spath, typename))
ida_kernwin.msg("User selected %s of type %s\n" % (spath, typename))
if path.empty():
return False
vu.cfunc.set_user_union_selection(self.ea, path)
@@ -151,16 +156,30 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t):
# --------------------------------------------------------------------------
if ida_hexrays.init_hexrays_plugin():
print("Hex-rays version %s has been detected, Structure offsets ready to use" % ida_hexrays.get_hexrays_version())
ida_kernwin.register_action(
ida_kernwin.action_desc_t(
"vds17:strchoose",
"Structure offsets",
func_stroff_ah_t(),
"Shift+T"))
else:
print('vds17: Hex-rays is not available.')
# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
flags = ida_idaapi.PLUGIN_HIDE
wanted_name = "Structure offsets (IDAPython)"
wanted_hotkey = ""
comment = "Sample plugin17 for Hex-Rays decompiler"
help = ""
def init(self):
if ida_hexrays.init_hexrays_plugin():
print("Hex-rays version %s has been detected, Structure offsets ready to use" % ida_hexrays.get_hexrays_version())
ida_kernwin.register_action(
ida_kernwin.action_desc_t(
"vds17:strchoose",
"Structure offsets",
func_stroff_ah_t(),
"Shift+T"))
return ida_idaapi.PLUGIN_KEEP # keep us in the memory
def term(self):
pass
def run(self, arg):
pass
def PLUGIN_ENTRY():
return my_plugin_t()
"""
# A few notes about the VDS17 sample
+68
View File
@@ -0,0 +1,68 @@
"""
summary: a custom microcode instruction optimization rule (`x | ~x => -1`)
description:
Installs a custom microcode instruction optimization rule,
to transform:
x | ~x
into
-1
To see this plugin in action please use be_ornot_be.idb
"""
import ida_hexrays
import ida_idaapi
# recognize "x | ~x" and replace by -1
class subinsn_optimizer_t(ida_hexrays.minsn_visitor_t):
cnt = 0
def visit_minsn(self): # for each instruction...
ins = self.curins # take a reference to the current instruction
# THE CORE OF THE PLUGIN IS HERE:
# check the pattern "x | ~x"
if ins.opcode == ida_hexrays.m_or and ins.r.is_insn(ida_hexrays.m_bnot) and ins.l == ins.r.d.l:
if not ins.l.has_side_effects(): # avoid destroying side effects
# pattern matched, convert to "mov -1, ..."
ins.opcode = ida_hexrays.m_mov
ins.l.make_number(-1, ins.r.size)
ins.r = ida_hexrays.mop_t()
self.cnt = self.cnt + 1 # number of changes we made
return 0 # continue traversal
# a custom instruction optimizer, boilerplate code
class sample_optimizer_t(ida_hexrays.optinsn_t):
def func(self, blk, ins, optflags):
opt = subinsn_optimizer_t()
ins.for_all_insns(opt)
if opt.cnt != 0: # if we modified microcode,
blk.mba.verify(True) # run the verifier
return opt.cnt # report the number of changes
# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
flags = ida_idaapi.PLUGIN_HIDE
wanted_name = "optimize x|~x"
wanted_hotkey = ""
comment = ""
help = ""
def init(self):
if ida_hexrays.init_hexrays_plugin():
self.optimizer = sample_optimizer_t()
self.optimizer.install()
print("Installed sample optimizer for 'x | ~x'")
return ida_idaapi.PLUGIN_KEEP # keep us in the memory
def term(self):
self.optimizer.remove()
def run(self, arg):
if arg == 1:
return self.optimizer.remove()
elif arg == 2:
return self.optimizer.install()
def PLUGIN_ENTRY():
return my_plugin_t()
+97
View File
@@ -0,0 +1,97 @@
"""
summary: dynamically provide a custom call type
description:
This plugin can greatly improve decompilation of indirect calls:
call [eax+4]
For them, the decompiler has to guess the prototype of the called function.
This has to be done at a very early phase of decompilation because
the function prototype influences the data flow analysis. On the other
hand, we do not have global data flow analysis results yet because
we haven't analyzed all calls in the function. It is a chicked-and-egg
problem.
The decompiler uses various techniques to guess the called function
prototype. While it works very well, it may fail in some cases.
To fix, the user can specify the call prototype manually, using
"Edit, Operand types, Set operand type" at the call instruction.
This plugin illustrates another approach to the problem:
if you happen to be able to calculate the call prototypes dynamically,
this is how to inform the decompiler about them.
"""
import ida_idaapi
import ida_nalt
import ida_kernwin
import ida_typeinf
import ida_hexrays
testing = False # only for testing purposes
class callinfo_provider_t(ida_hexrays.Hexrays_Hooks):
# this callback will be called for all call instructions
# our plugin may provide the function prototype or even a complete new callinfo
# object. The callinfo object may be useful if the prototype is not enough
# to express all details of the call.
def build_callinfo(self, blk, type):
# it is a good idea to skip direct calls.
# note that some indirect calls may be resolved and become direct calls,
# and will be filtered out here:
ida_kernwin.msg("%x: got called for: %s\n" % (blk.tail.ea, blk.tail.dstr()))
tail = blk.tail
if tail.opcode == ida_hexrays.m_call:
return
# also, if the type was specified by the user, do not interfere
call_ea = tail.ea
tif = ida_typeinf.tinfo_t()
if ida_nalt.get_op_tinfo(tif, call_ea, 0):
return
global testing
if not testing:
# ok, the decompiler really has to guess the type.
# just for the sake of an example, return a predefined prototype.
# in real life you will provide the prototype you discovered yourself,
# using your magic of yours :)
my_proto = "int f();"
ida_kernwin.msg("%x: providing prototype %s\n" % (call_ea, my_proto))
ida_typeinf.parse_decl(type, None, my_proto, 0)
else:
# as an alternative to filling 'type', you can
# choose to return a mcallinfo_t instance.
mi = ida_hexrays.mcallinfo_t()
mi.cc = ida_typeinf.CM_CC_STDCALL | ida_typeinf.CM_N32_F48 # let's use stdcall to differentiate from the tinfo_t-filling version
mi.return_type = ida_typeinf.tinfo_t(ida_typeinf.BT_INT)
mi.return_argloc.set_reg1(0) # eax
return mi
# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
flags = ida_idaapi.PLUGIN_HIDE
wanted_name = "Hex-Rays custom prototype provider (IDAPython)"
wanted_hotkey = ""
comment = "Sample plugin21 for Hex-Rays decompiler"
help = ""
def init(self):
if ida_hexrays.init_hexrays_plugin():
self.hooks = callinfo_provider_t()
self.hooks.hook()
ida_kernwin.warning(
"Installed callinfo provider sample (vds21.py)\n" +\
"Please note that it is just an example\n" +\
"and will spoil your decompilations!")
return ida_idaapi.PLUGIN_KEEP # keep us in the memory
def term(self):
self.hooks.unhook()
def run(self, arg):
pass
def PLUGIN_ENTRY():
return my_plugin_t()
+106 -48
View File
@@ -1,14 +1,47 @@
""" Invert the then and else blocks of a cif_t.
Author: EiNSTeiN_ <einstein@g3nius.org>
This is a rewrite in Python of the vds3 example that comes with hexrays sdk.
"""
summary: invert if/else blocks
description:
Registers an action that can be used to invert the `if`
and `else` blocks of a `ida_hexrays.cif_t`.
For example, a statement like
if ( cond )
{
statements1;
}
else
{
statements2;
}
will be displayed as
if ( !cond )
{
statements2;
}
else
{
statements1;
}
The modifications are persistent: the user can quit & restart
IDA, and the changes will be present.
author: EiNSTeiN_ (einstein@g3nius.org)
"""
from __future__ import print_function
import idautils
import idaapi
import idc
import ida_kernwin
import ida_hexrays
import ida_netnode
import ida_idaapi
import ida_idp
import traceback
@@ -16,20 +49,20 @@ NETNODE_NAME = '$ hexrays-inverted-if'
inverter_actname = "vds3:invert"
class invert_action_handler_t(idaapi.action_handler_t):
class invert_action_handler_t(ida_kernwin.action_handler_t):
def __init__(self, inverter):
idaapi.action_handler_t.__init__(self)
ida_kernwin.action_handler_t.__init__(self)
self.inverter = inverter
def activate(self, ctx):
vdui = idaapi.get_widget_vdui(ctx.widget)
vdui = ida_hexrays.get_widget_vdui(ctx.widget)
self.inverter.invert_if_event(vdui)
return 1
def update(self, ctx):
return idaapi.AST_ENABLE_FOR_WIDGET if \
ctx.widget_type == idaapi.BWN_PSEUDOCODE else \
idaapi.AST_DISABLE_FOR_WIDGET
return ida_kernwin.AST_ENABLE_FOR_WIDGET if \
ctx.widget_type == ida_kernwin.BWN_PSEUDOCODE else \
ida_kernwin.AST_DISABLE_FOR_WIDGET
class hexrays_callback_info(object):
@@ -37,7 +70,7 @@ class hexrays_callback_info(object):
def __init__(self):
self.vu = None
self.node = idaapi.netnode()
self.node = ida_netnode.netnode()
if not self.node.create(NETNODE_NAME):
# node exists
self.load()
@@ -53,7 +86,7 @@ class hexrays_callback_info(object):
try:
data = self.node.getblob(0, 'I')
if data:
self.stored = eval(data)
self.stored = eval(data.decode("UTF-8"))
print('Invert-if: Loaded %s' % (repr(self.stored), ))
except:
print('Failed to load invert-if locations')
@@ -65,7 +98,7 @@ class hexrays_callback_info(object):
def save(self):
try:
self.node.setblob(repr(self.stored), 0, 'I')
self.node.setblob(repr(self.stored).encode("UTF-8"), 0, 'I')
except:
print('Failed to save invert-if locations')
traceback.print_exc()
@@ -73,7 +106,7 @@ class hexrays_callback_info(object):
return
def invert_if(self, cfunc, insn):
def invert_if(self, insn):
if insn.opname != 'if':
return False
@@ -83,12 +116,12 @@ class hexrays_callback_info(object):
if not cif.ithen or not cif.ielse:
return False
idaapi.qswap(cif.ithen, cif.ielse)
ida_hexrays.qswap(cif.ithen, cif.ielse)
# Make a copy of 'cif.expr': 'lnot' might destroy its toplevel
# cexpr_t and return a pointer to its direct child (but we'll want to
# 'swap' it later, the 'cif.expr' cexpr_t object must remain valid.)
cond = idaapi.cexpr_t(cif.expr)
notcond = idaapi.lnot(cond)
cond = ida_hexrays.cexpr_t(cif.expr)
notcond = ida_hexrays.lnot(cond)
cif.expr.swap(notcond)
@@ -104,26 +137,26 @@ class hexrays_callback_info(object):
def find_if_statement(self, vu):
vu.get_current_item(idaapi.USE_KEYBOARD)
vu.get_current_item(ida_hexrays.USE_KEYBOARD)
item = vu.item
if item.is_citem() and item.it.op == idaapi.cit_if and item.it.to_specific_type.cif.ielse is not None:
if item.is_citem() and item.it.op == ida_hexrays.cit_if and item.it.to_specific_type.cif.ielse is not None:
return item.it.to_specific_type
if vu.tail.citype == idaapi.VDI_TAIL and vu.tail.loc.itp == idaapi.ITP_ELSE:
if vu.tail.citype == ida_hexrays.VDI_TAIL and vu.tail.loc.itp == ida_hexrays.ITP_ELSE:
# for tail marks, we know only the corresponding ea,
# not the pointer to if-statement
# find it by walking the whole ctree
class if_finder_t(idaapi.ctree_visitor_t):
class if_finder_t(ida_hexrays.ctree_visitor_t):
def __init__(self, ea):
idaapi.ctree_visitor_t.__init__(self, idaapi.CV_FAST | idaapi.CV_INSNS)
ida_hexrays.ctree_visitor_t.__init__(self, ida_hexrays.CV_FAST | ida_hexrays.CV_INSNS)
self.ea = ea
self.found = None
return
def visit_insn(self, i):
if i.op == idaapi.cit_if and i.ea == self.ea:
if i.op == ida_hexrays.cit_if and i.ea == self.ea:
self.found = i
return 1 # stop enumeration
return 0
@@ -136,12 +169,11 @@ class hexrays_callback_info(object):
def invert_if_event(self, vu):
cfunc = vu.cfunc.__deref__()
i = self.find_if_statement(vu)
if not i:
return False
if self.invert_if(cfunc, i):
if self.invert_if(i):
vu.refresh_ctext()
self.add_location(i.ea)
@@ -149,18 +181,18 @@ class hexrays_callback_info(object):
def restore(self, cfunc):
class visitor(idaapi.ctree_visitor_t):
class visitor(ida_hexrays.ctree_visitor_t):
def __init__(self, inverter, cfunc):
idaapi.ctree_visitor_t.__init__(self, idaapi.CV_FAST | idaapi.CV_INSNS)
ida_hexrays.ctree_visitor_t.__init__(self, ida_hexrays.CV_FAST | ida_hexrays.CV_INSNS)
self.inverter = inverter
self.cfunc = cfunc
return
def visit_insn(self, i):
try:
if i.op == idaapi.cit_if and i.ea in self.inverter.stored:
self.inverter.invert_if(self.cfunc, i)
if i.op == ida_hexrays.cit_if and i.ea in self.inverter.stored:
self.inverter.invert_if(i)
except:
traceback.print_exc()
return 0 # continue enumeration
@@ -170,31 +202,57 @@ class hexrays_callback_info(object):
return
class vds3_hooks_t(idaapi.Hexrays_Hooks):
class vds3_hooks_t(ida_hexrays.Hexrays_Hooks):
def __init__(self, i):
idaapi.Hexrays_Hooks.__init__(self)
ida_hexrays.Hexrays_Hooks.__init__(self)
self.i = i
def populating_popup(self, widget, phandle, vu):
idaapi.attach_action_to_popup(vu.ct, None, inverter_actname)
ida_kernwin.attach_action_to_popup(vu.ct, None, inverter_actname)
return 0
def maturity(self, cfunc, maturity):
if maturity == idaapi.CMAT_FINAL:
if maturity == ida_hexrays.CMAT_FINAL:
self.i.restore(cfunc)
return 0
class idp_hooks_t(ida_idp.IDP_Hooks):
def __init__(self, i):
ida_idp.IDP_Hooks.__init__(self)
self.i = i
if idaapi.init_hexrays_plugin():
i = hexrays_callback_info()
idaapi.register_action(
idaapi.action_desc_t(
inverter_actname,
"Invert then/else",
invert_action_handler_t(i),
"I"))
vds3_hooks = vds3_hooks_t(i)
vds3_hooks.hook()
else:
print('invert-if: hexrays is not available.')
# 'node' refers to index of the named node, this index became invalid after
# privrange moving, so we recreate the node here to update nodeidx
def ev_privrange_changed(self, old_privrange, delta):
i.node.create(NETNODE_NAME)
# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
flags = ida_idaapi.PLUGIN_HIDE
wanted_name = "Hex-Rays if-inverter (IDAPython)"
wanted_hotkey = ""
comment = "Sample plugin3 for Hex-Rays decompiler"
help = ""
def init(self):
if ida_hexrays.init_hexrays_plugin():
i = hexrays_callback_info()
ida_kernwin.register_action(
ida_kernwin.action_desc_t(
inverter_actname,
"Invert then/else",
invert_action_handler_t(i),
"I"))
self.vds3_hooks = vds3_hooks_t(i)
self.vds3_hooks.hook()
# we need this hook to react to privrange moving event
self.idp_hooks = idp_hooks_t(i)
self.idp_hooks.hook()
return ida_idaapi.PLUGIN_KEEP # keep us in the memory
def term(self):
self.vds3_hooks.unhook()
def run(self, arg):
pass
def PLUGIN_ENTRY():
return my_plugin_t()
+43 -34
View File
@@ -1,58 +1,67 @@
""" Print user-defined details to the output window.
Author: EiNSTeiN_ <einstein@g3nius.org>
This is a rewrite in Python of the vds4 example that comes with hexrays sdk.
"""
summary: dump user-defined information
description:
Prints user-defined information to the "Output" window.
Namely:
* user defined label names
* user defined indented comments
* user defined number formats
* user defined local variable names, types, comments
This script loads information from the database without decompiling anything.
author: EiNSTeiN_ (einstein@g3nius.org)
"""
from __future__ import print_function
import idautils
import idaapi
import idc
import traceback
import ida_kernwin
import ida_hexrays
import ida_bytes
def run():
cfunc = idaapi.decompile(idaapi.get_screen_ea())
if not cfunc:
print('Please move the cursor into a function.')
return
entry_ea = cfunc.entry_ea
print("Dump of user-defined information for function at %x" % (entry_ea, ))
f = ida_funcs.get_func(ida_kernwin.get_screen_ea());
if f is None:
print("Please position the cursor within a function")
return True
entry_ea = f.start_ea
print("Dump of user-defined information for function at %x" % entry_ea)
# Display user defined labels.
labels = idaapi.restore_user_labels(entry_ea);
labels = ida_hexrays.restore_user_labels(entry_ea);
if labels is not None:
print("------- %u user defined labels" % (len(labels), ))
print("------- %u user defined labels" % len(labels))
for org_label, name in labels.items():
print("Label %d: %s" % (org_label, str(name)))
idaapi.user_labels_free(labels)
ida_hexrays.user_labels_free(labels)
# Display user defined comments
cmts = idaapi.restore_user_cmts(entry_ea);
cmts = ida_hexrays.restore_user_cmts(entry_ea);
if cmts is not None:
print("------- %u user defined comments" % (len(cmts), ))
for tl, cmt in cmts.items():
print("Comment at %x, preciser %x:\n%s\n" % (tl.ea, tl.itp, str(cmt)))
idaapi.user_cmts_free(cmts)
ida_hexrays.user_cmts_free(cmts)
# Display user defined citem iflags
iflags = idaapi.restore_user_iflags(entry_ea)
iflags = ida_hexrays.restore_user_iflags(entry_ea)
if iflags is not None:
print("------- %u user defined citem iflags" % (len(iflags), ))
for cl, f in iflags.items():
print("%x(%d): %08X%s" % (cl.ea, cl.op, f, " CIT_COLLAPSED" if f & idaapi.CIT_COLLAPSED else ""))
idaapi.user_iflags_free(iflags)
print("%x(%d): %08X%s" % (cl.ea, cl.op, f, " CIT_COLLAPSED" if f & ida_hexrays.CIT_COLLAPSED else ""))
ida_hexrays.user_iflags_free(iflags)
# Display user defined number formats
numforms = idaapi.restore_user_numforms(entry_ea)
numforms = ida_hexrays.restore_user_numforms(entry_ea)
if numforms is not None:
print("------- %u user defined number formats" % (len(numforms), ))
for ol, nf in numforms.items():
print("Number format at %a, operand %d: %s" % (ol.ea, ol.opnum, "negated " if (nf.props & NF_NEGATE) != 0 else ""))
print("Number format at %a, operand %d: %s" % \
(ol.ea,
ol.opnum,
"negated " if (ord(nf.props) & ida_hexrays.NF_NEGATE) != 0 else ""))
if nf.is_enum():
print("enum %s (serial %d)" % (str(nf.type_name), nf.serial))
@@ -64,13 +73,13 @@ def run():
print("struct offset %s" % (str(nf.type_name), ))
else:
print("number base=%d" % (idaapi.get_radix(nf.flags, ol.opnum), ))
print("number base=%d" % (ida_bytes.get_radix(nf.flags, ol.opnum), ))
idaapi.user_numforms_free(numforms)
ida_hexrays.user_numforms_free(numforms)
# Display user-defined local variable information
lvinf = idaapi.lvar_uservec_t()
if idaapi.restore_user_lvar_settings(lvinf, entry_ea):
lvinf = ida_hexrays.lvar_uservec_t()
if ida_hexrays.restore_user_lvar_settings(lvinf, entry_ea):
print("------- User defined local variable information\n")
for lv in lvinf.lvvec:
print("Lvar defined at %x" % (lv.ll.defea, ))
@@ -89,7 +98,7 @@ def run():
return
if idaapi.init_hexrays_plugin():
if ida_hexrays.init_hexrays_plugin():
run()
else:
print('dump user info: hexrays is not available.')
+40 -13
View File
@@ -1,11 +1,25 @@
"""
summary: show ctree graph
description:
Registers an action that can be used to show the graph of the ctree.
The current item will be highlighted in the graph.
The command shortcut is `Ctrl+Shift+G`, and is also added
to the context menu.
To display the graph, we produce a .gdl file, and
request that ida displays that using `ida_gdl.display_gdl`.
"""
from __future__ import print_function
import ida_idaapi
import ida_pro
import ida_hexrays
import ida_kernwin
import ida_gdl
import ida_lines
import ida_idaapi
ACTION_NAME = "vds5.py:displaygraph"
ACTION_SHORTCUT = "Ctrl+Shift+G"
@@ -296,18 +310,31 @@ class display_graph_ah_t(ida_kernwin.action_handler_t):
class vds5_hooks_t(ida_hexrays.Hexrays_Hooks):
def populating_popup(self, widget, handle, vu):
idaapi.attach_action_to_popup(vu.ct, None, ACTION_NAME)
ida_kernwin.attach_action_to_popup(vu.ct, None, ACTION_NAME)
return 0
if ida_hexrays.init_hexrays_plugin():
ida_kernwin.register_action(
ida_kernwin.action_desc_t(
ACTION_NAME,
"Hex-Rays show C graph (IDAPython)",
display_graph_ah_t(),
ACTION_SHORTCUT))
vds5_hooks = vds5_hooks_t()
vds5_hooks.hook()
else:
print('hexrays-graph: hexrays is not available.')
# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
flags = ida_idaapi.PLUGIN_HIDE
wanted_name = "Hex-Rays show C graph (IDAPython)"
wanted_hotkey = ""
comment = "Sample plugin5 for Hex-Rays decompiler"
help = ""
def init(self):
if ida_hexrays.init_hexrays_plugin():
ida_kernwin.register_action(
ida_kernwin.action_desc_t(
ACTION_NAME,
"Hex-Rays show C graph (IDAPython)",
display_graph_ah_t(),
ACTION_SHORTCUT))
self.vds5_hooks = vds5_hooks_t()
self.vds5_hooks.hook()
return ida_idaapi.PLUGIN_KEEP # keep us in the memory
def term(self):
self.vds5_hooks.unhook()
def run(self, arg):
pass
def PLUGIN_ENTRY():
return my_plugin_t()
+29 -12
View File
@@ -1,10 +1,13 @@
"""
This is a crude (and not very pythonic) reimplementation of the example
hexrays plugin 'hexrays_sample6.cpp', shipped with the Hex-Rays decompiler.
summary: superficially modify the decompilation output
It modifies the decompilation output: removes some space characters.
description:
modifies the decompilation output in a superficial manner,
by removing some white spaces
Note: this is rather crude, not quite "pythonic" code.
"""
from __future__ import print_function
import idautils
@@ -51,6 +54,9 @@ def remove_spaces(sl):
last = None # last seen character
while True:
# go until comments
if l.startswith("//"):
push(l)
break
dbg("-" * 60)
nchars = ida_lines.tag_advance(l, 1)
push(l[0:nchars])
@@ -58,9 +64,6 @@ def remove_spaces(sl):
l = my_tag_skipcodes(l, out)
if not l:
break
if l.startswith("//"):
push(l)
break
c = l[0]
dbg("c: '%s', last: '%s', l: '%s'" % (c, last, l))
if delim:
@@ -89,8 +92,22 @@ class vds6_hooks_t(ida_hexrays.Hexrays_Hooks):
remove_spaces(sl);
return 0
if ida_hexrays.init_hexrays_plugin():
vds6_hooks = vds6_hooks_t()
vds6_hooks.hook()
else:
print('remove spaces: hexrays is not available.')
# a plugin interface, boilerplate code
class my_plugin_t(ida_idaapi.plugin_t):
flags = ida_idaapi.PLUGIN_HIDE
wanted_name = "Hex-Rays space remover (IDAPython)"
wanted_hotkey = ""
comment = "Sample plugin6 for Hex-Rays decompiler"
help = ""
def init(self):
if ida_hexrays.init_hexrays_plugin():
self.vds6_hooks = vds6_hooks_t()
self.vds6_hooks.hook()
return ida_idaapi.PLUGIN_KEEP # keep us in the memory
def term(self):
self.vds6_hooks.unhook()
def run(self, arg):
pass
def PLUGIN_ENTRY():
return my_plugin_t()
+17 -25
View File
@@ -1,31 +1,25 @@
""" It demonstrates how to iterate a cblock_t object.
Author: EiNSTeiN_ <einstein@g3nius.org>
This is a rewrite in Python of the vds7 example that comes with hexrays sdk.
"""
summary: iterate a cblock_t object
description:
Using a `ida_hexrays.ctree_visitor_t`, search for
`ida_hexrays.cit_block` instances and dump them.
author: EiNSTeiN_ (einstein@g3nius.org)
"""
from __future__ import print_function
import idautils
import idaapi
import idc
import ida_hexrays
import traceback
class cblock_visitor_t(idaapi.ctree_visitor_t):
class cblock_visitor_t(ida_hexrays.ctree_visitor_t):
def __init__(self):
idaapi.ctree_visitor_t.__init__(self, idaapi.CV_FAST)
return
ida_hexrays.ctree_visitor_t.__init__(self, ida_hexrays.CV_FAST)
def visit_insn(self, ins):
try:
if ins.op == idaapi.cit_block:
self.dump_block(ins.ea, ins.cblock)
except:
traceback.print_exc()
if ins.op == ida_hexrays.cit_block:
self.dump_block(ins.ea, ins.cblock)
return 0
def dump_block(self, ea, b):
@@ -34,18 +28,16 @@ class cblock_visitor_t(idaapi.ctree_visitor_t):
for ins in b:
print(" %x: insn %s" % (ins.ea, ins.opname))
return
class vds7_hooks_t(idaapi.Hexrays_Hooks):
class vds7_hooks_t(ida_hexrays.Hexrays_Hooks):
def maturity(self, cfunc, maturity):
if maturity == idaapi.CMAT_BUILT:
if maturity == ida_hexrays.CMAT_BUILT:
cbv = cblock_visitor_t()
cbv.apply_to(cfunc.body, None)
return 0
if idaapi.init_hexrays_plugin():
if ida_hexrays.init_hexrays_plugin():
vds7_hooks = vds7_hooks_t()
vds7_hooks.hook()
else:
+12 -10
View File
@@ -1,14 +1,16 @@
"""
summary: using `ida_hexrays.udc_filter_t`
# Hex-Rays Decompiler project
# Copyright (c) 2007-2019 by Hex-Rays, support@hex-rays.com
# ALL RIGHTS RESERVED.
#
# Sample script for Hex-Rays Decompiler usage of udc_filter_t
# class: decompile svc 0x900001 and svc 0x9000F8 as function calls to
# svc_exit() and svc_exit_group() respectively.
# NOTE: You will need to have an ARM + Linux IDB for this script to be usable
#
# It is also added into the right-click menu as "vds8.py:Toggle UDC"
description:
Registers an action that uses a `ida_hexrays.udc_filter_t` to decompile
`svc 0x900001` and `svc 0x9000F8` as function calls to
`svc_exit()` and `svc_exit_group()` respectively.
You will need to have an ARM + Linux IDB for this script to be usable
In addition to having a shortcut, the action will be present
in the context menu.
"""
import ida_idaapi
import ida_hexrays
+13 -6
View File
@@ -1,10 +1,17 @@
"""'Hints' example for Hexrays Decompiler
"""
summary: decompiler hints
Handle 'hxe_create_hint' notification using hooks, to return our own.
If the object under the cursor is:
- a function call, prefix the original decompiler hint with "==> "
- a local variable declaration, replace the hint with our own in the form of "!{varname}" (where '{varname}' is replaced w/ the variable name)
- an 'if' statement, replace the hint with our own, saying "condition"
description:
Handle `ida_hexrays.hxe_create_hint` notification using hooks,
to return our own.
If the object under the cursor is:
* a function call, prefix the original decompiler hint with `==> `
* a local variable declaration, replace the hint with our own in
the form of `!{varname}` (where `{varname}` is replaced with the
variable name)
* an `if` statement, replace the hint with our own, saying "condition"
"""
import ida_idaapi
+84 -43
View File
@@ -1,18 +1,33 @@
"""
Various hooks for Hexrays Decompiler
summary: various decompiler hooks
description:
Shows how to hook to many notifications sent by the decompiler.
This plugin doesn't really accomplish anything: it just prints
the parameters.
The list of notifications handled below should be exhaustive,
and is there to hint at what is possible to accomplish by
subclassing `ida_hexrays.Hexrays_Hooks`
see_also: curpos_details
"""
from __future__ import print_function
import inspect
import ida_idaapi
import ida_typeinf
import ida_hexrays
class vds_hooks_t(ida_hexrays.Hexrays_Hooks):
def _shorten(self, cfunc):
raw = str(cfunc)
if len(raw) > 20:
raw = raw[0:20] + "[...snipped...]"
return raw
def __init__(self):
ida_hexrays.Hexrays_Hooks.__init__(self)
self.display_shortened_cfuncs = False
self.display_vdui_curpos = False
self.inhibit_log = 0;
def _format_lvar(self, v):
parts = []
@@ -26,106 +41,132 @@ class vds_hooks_t(ida_hexrays.Hexrays_Hooks):
parts.append("divisor=%s" % v.divisor)
return "{%s}" % ", ".join(parts)
def _log(self, msg):
print("### %s" % msg)
def _format_vdui_curpos(self, v):
return "cpos={lnnum=%d, x=%d, y=%d}" % (v.cpos.lnnum, v.cpos.x, v.cpos.y)
def _format_value(self, v):
if isinstance(v, ida_hexrays.lvar_t):
v = self._format_lvar(v)
elif isinstance(v, ida_hexrays.cfunc_t):
if self.display_shortened_cfuncs:
self.inhibit_log += 1
v = str(v)
if len(v) > 20:
v = v[0:20] + "[...snipped...]"
self.inhibit_log -= 1
else:
v = "<cfunc>" # cannot print contents: we'll end up being called recursively
elif isinstance(v, ida_hexrays.vdui_t) and self.display_vdui_curpos:
v = str(v) + " " + self._format_vdui_curpos(v)
return str(v)
def _log(self):
if self.inhibit_log <= 0:
stack = inspect.stack()
frame, _, _, _, _, _ = stack[1]
args, _, _, values = inspect.getargvalues(frame)
method_name = inspect.getframeinfo(frame)[2]
argstrs = []
for arg in args[1:]:
argstrs.append("%s=%s" % (arg, self._format_value(values[arg])))
print("### %s: %s" % (method_name, ", ".join(argstrs)))
return 0
def flowchart(self, fc):
return self._log("flowchart: fc=%s" % fc)
return self._log()
def stkpnts(self, mba, stkpnts):
return self._log("stkpnts: mba=%s, stkpnts=%s" % (mba, stkpnts))
return self._log()
def prolog(self, mba, fc, reachable_blocks, decomp_flags):
return self._log("prolog: mba=%s, fc=%s, reachable_blocks=%s, decomp_flags=%x" % (mba, fc, reachable_blocks, decomp_flags))
return self._log()
def microcode(self, mba):
return self._log("microcode: mba=%s" % (mba,))
return self._log()
def preoptimized(self, mba):
return self._log("preoptimized: mba=%s" % (mba,))
return self._log()
def locopt(self, mba):
return self._log("locopt: mba=%s" % (mba,))
return self._log()
def prealloc(self, mba):
return self._log("prealloc: mba=%s" % (mba,))
return self._log()
def glbopt(self, mba):
return self._log("glbopt: mba=%s" % (mba,))
return self._log()
def structural(self, ctrl_graph):
return self._log("structural: ctrl_graph: %s" % (ctrl_graph,))
return self._log()
def maturity(self, cfunc, maturity):
return self._log("maturity: cfunc=%s, maturity=%s" % (self._shorten(cfunc), maturity))
return self._log()
def interr(self, code):
return self._log("interr: code=%s" % (code,))
return self._log()
def combine(self, blk, insn):
return self._log("combine: blk=%s, insn=%s" % (blk, insn))
return self._log()
def print_func(self, cfunc, printer):
# Note: we can't print/str()-ify 'cfunc' here,
# because that'll call print_func() us recursively.
return self._log("print_func: cfunc=..., printer=%s" % (printer,))
return self._log()
def func_printed(self, cfunc):
return self._log("func_printed: cfunc=%s" % (cfunc,))
return self._log()
def resolve_stkaddrs(self, mba):
return self._log("resolve_stkaddrs: mba=%s" % (mba,))
return self._log()
def open_pseudocode(self, vu):
return self._log("open_pseudocode: vu=%s" % (vu,))
return self._log()
def switch_pseudocode(self, vu):
return self._log("switch_pseudocode: vu=%s" % (vu,))
return self._log()
def refresh_pseudocode(self, vu):
return self._log("refresh_pseudocode: vu=%s" % (vu,))
return self._log()
def close_pseudocode(self, vu):
return self._log("close_pseudocode: vu=%s" % (vu,))
return self._log()
def keyboard(self, vu, key_code, shift_state):
return self._log("keyboard: vu=%s, key_code=%s, shift_state=%s" % (vu, key_code, shift_state))
return self._log()
def right_click(self, vu):
return self._log("right_click: vu=%s" % (vu,))
return self._log()
def double_click(self, vu, shift_state):
return self._log("double_click: vu=%s, shift_state=%s" % (vu, shift_state))
return self._log()
def curpos(self, vu):
return self._log("curpos: vu=%s (vu.cpos.lnnum=%d, vu.cpos.x=%d, vu.cpos.y=%d)" % (
vu, vu.cpos.lnnum, vu.cpos.x, vu.cpos.y))
return self._log()
def create_hint(self, vu):
return self._log("create_hint: vu=%s: " % (vu,))
return self._log()
def text_ready(self, vu):
return self._log("text_ready: vu=%s" % (vu,))
return self._log()
def populating_popup(self, widget, popup, vu):
return self._log("populating_popup: widget=%s, popup=%s, vu=%s" % (widget, popup, vu))
return self._log()
def lvar_name_changed(self, vu, v, name, is_user_name):
return self._log("lvar_name_changed: vu=%s, v=%s, name=%s, is_user_name=%s" % (vu, self._format_lvar(v), name, is_user_name))
return self._log()
def lvar_type_changed(self, vu, v, tif):
return self._log("lvar_type_changed: vu=%s, v=%s, tinfo=%s" % (vu, self._format_lvar(v), tif._print()))
return self._log()
def lvar_cmt_changed(self, vu, v, cmt):
return self._log("lvar_cmt_changed: vu=%s, v=%s, cmt=%s" % (vu, self._format_lvar(v), cmt))
return self._log()
def lvar_mapping_changed(self, vu, _from, to):
return self._log("lvar_mapping_changed: vu=%s, from=%s, to=%s" % (vu, _from, to))
return self._log()
def cmt_changed(self, cfunc, loc, cmt):
return self._log("cmt_changed: cfunc=%s, loc=%s, cmt=%s" % (self._shorten(cfunc),loc, cmt))
return self._log()
def build_callinfo(self, *args):
return self._log()
vds_hooks = vds_hooks_t()
vds_hooks.hook()
@@ -1,3 +1,10 @@
"""
summary: modifying local variables
description:
Use a `ida_hexrays.user_lvar_modifier_t` to modify names,
comments and/or types of local variables.
"""
import ida_hexrays
import ida_typeinf
+58 -56
View File
@@ -1,18 +1,25 @@
""" Xref script for Hexrays Decompiler
"""
summary: show decompiler xrefs
Author: EiNSTeiN_ <einstein@g3nius.org>
description:
Show decompiler-style Xref when the `Ctrl+X` key is
pressed in the Decompiler window.
Show decompiler-style Xref when the X key is pressed in the Decompiler window.
- It supports any global name: functions, strings, integers, etc.
- It supports structure member.
* supports any global name: functions, strings, integers, ...
* supports structure member.
author: EiNSTeiN_ (einstein@g3nius.org)
"""
from __future__ import print_function
import ida_kernwin
import ida_hexrays
import ida_typeinf
import ida_idaapi
import ida_struct
import ida_funcs
import idautils
import idaapi
import idc
import traceback
@@ -21,27 +28,27 @@ from PyQt5 import QtCore, QtWidgets
XREF_EA = 0
XREF_STRUC_MEMBER = 1
class XrefsForm(idaapi.PluginForm):
class XrefsForm(ida_kernwin.PluginForm):
def __init__(self, target):
idaapi.PluginForm.__init__(self)
ida_kernwin.PluginForm.__init__(self)
self.target = target
if type(self.target) == idaapi.cfunc_t:
if type(self.target) == ida_hexrays.cfunc_t:
self.__type = XREF_EA
self.__ea = self.target.entry_ea
self.__name = 'Xrefs of %x' % (self.__ea, )
elif type(self.target) == idaapi.cexpr_t and self.target.opname == 'obj':
elif type(self.target) == ida_hexrays.cexpr_t and self.target.opname == 'obj':
self.__type = XREF_EA
self.__ea = self.target.obj_ea
self.__name = 'Xrefs of %x' % (self.__ea, )
elif type(self.target) == idaapi.cexpr_t and self.target.opname in ('memptr', 'memref'):
elif type(self.target) == ida_hexrays.cexpr_t and self.target.opname in ('memptr', 'memref'):
self.__type = XREF_STRUC_MEMBER
name = self.get_struc_name()
@@ -59,10 +66,11 @@ class XrefsForm(idaapi.PluginForm):
xtype = x.type
xtype.remove_ptr_or_array()
typename = idaapi.print_tinfo('', 0, 0, idaapi.PRTYPE_1LINE, xtype, '', '')
typename = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, xtype, '', '')
sid = idc.get_struc_id(typename)
member = idc.get_member_name(sid, m)
sid = ida_struct.get_struc_id(typename)
sptr = ida_struct.get_struc(sid)
member = ida_struct.get_member(sptr, m)
return '%s::%s' % (typename, member)
@@ -76,7 +84,7 @@ class XrefsForm(idaapi.PluginForm):
return
def Show(self):
idaapi.PluginForm.Show(self, self.__name)
ida_kernwin.PluginForm.Show(self, self.__name)
return
def populate_form(self):
@@ -109,7 +117,7 @@ class XrefsForm(idaapi.PluginForm):
def double_clicked(self, row, column):
ea = self.functions[row]
idaapi.open_pseudocode(ea, True)
ida_hexrays.open_pseudocode(ea, True)
return
@@ -125,12 +133,12 @@ class XrefsForm(idaapi.PluginForm):
lines = []
for stmt in insnvec:
qp = idaapi.qstring_printer_t(cfunc.__deref__(), False)
qp = ida_hexrays.qstring_printer_t(cfunc, False)
stmt._print(0, qp)
s = qp.s.split('\n')[0]
#~ s = idaapi.tag_remove(s)
#~ s = ida_lines.tag_remove(s)
lines.append(s)
return '\n'.join(lines)
@@ -141,18 +149,13 @@ class XrefsForm(idaapi.PluginForm):
items = []
for ea in frm:
try:
cfunc = idaapi.decompile(ea)
self.functions.append(cfunc.entry_ea)
self.items.append((ea, idc.get_func_name(cfunc.entry_ea), self.get_decompiled_line(cfunc, ea)))
except Exception as e:
print('could not decompile: %s' % (str(e), ))
raise
return
cfunc = ida_hexrays.decompile(ea)
if not cfunc:
print('Decompilation of %x failed' % (ea, ))
continue
self.functions.append(cfunc.entry_ea)
self.items.append((ea, ida_funcs.get_func_name(cfunc.entry_ea) or "", self.get_decompiled_line(cfunc, ea)))
def get_items_for_type(self):
x = self.target.operands['x']
@@ -160,36 +163,35 @@ class XrefsForm(idaapi.PluginForm):
xtype = x.type
xtype.remove_ptr_or_array()
typename = idaapi.print_tinfo('', 0, 0, idaapi.PRTYPE_1LINE, xtype, '', '')
typename = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, xtype, '', '')
addresses = []
for ea in idautils.Functions():
try:
cfunc = idaapi.decompile(ea)
except:
cfunc = ida_hexrays.decompile(ea)
if not cfunc:
print('Decompilation of %x failed' % (ea, ))
continue
str(cfunc)
print(str(cfunc)) # KLUDGE
for citem in cfunc.treeitems:
citem = citem.to_specific_type
if not (type(citem) == idaapi.cexpr_t and citem.opname in ('memptr', 'memref')):
if not (type(citem) == ida_hexrays.cexpr_t and citem.opname in ('memptr', 'memref')):
continue
_x = citem.operands['x']
_m = citem.operands['m']
_xtype = _x.type
_xtype.remove_ptr_or_array()
_typename = idaapi.print_tinfo('', 0, 0, idaapi.PRTYPE_1LINE, _xtype, '', '')
_typename = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, _xtype, '', '')
if not (_typename == typename and _m == m):
continue
parent = citem
while parent:
if type(parent.to_specific_type) == idaapi.cinsn_t:
if type(parent.to_specific_type) == ida_hexrays.cinsn_t:
break
parent = cfunc.body.find_parent_of(parent)
@@ -200,7 +202,7 @@ class XrefsForm(idaapi.PluginForm):
if parent.ea in addresses:
continue
if parent.ea == idaapi.BADADDR:
if parent.ea == ida_idaapi.BADADDR:
print('parent.ea is BADADDR')
continue
@@ -209,7 +211,7 @@ class XrefsForm(idaapi.PluginForm):
self.functions.append(cfunc.entry_ea)
self.items.append((
parent.ea,
idc.get_func_name(cfunc.entry_ea),
ida_funcs.get_func_name(cfunc.entry_ea) or "",
self.get_decompiled_line(cfunc, parent.ea)))
@@ -250,13 +252,13 @@ class XrefsForm(idaapi.PluginForm):
pass
class show_xrefs_ah_t(idaapi.action_handler_t):
class show_xrefs_ah_t(ida_kernwin.action_handler_t):
def __init__(self):
idaapi.action_handler_t.__init__(self)
ida_kernwin.action_handler_t.__init__(self)
self.sel = None
def activate(self, ctx):
vu = idaapi.get_widget_vdui(ctx.widget)
vu = ida_hexrays.get_widget_vdui(ctx.widget)
if not vu or not self.sel:
print("No vdui? Strange, since this action should be enabled only for pseudocode views.")
return 0
@@ -266,32 +268,32 @@ class show_xrefs_ah_t(idaapi.action_handler_t):
return 1
def update(self, ctx):
if ctx.widget_type != idaapi.BWN_PSEUDOCODE:
return idaapi.AST_DISABLE_FOR_WIDGET
vu = idaapi.get_widget_vdui(ctx.widget)
vu.get_current_item(idaapi.USE_KEYBOARD)
if ctx.widget_type != ida_kernwin.BWN_PSEUDOCODE:
return ida_kernwin.AST_DISABLE_FOR_WIDGET
vu = ida_hexrays.get_widget_vdui(ctx.widget)
vu.get_current_item(ida_hexrays.USE_KEYBOARD)
item = vu.item
self.sel = None
if item.citype == idaapi.VDI_EXPR and item.it.to_specific_type.opname in ('obj', 'memref', 'memptr'):
if item.citype == ida_hexrays.VDI_EXPR and item.it.to_specific_type.opname in ('obj', 'memref', 'memptr'):
# if an expression is selected. verify that it's either a cot_obj, cot_memref or cot_memptr
self.sel = item.it.to_specific_type
elif item.citype == idaapi.VDI_FUNC:
elif item.citype == ida_hexrays.VDI_FUNC:
# if the function itself is selected, show xrefs to it.
self.sel = item.f
return idaapi.AST_ENABLE if self.sel else idaapi.AST_DISABLE
return ida_kernwin.AST_ENABLE if self.sel else ida_kernwin.AST_DISABLE
class vds_xrefs_hooks_t(idaapi.Hexrays_Hooks):
class vds_xrefs_hooks_t(ida_hexrays.Hexrays_Hooks):
def populating_popup(self, widget, phandle, vu):
idaapi.attach_action_to_popup(widget, phandle, "vdsxrefs:show", None)
ida_kernwin.attach_action_to_popup(widget, phandle, "vdsxrefs:show", None)
return 0
if idaapi.init_hexrays_plugin():
adesc = idaapi.action_desc_t('vdsxrefs:show', 'Show xrefs', show_xrefs_ah_t(), "Ctrl+X")
if idaapi.register_action(adesc):
if ida_hexrays.init_hexrays_plugin():
adesc = ida_kernwin.action_desc_t('vdsxrefs:show', 'Show xrefs', show_xrefs_ah_t(), "Ctrl+X")
if ida_kernwin.register_action(adesc):
vds_xrefs_hooks = vds_xrefs_hooks_t()
vds_xrefs_hooks.hook()
else:
+357
View File
@@ -0,0 +1,357 @@
"""
summary: logging IDB events
description:
these hooks will be notified about IDB events, and
dump their information to the "Output" window
"""
import inspect
import ida_idp
class idb_logger_hooks_t(ida_idp.IDB_Hooks):
def __init__(self):
ida_idp.IDB_Hooks.__init__(self)
self.inhibit_log = 0;
def _format_value(self, v):
return str(v)
def _log(self, msg=None):
if self.inhibit_log <= 0:
if msg:
print(">>> idb_logger_hooks_t: %s" % msg)
else:
stack = inspect.stack()
frame, _, _, _, _, _ = stack[1]
args, _, _, values = inspect.getargvalues(frame)
method_name = inspect.getframeinfo(frame)[2]
argstrs = []
for arg in args[1:]:
argstrs.append("%s=%s" % (arg, self._format_value(values[arg])))
print(">>> idb_logger_hooks_t.%s: %s" % (method_name, ", ".join(argstrs)))
return 0
def adding_segm(self, segment):
return self._log()
def allsegs_moved(self, info):
return self._log()
def auto_empty(self):
return self._log()
def auto_empty_finally(self):
return self._log()
def bookmark_changed(self, index, pos, desc, op):
return self._log()
def byte_patched(self, ea, old_value):
return self._log()
def callee_addr_changed(self, ea, callee):
return self._log()
def changing_cmt(self, ea, is_repeatable, new_comment):
return self._log()
def changing_enum_bf(self, tid, new_bf):
return self._log()
def changing_enum_cmt(self, tid, is_repeatable, new_comment):
return self._log()
def changing_op_ti(self, ea, n, new_type, new_fnames):
return self._log()
def changing_op_type(self, ea, n, opinfo):
return self._log()
def changing_range_cmt(self, kind, _range, comment, is_repeatable):
return self._log()
def changing_segm_class(self, segment):
return self._log()
def changing_segm_end(self, segment, new_end, flags):
return self._log()
def changing_segm_name(self, segment, old_name):
return self._log()
def changing_segm_start(self, segment, new_start, flags):
return self._log()
def changing_struc_align(self, sptr):
return self._log()
def changing_struc_cmt(self, tid, is_repeatable, comment):
return self._log()
def changing_struc_member(self, sptr, mptr, flags, ti, nbytes):
return self._log()
def changing_ti(self, ea, new_type, new_fnames):
return self._log()
def closebase(self):
return self._log()
def cmt_changed(self, ea, is_repeatable):
return self._log()
def compiler_changed(self, may_adjust_inf_fields):
return self._log()
def deleting_enum(self, tid):
return self._log()
def deleting_enum_member(self, tid, cid):
return self._log()
def deleting_func(self, pfn):
return self._log()
def deleting_func_tail(self, pfn, tail):
return self._log()
def deleting_segm(self, start_ea):
return self._log()
def deleting_struc(self, sptr):
return self._log()
def deleting_struc_member(self, sptr, mptr):
return self._log()
def deleting_tryblks(self, _range):
return self._log()
def destroyed_items(self, ea1, ea2, will_disable_range):
return self._log()
def determined_main(self, main):
return self._log()
def dirtree_link(self, dt, path, is_link):
return self._log()
def dirtree_mkdir(self, dt, path):
return self._log()
def dirtree_move(self, dt, _from, to):
return self._log()
def dirtree_rank(self, dt, path, rank):
return self._log()
def dirtree_rmdir(self, dt, path):
return self._log()
def dirtree_rminode(self, dt, inode):
return self._log()
def dirtree_segm_moved(self, dt):
return self._log()
def enum_bf_changed(self, tid):
return self._log()
def enum_cmt_changed(self, tid, is_repeatable):
return self._log()
def enum_created(self, tid):
return self._log()
def enum_deleted(self, tid):
return self._log()
def enum_member_created(self, tid, cid):
return self._log()
def enum_member_deleted(self, tid, cid):
return self._log()
def enum_renamed(self, tid):
return self._log()
def expanding_struc(self, sptr, offset, delta):
return self._log()
def extlang_changed(self, kind, el, idx):
return self._log()
def extra_cmt_changed(self, ea, line_idx, comment):
return self._log()
def flow_chart_created(self, fc):
return self._log()
def frame_deleted(self, pfn):
return self._log()
def func_added(self, pfn):
return self._log()
def func_deleted(self, func_ea):
return self._log()
def func_noret_changed(self, pfn):
return self._log()
def func_tail_appended(self, pfn, tail):
return self._log()
def func_tail_deleted(self, pfn, tail_ea):
return self._log()
def func_updated(self, pfn):
return self._log()
def idasgn_loaded(self, sig_name):
return self._log()
def item_color_changed(self, ea, color):
return self._log()
def kernel_config_loaded(self, pass_number):
return self._log()
def loader_finished(self, li, neflags, filetypename):
return self._log()
def local_types_changed(self):
return self._log()
def make_code(self, insn):
return self._log()
def make_data(self, ea, flags, tid, _len):
return self._log()
def op_ti_changed(self, ea, n, _type, fnames):
return self._log()
def op_type_changed(self, ea, n):
return self._log()
def range_cmt_changed(self, kind, _range, comment, is_repeatable):
return self._log()
def renamed(self, ea, new_name, is_local_name, old_name):
return self._log()
def renaming_enum(self, tid, is_enum, new_name):
return self._log()
def renaming_struc(self, tid, old_name, new_name):
return self._log()
def renaming_struc_member(self, sptr, mptr, new_name):
return self._log()
def savebase(self):
return self._log()
def segm_added(self, segment):
return self._log()
def segm_attrs_updated(self, segment):
return self._log()
def segm_class_changed(self, segment, sclass):
return self._log()
def segm_deleted(self, start_ea, end_ea, flags):
return self._log()
def segm_end_changed(self, segment, old_end):
return self._log()
def segm_moved(self, _from, to, size, changed_netmap):
return self._log()
def segm_name_changed(self, segment, name):
return self._log()
def segm_start_changed(self, segment, old_start):
return self._log()
def set_func_end(self, pfn, new_end):
return self._log()
def set_func_start(self, pfn, new_start):
return self._log()
def sgr_changed(self, start_ea, end_ea, regnum, value, old_value, tag):
return self._log()
def sgr_deleted(self, start_ea, end_ea, regnum):
return self._log()
def stkpnts_changed(self, pfn):
return self._log()
def struc_align_changed(self, sptr):
return self._log()
def struc_cmt_changed(self, tid, is_repeatable):
return self._log()
def struc_created(self, tid):
return self._log()
def struc_deleted(self, tid):
return self._log()
def struc_expanded(self, sptr):
return self._log()
def struc_member_changed(self, sptr, mptr):
return self._log()
def struc_member_created(self, sptr, mptr):
return self._log()
def struc_member_deleted(self, sptr, mid, offset):
return self._log()
def struc_member_renamed(self, sptr, mptr):
return self._log()
def struc_renamed(self, sptr, success):
return self._log()
def tail_owner_changed(self, tail, owner_func, old_owner):
return self._log()
def thunk_func_created(self, pfn):
return self._log()
def ti_changed(self, ea, _type, fnames):
return self._log()
def tryblks_updated(self, tbv):
return self._log()
def updating_tryblks(self, tbv):
return self._log()
def upgraded(self, _from):
return self._log()
def enum_width_changed(self, tid, width):
return self._log()
def enum_flag_changed(self, tid, flags):
return self._log()
def enum_ordinal_changed(self, tid, ordinal):
return self._log()
idb_hooks = idb_logger_hooks_t()
idb_hooks.hook()
+70
View File
@@ -0,0 +1,70 @@
"""
summary: notify the user when an instruction operand changes
description:
Show notifications whenever the user changes
an instruction's operand, or a data item.
"""
import binascii
import ida_idp
import ida_bytes
import ida_nalt
import ida_struct
import ida_enum
class operand_changed_t(ida_idp.IDB_Hooks):
def log(self, msg):
print(">>> %s" % msg)
def op_type_changed(self, ea, n):
flags = ida_bytes.get_flags(ea)
self.log("op_type_changed(ea=0x%08X, n=%d). Flags now: 0x%08X" % (ea, n, flags))
buf = ida_nalt.opinfo_t()
opi = ida_bytes.get_opinfo(buf, ea, n, flags)
if opi:
if ida_bytes.is_struct(flags):
self.log("New struct: 0x%08X (name=%s)" % (
opi.tid,
ida_struct.get_struc_name(opi.tid)))
elif ida_bytes.is_strlit(flags):
encidx = ida_nalt.get_str_encoding_idx(opi.strtype)
if encidx == ida_nalt.STRENC_DEFAULT:
encidx = ida_nalt.get_default_encoding_idx(ida_nalt.get_strtype_bpu(opi.strtype))
encname = ida_nalt.get_encoding_name(encidx)
strlen = ida_bytes.get_max_strlit_length(
ea,
opi.strtype,
ida_bytes.ALOPT_IGNHEADS | ida_bytes.ALOPT_IGNCLT)
raw = ida_bytes.get_strlit_contents(ea, strlen, opi.strtype) or b""
self.log("New strlit: 0x%08X, raw hex=%s (encoding=%s)" % (
opi.strtype,
binascii.hexlify(raw),
encname))
elif ida_bytes.is_off(flags, n):
self.log("New offset: refinfo={target=0x%08X, base=0x%08X, tdelta=0x%08X, flags=0x%X}" % (
opi.ri.target,
opi.ri.base,
opi.ri.tdelta,
opi.ri.flags))
elif ida_bytes.is_enum(flags, n):
self.log("New enum: 0x%08X (enum=%s), serial=%d" % (
opi.ec.tid,
ida_enum.get_enum_name(opi.ec.tid),
opi.ec.serial))
pass
elif ida_bytes.is_stroff(flags, n):
parts = []
for i in range(opi.path.len):
tid = opi.path.ids[i]
parts.append("0x%08X (name=%s)" % (tid, ida_struct.get_struc_name(tid)))
self.log("New stroff: path=[%s] (len=%d, delta=0x%08X)" % (
", ".join(parts),
opi.path.len,
opi.path.delta))
elif ida_bytes.is_custom(flags) or ida_bytes.is_custfmt(flags, n):
self.log("New custom data type") # unimplemented
else:
print("Cannot retrieve opinfo_t")
@@ -0,0 +1,81 @@
"""
summary: Record and replay changes in function prototypes
description:
This is a sample script, that will record (in memory) all changes in
functions prototypes, in order to re-apply them later.
To use this script:
- open an IDB (say, "test.idb")
- modify some functions prototypes (e.g., by triggering the 'Y'
shortcut when the cursor is placed on the first address of a
function)
- reload that IDB, *without saving it first*
- call rpc.replay(), to re-apply the modifications.
Note: 'ti_changed' is also called for changes to the function
frames, but we'll only record function prototypes changes.
"""
import ida_idp
import ida_funcs
import ida_typeinf
class replay_prototypes_changes_t(ida_idp.IDB_Hooks):
def __init__(self):
ida_idp.IDB_Hooks.__init__(self)
# we'll store tuples (ea, typ, fields). We cannot store
# tinfo_t instances in there, because tinfo_t's are only
# valid while the IDB is opened.
# Since the very purpose of this example is to re-apply
# types after the IDB has been closed & re-opened, we
# must therefore keep the serialized version only.
self.memo = []
self.replaying = False
def _deser(self, typ, fields):
tif = ida_typeinf.tinfo_t()
if not tif.deserialize(ida_typeinf.get_idati(), typ, fields):
tif = None
return tif
def ti_changed(self, ea, typ, fields):
if not self.replaying:
pfn = ida_funcs.get_func(ea)
if pfn and pfn.start_ea == ea:
self.memo.append((ea, typ, fields))
# de-serialize, just for the sake of printing
tif = self._deser(typ, fields)
if tif:
print("%x: type changed: %s" % (
ea,
tif._print(None, ida_typeinf.PRTYPE_1LINE)))
def replay(self):
self.replaying = True
try:
for ea, typ, fields in self.memo:
tif = self._deser(typ, fields)
if tif:
print("%x: applying type: %s" % (
ea,
tif._print(None, ida_typeinf.PRTYPE_1LINE)))
# Since that type information was remembered from a change
# the user made, we'll re-apply it as a definite type (i.e.,
# can't be overriden by IDA's auto-analysis/heuristics.)
apply_flags = ida_typeinf.TINFO_DEFINITE
if not ida_typeinf.apply_tinfo(ea, tif, apply_flags):
print("FAILED")
finally:
self.replaying = False
rpc = replay_prototypes_changes_t()
if rpc.hook():
print("""
Please modify some functions prototypes (press 'Y' when the
cursor is on the function name, or first address), and when
you are done reload this IDB, *WITHOUT* saving it first,
and type 'rpc.replay()'
""")
else:
print("Couldn't create hooks")
Binary file not shown.
+25 -14
View File
@@ -1,32 +1,44 @@
"""
summary: override some parts of the processor module
description:
Implements disassembly of BUG_INSTR used in Linux kernel
BUG() macro, which is architecturally undefined and is not
disassembled by IDA's ARM module
See Linux/arch/arm/include/asm/bug.h for more info
"""
from __future__ import print_function
# this script implements disassembly of BUG_INSTR used in Linux kernel BUG() macro
# normally it's architecturally undefined and is not disassembled by IDA's ARM module
# see Linux/arch/arm/include/asm/bug.h
import idaapi
import ida_idp
import ida_bytes
import ida_segregs
ITYPE_BUGINSN = idaapi.CUSTOM_CMD_ITYPE + 10
ITYPE_BUGINSN = ida_idp.CUSTOM_INSN_ITYPE + 10
MNEM_WIDTH = 16
class MyHooks(idaapi.IDP_Hooks):
class MyHooks(ida_idp.IDP_Hooks):
def __init__(self):
idaapi.IDP_Hooks.__init__(self)
ida_idp.IDP_Hooks.__init__(self)
self.reported = []
def ev_ana_insn(self, insn):
t = get_sreg(insn.ea, "T")
if t==0 and get_wide_dword(insn.ea) == 0xE7F001F2:
t_reg = ida_idp.str2reg("T")
t = ida_segregs.get_sreg(insn.ea, t_reg)
if t==0 and ida_bytes.get_wide_dword(insn.ea) == 0xE7F001F2:
insn.itype = ITYPE_BUGINSN
insn.size = 4
elif t!=0 and get_wide_word(insn.ea) == 0xde02:
elif t!=0 and ida_bytes.get_wide_word(insn.ea) == 0xde02:
insn.itype = ITYPE_BUGINSN
insn.size = 2
return insn.size
def ev_emu_insn(self, insn):
if insn.ea == ITYPE_BUGINSN:
return 1
if insn.itype == ITYPE_BUGINSN:
return 1 # do not add any xrefs (stop code flow)
# use default processing for all other functions
return 0
def ev_out_mnem(self, outctx):
@@ -35,10 +47,9 @@ class MyHooks(idaapi.IDP_Hooks):
return 1
return 0
if idaapi.ph.id == idaapi.PLFM_ARM:
if ida_idp.ph.id == ida_idp.PLFM_ARM:
bahooks = MyHooks()
bahooks.hook()
print("BUG_INSTR processor extension installed")
else:
warning("This script only supports ARM files")
+12 -13
View File
@@ -1,20 +1,19 @@
"""
summary: an `ida_idp.IDP_Hooks.assembly` implementation
description:
We add support for assembling the following pseudo instructions:
* "zero eax" -> xor eax, eax
* "nothing" -> nop
"""
from __future__ import print_function
import idaapi
import ida_idp
import idautils
"""
This is a sample script for extending the assemble() hook.
We add support for assembling the following pseudo instructions:
- "zero eax" -> xor eax, eax
- "nothing" -> nop
(c) Hex-Rays
"""
#--------------------------------------------------------------------------
class assemble_idp_hook_t(idaapi.IDP_Hooks):
class assemble_idp_hook_t(ida_idp.IDP_Hooks):
def ev_assemble(self, ea, cs, ip, use32, line):
line = line.strip()
if line == "zero eax":
+28
View File
@@ -0,0 +1,28 @@
body
{
margin: 3%;
}
.exp-col
{
cursor: pointer;
padding: 0 4px 0 6px;
}
.collapsed-entry .details
{
display: none;
}
.example-entry .details
{
margin-left: 3%;
padding: 6px;
background-color: #eef;
}
a
{
text-decoration: none;
}
+4386
View File
File diff suppressed because it is too large Load Diff
+72
View File
@@ -0,0 +1,72 @@
function on_see_also(see_also)
{
var idx = see_also.indexOf("#");
if ( idx > -1 )
see_also = see_also.substring(idx+1,see_also.length);
var entry = find_entry_el(document.getElementById('DIV_' + see_also));
if ( entry )
{
set_entry_state(entry, true);
entry.scrollIntoView();
}
}
function find_parent_with_class(el, klass)
{
while ( el )
{
if ( el.className && el.className.indexOf(klass) > -1 )
return el;
el = el.parentNode;
}
}
function find_child_with_class(el, klass)
{
return el.querySelector("." + klass);
}
function find_entry_el(el) { return find_parent_with_class(el, "example-entry"); }
function find_expander(entry_el) { return find_child_with_class(entry_el, "expander"); }
function find_collapser(entry_el) { return find_child_with_class(entry_el, "collapser"); }
function set_entry_state(entry_el, expanded)
{
var collapser_el = find_collapser(entry_el);
var expander_el = find_expander(entry_el);
collapser_el.style.display = expanded ? "" : "none";
expander_el.style.display = expanded ? "none" : "";
if ( expanded )
entry_el.classList.remove("collapsed-entry");
else
entry_el.classList.add("collapsed-entry");
}
function handle_click(e)
{
e = e || window.event;
var el = e.target || e.srcElement;
var entry_el = find_entry_el(el);
var ok = false;
if ( el.classList.contains("collapser") )
set_entry_state(entry_el, false);
else if ( el.classList.contains("expander") )
set_entry_state(entry_el, true);
else if ( el.classList.contains("ex_link") ) // see also
on_see_also(el.href);
else
return;
e.stopPropagation();
}
function handle_toplevel_action(e)
{
e = e || window.event;
var el = e.target || e.srcElement;
var expanded = el.classList.contains("expand-all");
var els = document.getElementsByClassName("example-entry");
for ( var idx = 0, n = els.length; idx < n; ++idx )
set_entry_state(els[idx], expanded);
e.stopPropagation();
}
+3648
View File
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More