mirror of
https://github.com/idapython/src
synced 2026-06-08 14:47:00 +00:00
IDAPython for IDA 7.6 SP1
This commit is contained in:
+2
-1
@@ -1,2 +1,3 @@
|
||||
obj/
|
||||
*.pyc
|
||||
*.pyc
|
||||
idapyswitch*
|
||||
|
||||
@@ -30,3 +30,8 @@ test as well.
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
# get information about function(s)
|
||||
"""
|
||||
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
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
"""
|
||||
summary: custom actions, with icons & tooltips
|
||||
|
||||
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
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
"""
|
||||
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.
|
||||
#
|
||||
# 'ida_kernwin.add_hotkey' offers a simpler alternative to
|
||||
# 'ida_kernwin.register_action', but is much less flexible.
|
||||
#
|
||||
# Author: IDAPython team
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
def hotkey_pressed():
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
"""
|
||||
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
|
||||
#---------------------------------------------------------------------
|
||||
# This script demonstrates the usage of hotkeys, using an alternative API.
|
||||
# See also:
|
||||
# add_hotkey.py
|
||||
# actions.py
|
||||
#
|
||||
# Author: Gergely Erdelyi <gergely.erdelyi@d-dome.net>
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
import ida_expr
|
||||
import ida_kernwin
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
"""
|
||||
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
|
||||
|
||||
# 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.)
|
||||
|
||||
import ida_kernwin
|
||||
import ida_bytes
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
"""
|
||||
summary: programmatically create & populate a structure
|
||||
|
||||
description:
|
||||
Usage of the API to create & populate a structure with
|
||||
members of different types.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
#---------------------------------------------------------------------
|
||||
# Structure test
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
"""
|
||||
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
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
"""
|
||||
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
|
||||
#
|
||||
|
||||
import ida_bytes
|
||||
import ida_idaapi
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
"""
|
||||
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
|
||||
# -----------------------------------------------------------------------
|
||||
# This example illustrates how to use the 'get_extra_cmt' API,
|
||||
# to retrieve anterior and posterior extra comments.
|
||||
#
|
||||
# After running this script, use Ctrl+Shift+Y when in the disassembly
|
||||
# view to print previous extra comment, and Ctrl+Shift+Z to print next
|
||||
# extra comments.
|
||||
#
|
||||
# (c) Hex-Rays
|
||||
|
||||
import ida_lines
|
||||
import ida_kernwin
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
# -*- 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
|
||||
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
"""
|
||||
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
|
||||
|
||||
# This example illustrates how to accurately retrieve the current selection.
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# (c) Hex-Rays
|
||||
|
||||
import ida_kernwin
|
||||
import ida_lines
|
||||
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
"""
|
||||
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
|
||||
#
|
||||
|
||||
import ida_expr
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
#---------------------------------------------------------------------
|
||||
# Example user initialisation script: idapythonrc.py
|
||||
#
|
||||
# Place this script to ~/.idapro/ or to
|
||||
# %APPDATA%\Hex-Rays\IDA Pro
|
||||
#---------------------------------------------------------------------
|
||||
"""
|
||||
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")
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
"""
|
||||
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 ida_lines
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
"""
|
||||
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 ida_nalt
|
||||
|
||||
nimps = ida_nalt.get_import_module_qty()
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
"""
|
||||
summary: enumerate patched bytes
|
||||
|
||||
description:
|
||||
Using the API to iterate over all the places in the file,
|
||||
that were patched using IDA.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
# -------------------------------------------------------------------------
|
||||
# This is an example illustrating how to visit all patched bytes in Python
|
||||
# (c) Hex-Rays
|
||||
|
||||
import ida_bytes
|
||||
import ida_idaapi
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
"""
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#
|
||||
# This example demonstrates how to retrieve all xrefs to
|
||||
# a stack variable within a function.
|
||||
# Contrary to (in-memory) data & code xrefs, retrieving
|
||||
# stack variables xrefs require a bit more work than just
|
||||
# using ida_xref's first_to(), next_to() (or higher level
|
||||
# utilities such as idautils.XrefsTo)
|
||||
#
|
||||
# Press Ctrl+Shift+F7 to invoke the action that will print xrefs
|
||||
# to the variable name that's under the cursor.
|
||||
#
|
||||
"""
|
||||
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"
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
"""
|
||||
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 idautils
|
||||
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
"""
|
||||
This example demonstrates how one can automate IDA to perform auto-analysis
|
||||
on a file and, as soon as it is finished, produce a .c file containing the
|
||||
decompilation of all the functions that the file contains.
|
||||
summary: decompile entire file
|
||||
|
||||
Run like so:
|
||||
ida -A "-S...path/to/produce_c_file.py" <binary-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.
|
||||
|
||||
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.)
|
||||
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
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
"""
|
||||
This example demonstrates how one can automate IDA to perform auto-analysis
|
||||
on a file and, as soon as it is finished, produce a .lst file containing the
|
||||
disassembly.
|
||||
summary: produce listing
|
||||
|
||||
Run like so:
|
||||
ida -A "-S...path/to/produce_lst_file.py" <binary-file>
|
||||
description:
|
||||
automate IDA to perform auto-analysis on a file and,
|
||||
once that is done, produce a .lst file with the disassembly.
|
||||
|
||||
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.)
|
||||
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
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
"""
|
||||
summary: using timers for delayed execution
|
||||
|
||||
description:
|
||||
Register (possibly repeating) timers.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
# -------------------------------------------------------------------------
|
||||
# This is an example illustrating how to use timers
|
||||
# (c) Hex-Rays
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
"""
|
||||
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
|
||||
# * 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.
|
||||
#
|
||||
# (c) Hex-Rays
|
||||
#
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
"""
|
||||
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
|
||||
#
|
||||
# This sample illustrates how to use appcall, with the
|
||||
# 'simple_appcall_linux32' or 'simple_appcall_linux64' test
|
||||
# programs (see subdirectories.)
|
||||
#
|
||||
# This example will run the test program and stop wherever
|
||||
# the cursor currently is, and then perform an appcall to
|
||||
# `ref4` and `ref8`
|
||||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
"""
|
||||
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
|
||||
#
|
||||
# This sample illustrates how to use appcall, with the
|
||||
# 'simple_appcall_win32.exe' or 'simple_appcall_win64.exe' test
|
||||
# programs (see subdirectories.)
|
||||
#
|
||||
# This example will run the test program and stop wherever
|
||||
# the cursor currently is, and then perform an appcall to
|
||||
# `ref4` and `ref8`
|
||||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
"""
|
||||
summary: programmatically drive a debugging session
|
||||
|
||||
description:
|
||||
Start a debugging session, step through the first five
|
||||
instructions. Each instruction is disassembled after
|
||||
execution.
|
||||
"""
|
||||
|
||||
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
|
||||
#
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
import ida_dbg
|
||||
import ida_ida
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
"""
|
||||
This script demonstrates using the low-level tracing hook (dbg_trace)
|
||||
It can be run like: ida[t].exe -B -Sdbg_trace.py -Ltrace.log file.exe
|
||||
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
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
"""
|
||||
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
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
"""
|
||||
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 ida_dbg
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
"""
|
||||
This example shows how one can dynamically alter the lines background
|
||||
rendering for pseudocode listings (as opposed to using
|
||||
ida_hexrays.cfunc_t.pseudocode[N].bgcolor)
|
||||
summary: interactively color certain pseudocode lines
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
from __future__ import print_function
|
||||
"""
|
||||
summary: automatic decompilation of functions
|
||||
|
||||
#
|
||||
# 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, for example:
|
||||
# idat -Ldecompile.log -Sdecompile_entry_points.py -c file
|
||||
#
|
||||
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
|
||||
|
||||
import ida_ida
|
||||
import ida_auto
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
"""
|
||||
summary: decompile & print current function.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import ida_hexrays
|
||||
|
||||
+15
-15
@@ -1,18 +1,18 @@
|
||||
#
|
||||
# Hex-Rays Decompiler project
|
||||
# Copyright (c) 2007-2021 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
|
||||
|
||||
+19
-18
@@ -1,21 +1,22 @@
|
||||
#
|
||||
# Hex-Rays Decompiler project
|
||||
# Copyright (c) 2007-2021 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
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
#
|
||||
# Hex-Rays Decompiler project
|
||||
# Copyright (c) 2007-2021 by Hex-Rays, support@hex-rays.com
|
||||
# ALL RIGHTS RESERVED.
|
||||
#
|
||||
# Sample script 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
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
#
|
||||
# Hex-Rays Decompiler project
|
||||
# Copyright (c) 2007-2021 by Hex-Rays, support@hex-rays.com
|
||||
# ALL RIGHTS RESERVED.
|
||||
#
|
||||
# Sample script 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
|
||||
|
||||
+12
-12
@@ -1,15 +1,15 @@
|
||||
#
|
||||
# Hex-Rays Decompiler project
|
||||
# Copyright (c) 2007-2021 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
|
||||
|
||||
+15
-11
@@ -1,14 +1,18 @@
|
||||
#
|
||||
# Hex-Rays Decompiler project
|
||||
# Copyright (c) 2007-2021 by Hex-Rays, support@hex-rays.com
|
||||
# ALL RIGHTS RESERVED.
|
||||
#
|
||||
# Sample plugin for Hex-Rays Decompiler.
|
||||
# It installs a custom microcode optimization rule:
|
||||
# x | ~x => -1
|
||||
#
|
||||
# To see this plugin in action please use be_ornot_be.idb
|
||||
#
|
||||
"""
|
||||
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
|
||||
|
||||
+18
-17
@@ -1,26 +1,27 @@
|
||||
""" Example: provide custom call type dynamically
|
||||
"""
|
||||
summary: dynamically provide a custom call type
|
||||
|
||||
This plugin can greatly improve decompilation of indirect calls:
|
||||
description:
|
||||
This plugin can greatly improve decompilation of indirect calls:
|
||||
|
||||
call [eax+4]
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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
|
||||
|
||||
@@ -1,9 +1,38 @@
|
||||
""" 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
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
""" 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
|
||||
|
||||
author: EiNSTeiN_ <einstein@g3nius.org>
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
"""
|
||||
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_pro
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
""" 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 ida_hexrays
|
||||
|
||||
+12
-10
@@ -1,14 +1,16 @@
|
||||
"""
|
||||
summary: using `ida_hexrays.udc_filter_t`
|
||||
|
||||
# Hex-Rays Decompiler project
|
||||
# Copyright (c) 2007-2021 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
"""
|
||||
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.
|
||||
|
||||
Also, the list of notifications handled below, isn't exhaustive.
|
||||
Please investigate `ida_hexrays.Hexrays_Hooks` for a full list.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
""" 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
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
"""
|
||||
This example shows notifications whenever the user changes
|
||||
an instruction's operand, or a data item.
|
||||
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
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
"""
|
||||
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 ida_idp
|
||||
import ida_bytes
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
"""
|
||||
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 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(ida_idp.IDP_Hooks):
|
||||
def assemble(self, ea, cs, ip, use32, line):
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+2928
-4024
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
|
||||
function on_see_also(see_also)
|
||||
{
|
||||
if ( is_expanded() )
|
||||
{
|
||||
// likely it is, since I clicked on "see also";
|
||||
// close it
|
||||
|
||||
expand_toggle(name_expanded);
|
||||
}
|
||||
|
||||
expand_toggle(see_also);
|
||||
|
||||
document.getElementById('IMG_' + see_also).scrollIntoView();
|
||||
return true;
|
||||
}
|
||||
|
||||
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
|
||||
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();
|
||||
}
|
||||
+288
-150
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,23 @@
|
||||
#
|
||||
# This example illustrates how one can execute commands in the
|
||||
# "Output" window, from their own widgets.
|
||||
#
|
||||
# In order to do so, we have to be careful that:
|
||||
# - the original, underlying 'cli:Execute' action, that has to be
|
||||
# triggered for the code present in the input field to execute
|
||||
# and be placed in the history, requires that the input field
|
||||
# has focus (otherwise it simply won't do anything.)
|
||||
# - this, in turn, forces us to do "delayed" execution of that action,
|
||||
# hence the need for a QTimer
|
||||
# - the IDA/SWiG 'TWidget' type that we retrieve through
|
||||
# `ida_kernwin.find_widget`, is not the same type as a
|
||||
# `QtWidgets.QWidget`. We therefore need to convert it using
|
||||
# `ida_kernwin.PluginForm.TWidgetToPyQtWidget`
|
||||
#
|
||||
"""
|
||||
summary: injecting commands is the "Output" window
|
||||
|
||||
description:
|
||||
This example illustrates how one can execute commands in the
|
||||
"Output" window, from their own widgets.
|
||||
|
||||
A few notes:
|
||||
|
||||
* the original, underlying `cli:Execute` action, that has to be
|
||||
triggered for the code present in the input field to execute
|
||||
and be placed in the history, requires that the input field
|
||||
has focus (otherwise it simply won't do anything.)
|
||||
* this, in turn, forces us to do "delayed" execution of that action,
|
||||
hence the need for a `QTimer`
|
||||
* the IDA/SWiG 'TWidget' type that we retrieve through
|
||||
`ida_kernwin.find_widget`, is not the same type as a
|
||||
`QtWidgets.QWidget`. We therefore need to convert it using
|
||||
`ida_kernwin.PluginForm.TWidgetToPyQtWidget`
|
||||
"""
|
||||
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5 import QtGui
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
"""
|
||||
summary: custom painting on top of the navigation band
|
||||
|
||||
description:
|
||||
Using an "event filter", we'll intercept paint events
|
||||
targeted at the navigation band widget, let it paint itself,
|
||||
and then add our own markers on top.
|
||||
"""
|
||||
|
||||
import random
|
||||
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
"""
|
||||
summary: adding PyQt5 widgets into an `ida_kernwin.PluginForm`
|
||||
|
||||
description:
|
||||
Using `ida_kernwin.PluginForm.FormToPyQtWidget`, this script
|
||||
converts IDA's own dockable widget into a type that is
|
||||
recognized by PyQt5, which then enables populating it with
|
||||
regular Qt widgets.
|
||||
"""
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
"""
|
||||
We color the function in the Function window according to its size.
|
||||
The larger the function, the darker the color.
|
||||
summary: using `ida_kernwin.UI_Hooks.get_chooser_item_attrs` to override some defaults
|
||||
|
||||
description:
|
||||
color the function in the Function window according to its size.
|
||||
The larger the function, the darker the color.
|
||||
"""
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
"""
|
||||
This example shows how one can dynamically alter the lines background
|
||||
rendering (as opposed to, say, using ida_nalt.set_item_color()), and
|
||||
also shows how that rendering can be limited to just a few glyphs,
|
||||
not the whole line.
|
||||
summary: dynamically colorize lines backgrounds (or parts of them)
|
||||
|
||||
description:
|
||||
shows how one can dynamically alter the lines background
|
||||
rendering (as opposed to, say, using ida_nalt.set_item_color()),
|
||||
and also shows how that rendering can be limited to just a few
|
||||
glyphs, not the whole line.
|
||||
"""
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"""
|
||||
summary: being notified, and logging a few UI events
|
||||
|
||||
description:
|
||||
hooks to be notified about certain UI events, and
|
||||
dump their information to the "Output" window
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
#---------------------------------------------------------------------
|
||||
# UI hook example
|
||||
#
|
||||
# (c) Hex-Rays
|
||||
#
|
||||
# Maintained By: IDAPython Team
|
||||
#
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
"""
|
||||
This example shows how to use ida_kernwin.UI_Hooks, to respond to a
|
||||
command instead of the action that would otherwise do it.
|
||||
summary: taking precedence over actions
|
||||
|
||||
description:
|
||||
Using `ida_kernwin.UI_Hooks.preprocess_action`, it is possible
|
||||
to respond to a command instead of the action that would
|
||||
otherwise do it.
|
||||
"""
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
"""
|
||||
summary: Non-trivial uses of the `ida_kernwin.Form` helper class
|
||||
|
||||
description:
|
||||
How to query for complex user input, using IDA's built-in forms.
|
||||
|
||||
Note: while this example produces full-fledged forms for complex input,
|
||||
simpler types of inputs might can be retrieved by using
|
||||
`ida_kernwin.ask_str` and similar functions.
|
||||
|
||||
keywords: forms
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
# -----------------------------------------------------------------------
|
||||
# This is an example illustrating how to use the Form class
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
"""
|
||||
summary: drawing custom graphs
|
||||
|
||||
description:
|
||||
Showing custom graphs, using `ida_graph.GraphViewer`. In addition,
|
||||
show how to write actions that can be performed on those.
|
||||
|
||||
keywords: graph, actions
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
# -----------------------------------------------------------------------
|
||||
# This is an example illustrating how to use the user graphing functionality
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
"""
|
||||
summary: follow the movements of a disassembly graph, in another.
|
||||
|
||||
description:
|
||||
Since it is possible to be notified of movements that happen
|
||||
take place in a widget, it's possible to "replay" those
|
||||
movements in another.
|
||||
|
||||
In this case, "IDA View-B" (will be opened if necessary) will
|
||||
show the same contents as "IDA View-A", slightly zoomed out.
|
||||
|
||||
keywords: graph, idaview
|
||||
|
||||
see_also: wrap_idaview
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
@@ -3,14 +3,13 @@ summary: manipulate IDAView and graph
|
||||
|
||||
description:
|
||||
This is an example illustrating how to manipulate an existing IDA-provided
|
||||
view (and thus its graph), in Python.
|
||||
view (and thus possibly its graph), in Python.
|
||||
|
||||
keywords: idaview, graph
|
||||
|
||||
see_also: custom_graph_with_actions, sync_two_graphs
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
"""
|
||||
summary: create custom listings in IDA
|
||||
|
||||
description:
|
||||
How to create simple listings, that will share many of the features
|
||||
as the built-in IDA widgets (highlighting, copy & paste,
|
||||
notifications, ...)
|
||||
|
||||
In addition, creates actions that will be bound to the
|
||||
freshly-created widget (using `ida_kernwin.attach_action_to_popup`.)
|
||||
|
||||
keywords: listing, actions
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
# -----------------------------------------------------------------------
|
||||
# This is an example illustrating how to use customview in Python
|
||||
# (c) Hex-Rays
|
||||
#
|
||||
|
||||
import ida_kernwin
|
||||
import ida_lines
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""
|
||||
summary:
|
||||
This example illustrates how one can implement a "jump to next comment"
|
||||
action within IDA's disassembly view.
|
||||
summary: implement a "jump to next comment" action within IDA's disassembly view.
|
||||
|
||||
description:
|
||||
We want our action not only to find the next line containing a comment,
|
||||
@@ -19,6 +17,8 @@ description:
|
||||
but can also be very handy for spotting tokens of interest (registers,
|
||||
addresses, comments, prefixes, instruction mnemonics, ...)
|
||||
|
||||
keywords: idaview, actions
|
||||
|
||||
see_also: save_and_restore_listing_pos
|
||||
"""
|
||||
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
"""
|
||||
summary: save, and then restore, positions in a listing
|
||||
|
||||
#
|
||||
# This example lets the user save the current position of
|
||||
# the current listing widget, and restore it later
|
||||
#
|
||||
description:
|
||||
Shows how it is possible re-implement IDA's bookmark capability,
|
||||
using 2 custom actions: one action saves the current location,
|
||||
and the other restores it.
|
||||
|
||||
Note that, contrary to actual bookmarks, this example:
|
||||
|
||||
* remembers only 1 saved position
|
||||
* doesn't save that position in the IDB (and therefore cannot
|
||||
be restored if IDA is closed & reopened.)
|
||||
|
||||
keywords: listing, actions
|
||||
|
||||
see_also: jump_next_comment
|
||||
"""
|
||||
|
||||
import ida_kernwin
|
||||
import ida_moves
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
# -----------------------------------------------------------------------
|
||||
# This is an example illustrating how to add custom menus to IDA, either
|
||||
# at the toplevel (i.e., the menubar), or as submenus in existing menus.
|
||||
# (c) Hex-Rays
|
||||
#
|
||||
"""
|
||||
summary: adding custom menus to IDA
|
||||
|
||||
description:
|
||||
It is possible to add custom menus to IDA, either at the
|
||||
toplevel (i.e., into the menubar), or as submenus of existing
|
||||
menus.
|
||||
|
||||
Notes:
|
||||
|
||||
* the same action can be present in more than 1 menu
|
||||
* this example does not deal with context menus
|
||||
|
||||
keywords: actions
|
||||
"""
|
||||
|
||||
import ida_kernwin
|
||||
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
"""
|
||||
summary: A widget showing data in a tabular fashion
|
||||
|
||||
description:
|
||||
Shows how to subclass the ida_kernwin.Choose class to
|
||||
show data organized in a simple table.
|
||||
In addition, registers a couple actions that can be applied to it.
|
||||
|
||||
keywords: chooser, actions
|
||||
|
||||
see_also: choose_multi, chooser_with_folders
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import ida_kernwin
|
||||
from ida_kernwin import Choose
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
"""
|
||||
summary: choose multi
|
||||
summary: A widget showing data in a tabular fashion, providing multiple selection
|
||||
|
||||
description:
|
||||
Similar to @{choose}, but with multiple selection
|
||||
|
||||
keywords: chooser, actions
|
||||
|
||||
see_also: choose, chooser_with_folders
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
"""
|
||||
summary: A widget that can show tabular data either as a simple table,
|
||||
or with a tree-like structure.
|
||||
|
||||
description:
|
||||
By adding the necessary bits to a ida_kernwin.Choose subclass,
|
||||
IDA can show the otherwise tabular data, in a tree-like fashion.
|
||||
|
||||
The important bits to enable this are:
|
||||
|
||||
* ida_dirtree.dirspec_t (and my_dirspec_t)
|
||||
* ida_kernwin.CH_HAS_DIRTREE
|
||||
* ida_kernwin.Choose.OnGetDirTree
|
||||
* ida_kernwin.Choose.OnIndexToInode
|
||||
|
||||
keywords: chooser, folders, actions
|
||||
|
||||
see_also: choose, choose_multi
|
||||
"""
|
||||
|
||||
import inspect
|
||||
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
"""
|
||||
summary: An alternative view over the list of functions
|
||||
|
||||
description:
|
||||
Partially re-implements the "Functions" widget present in
|
||||
IDA, with a custom widget.
|
||||
|
||||
keywords: chooser, functions
|
||||
|
||||
see_also: choose, choose_multi, chooser_with_folders
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
import idautils
|
||||
import idc
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
"""
|
||||
summary: retrieve the strings that are selected in the "Strings" window.
|
||||
|
||||
#
|
||||
# This example lets the user programmatically retrieve
|
||||
# the strings currently selected in the "Strings" window
|
||||
#
|
||||
description:
|
||||
In IDA it's possible to write actions that can be applied even to
|
||||
core (i.e., "standard") widgets. The actions in this example use the
|
||||
action "context" to know what the current selection is.
|
||||
|
||||
This example shows how you can either retrieve string literals data
|
||||
directly from the chooser (`ida_kernwin.get_chooser_data`), or
|
||||
by querying the IDB (`ida_bytes.get_strlit_contents`)
|
||||
|
||||
keywords: actions
|
||||
|
||||
see_also: list_strings
|
||||
"""
|
||||
|
||||
import ida_kernwin
|
||||
import ida_strlist
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"""
|
||||
summary: showing, updating & hiding the progress dialog
|
||||
|
||||
#
|
||||
# A simple example showing how to use:
|
||||
# ida_kernwin.show_wait_box
|
||||
# ida_kernwin.hide_wait_box
|
||||
# ida_kernwin.replace_wait_box
|
||||
#
|
||||
description:
|
||||
Using the progress dialog (aka 'wait box') primitives.
|
||||
|
||||
keywords: actions
|
||||
"""
|
||||
|
||||
import time
|
||||
import random
|
||||
|
||||
-782
@@ -1,782 +0,0 @@
|
||||
|
||||
/*
|
||||
* This utility is meant to be used to let IDA switch between
|
||||
* installed versions of Python3.
|
||||
*
|
||||
* See the documentation placed in 'opts.epilog', near the bottom
|
||||
* of this file.
|
||||
*/
|
||||
|
||||
#ifdef __NT__
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
//lint -esym(1788, iinc) is referenced only by its constructor or destructor
|
||||
//lint -e754 local struct member 'pylib_entries_t::path_history' not referenced
|
||||
|
||||
#include <pro.h>
|
||||
#include <err.h>
|
||||
#include <fpro.h>
|
||||
#include <prodir.h>
|
||||
#include <diskio.hpp>
|
||||
#include <network.hpp>
|
||||
|
||||
#define EXIT_CODE_FORCE_PATH_FAILED 110
|
||||
|
||||
#define EXIT_CODE_NO_INSTALLS 120
|
||||
#define EXIT_CODE_APPLY_FAILED 121
|
||||
|
||||
#ifdef __LINUX__
|
||||
# define EXIT_CODE_SPLIT_DEBUG_EXPAND_DT_NEEDED_ROOM_FAILED 140
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__ // gcc defines those macros, that are in our way
|
||||
# undef major
|
||||
# undef minor
|
||||
#endif
|
||||
|
||||
#ifdef __NT__
|
||||
# define PY_MODULE_EXT ".pyd"
|
||||
#else
|
||||
# define PY_MODULE_EXT ".so"
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct user_args_t
|
||||
{
|
||||
qstring force_path;
|
||||
bool verbose;
|
||||
bool auto_apply;
|
||||
bool dry_run;
|
||||
#ifdef __UNIX__
|
||||
bool ignore_python_config;
|
||||
uint32 major_version;
|
||||
#endif
|
||||
#ifdef __LINUX__
|
||||
qstring split_debug_expand_libpython3_dtneeded_room; //lint !e958 padding of 4 bytes needed to align member on a 8 byte boundary
|
||||
#endif
|
||||
|
||||
user_args_t()
|
||||
: verbose(false),
|
||||
auto_apply(false),
|
||||
dry_run(false)
|
||||
#ifdef __UNIX__
|
||||
, ignore_python_config(false),
|
||||
major_version(3)
|
||||
#endif
|
||||
{}
|
||||
};
|
||||
static user_args_t args;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static int out_ident = 0;
|
||||
struct out_ident_inc_t
|
||||
{
|
||||
out_ident_inc_t() { ++out_ident; }
|
||||
~out_ident_inc_t() { --out_ident; }
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
AS_PRINTF(1, 0) int vout(const char *format, va_list va)
|
||||
{
|
||||
for ( int i = 0; i < out_ident; ++i )
|
||||
printf(" ");
|
||||
return vprintf(format, va);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
AS_PRINTF(1, 2) int out(const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
int rc = vout(format, va);
|
||||
va_end(va);
|
||||
return rc;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
AS_PRINTF(1, 2) int out_verb(const char *format, ...)
|
||||
{
|
||||
int rc = 0;
|
||||
if ( args.verbose )
|
||||
{
|
||||
out("V: ");
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
rc = vout(format, va);
|
||||
va_end(va);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
NORETURN AS_PRINTF(2, 3) void error(int exit_code, const char *format, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
vout(format, va);
|
||||
va_end(va);
|
||||
qexit(exit_code);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct pylib_version_t
|
||||
{
|
||||
int major;
|
||||
int minor;
|
||||
int revision;
|
||||
qstring modifiers; //lint !e958 padding of 4 bytes needed to align member on a 8 byte boundary
|
||||
qstring raw;
|
||||
|
||||
pylib_version_t(
|
||||
int _major=0,
|
||||
int _minor=0,
|
||||
int _revision=0,
|
||||
const char *_modifiers=nullptr,
|
||||
const char *_raw=nullptr)
|
||||
: major(_major),
|
||||
minor(_minor),
|
||||
revision(_revision),
|
||||
modifiers(_modifiers),
|
||||
raw(_raw) {}
|
||||
|
||||
bool valid() const { return major > 0; }
|
||||
|
||||
const char *str(qstring *out) const
|
||||
{
|
||||
out->sprnt("%d.%d.%d%s ('%s')",
|
||||
major, minor, revision,
|
||||
modifiers.c_str(), raw.c_str());
|
||||
return out->c_str();
|
||||
}
|
||||
|
||||
DECLARE_COMPARISONS(pylib_version_t)
|
||||
{
|
||||
if ( major != r.major )
|
||||
return major - r.major;
|
||||
if ( minor != r.minor )
|
||||
return minor - r.minor;
|
||||
if ( revision != r.revision )
|
||||
return revision - r.revision;
|
||||
// when it comes to modifiers, we'll consider:
|
||||
// - that a debug version is lesser than a non-debug one
|
||||
// - that a version with more modifiers is "greater" than
|
||||
// one with fewer modifiers.
|
||||
const int has_debug = qstrstr(modifiers.c_str(), "d") != nullptr;
|
||||
const int r_has_debug = qstrstr(r.modifiers.c_str(), "d") != nullptr;
|
||||
if ( has_debug != r_has_debug )
|
||||
return r_has_debug - has_debug;
|
||||
return modifiers.length() - r.modifiers.length();
|
||||
}
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(pylib_version_t);
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct pylib_entry_t
|
||||
{
|
||||
pylib_version_t version;
|
||||
#ifdef __MAC__
|
||||
pylib_version_t compatibility_version; // only for OSX
|
||||
#endif
|
||||
#ifdef __NT__
|
||||
qstring display_name;
|
||||
#endif
|
||||
qstrvec_t paths;
|
||||
bool preferred;
|
||||
|
||||
pylib_entry_t(const pylib_version_t &_version)
|
||||
: version(_version), preferred(false) {}
|
||||
|
||||
const char *str(qstring *out) const
|
||||
{
|
||||
qstring pbuf, vbuf;
|
||||
for ( auto const &p : paths )
|
||||
{
|
||||
if ( !pbuf.empty() )
|
||||
pbuf.append(", ", 2);
|
||||
pbuf.append(p);
|
||||
}
|
||||
out->sprnt("Version: %s; paths: %s", version.str(&vbuf), pbuf.c_str());
|
||||
if ( preferred )
|
||||
out->append(" (PREFERRED)");
|
||||
return out->c_str();
|
||||
}
|
||||
|
||||
bool operator ==(const pylib_entry_t &r) const { return paths == r.paths; }
|
||||
bool operator !=(const pylib_entry_t &r) const { return !(*this == r); }
|
||||
};
|
||||
DECLARE_TYPE_AS_MOVABLE(pylib_entry_t);
|
||||
typedef qvector<pylib_entry_t> pylib_entry_vec_t;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct pylib_entries_t
|
||||
{
|
||||
pylib_entry_vec_t entries;
|
||||
qstrvec_t path_history;
|
||||
|
||||
pylib_entry_t *get_entry_for_version(const pylib_version_t &version)
|
||||
{
|
||||
for ( auto &e : entries )
|
||||
if ( e.version == version )
|
||||
return &e;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pylib_entry_t &add_entry(const pylib_version_t &version, const qstrvec_t &paths)
|
||||
{
|
||||
pylib_entry_t ne(version);
|
||||
ne.paths = paths;
|
||||
entries.push_back(ne);
|
||||
pylib_entry_t *e = &entries.back();
|
||||
return *e;
|
||||
}
|
||||
|
||||
pylib_entry_t &add_entry(const pylib_version_t &version, const char *path)
|
||||
{
|
||||
qstrvec_t paths;
|
||||
paths.push_back(path);
|
||||
return add_entry(version, paths);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef __UNIX__
|
||||
static void set_preferred_pylib_version(pylib_entries_t *result);
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct pyver_tool_t
|
||||
{
|
||||
static bool reverse_compare_entries(
|
||||
const pylib_entry_t &e0,
|
||||
const pylib_entry_t &e1)
|
||||
{
|
||||
if ( e0.preferred )
|
||||
return true;
|
||||
if ( e1.preferred )
|
||||
return false;
|
||||
int rc = e0.version.compare(e1.version);
|
||||
if ( rc > 0 )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool path_to_pylib_entry(
|
||||
pylib_entry_t *out,
|
||||
const char *path,
|
||||
qstring *errbuf) const
|
||||
{
|
||||
return do_path_to_pylib_entry(out, path, errbuf);
|
||||
}
|
||||
|
||||
void find_python_libs(pylib_entries_t *result) const
|
||||
{
|
||||
do_find_python_libs(result);
|
||||
|
||||
#ifdef __UNIX__
|
||||
set_preferred_pylib_version(result);
|
||||
#endif
|
||||
|
||||
std::sort(result->entries.begin(), result->entries.end(), reverse_compare_entries);
|
||||
|
||||
qstring buf;
|
||||
if ( args.verbose )
|
||||
{
|
||||
out_ident_inc_t iinc;
|
||||
for ( auto const &e : result->entries )
|
||||
out_verb("%s\n", e.str(&buf));
|
||||
}
|
||||
}
|
||||
|
||||
bool apply_version(
|
||||
const pylib_entry_t &entry,
|
||||
qstring *errbuf) const
|
||||
{
|
||||
return do_pick_sip(entry, errbuf)
|
||||
&& do_apply_version(entry, errbuf);
|
||||
}
|
||||
|
||||
private:
|
||||
// These three need to be implemented in different
|
||||
// ways on __NT__, __LINUX__ and __MAC__
|
||||
|
||||
// Look on the filesystem (or the registry on Windows)
|
||||
// for available Python3 installations.
|
||||
void do_find_python_libs(pylib_entries_t *out) const;
|
||||
|
||||
// Given a path to a .so, .dll or .dylib, try and parse the
|
||||
// Python3 version and produce an entry with it.
|
||||
bool do_path_to_pylib_entry(pylib_entry_t *entry, const char *path, qstring *errbuf) const;
|
||||
|
||||
// Pick the right sip.so for the selected version, and
|
||||
// copy it in the PyQt5 directory
|
||||
bool do_pick_sip(const pylib_entry_t &entry, qstring *errbuf) const;
|
||||
|
||||
// Do patch the idapython.[so|dylib] binary (or the
|
||||
// registry on Windows) so that they refer to the right
|
||||
// Python3 version.
|
||||
bool do_apply_version(const pylib_entry_t &entry, qstring *errbuf) const;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
bool pyver_tool_t::do_pick_sip(
|
||||
const pylib_entry_t &entry,
|
||||
qstring *errbuf) const
|
||||
{
|
||||
#ifdef __MAC__
|
||||
if ( entry.version.major == 2 )
|
||||
return true; // nothing to do for Python2
|
||||
#endif
|
||||
const char *src_sip_subdir = entry.version.minor >= 10 ? "python_3.10"
|
||||
: entry.version.minor >= 9 ? "python_3.9"
|
||||
: entry.version.minor >= 8 ? "python_3.8"
|
||||
: "python_3.4";
|
||||
char src_sip_path[QMAXPATH];
|
||||
qmakepath(src_sip_path, sizeof(src_sip_path), idadir(""),
|
||||
"python", "3", "PyQt5", src_sip_subdir, "sip" PY_MODULE_EXT, nullptr);
|
||||
|
||||
if ( !qfileexist(src_sip_path) )
|
||||
{
|
||||
errbuf->sprnt("File not found: \"%s\"", src_sip_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
char dst_sip_path[QMAXPATH];
|
||||
qmakepath(dst_sip_path, sizeof(dst_sip_path), idadir(""),
|
||||
"python", "3", "PyQt5", "sip" PY_MODULE_EXT, nullptr);
|
||||
|
||||
if ( args.dry_run )
|
||||
{
|
||||
out("Would copy %s to %s\n", src_sip_path, dst_sip_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_verb("Copying \"%s\" to \"%s\"\n", src_sip_path, dst_sip_path);
|
||||
const int code = qcopyfile(src_sip_path, dst_sip_path, /*overwrite=*/ true);
|
||||
if ( code != 0 )
|
||||
{
|
||||
errbuf->sprnt("Couldn't copy file \"%s\" to \"%s\": %s",
|
||||
src_sip_path, dst_sip_path, qstrerror(-1));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Accepts:
|
||||
// "3.7"
|
||||
// "3.7.1"
|
||||
// "3.7m"
|
||||
// "3.7.1dm"
|
||||
|
||||
//lint -esym(528, parse_python_version_str) not referenced
|
||||
static bool parse_python_version_str(pylib_version_t *out, const char *raw)
|
||||
{
|
||||
int major = 0;
|
||||
int minor = 0;
|
||||
int revision = 0;
|
||||
int nchars_read;
|
||||
|
||||
const char *p = raw;
|
||||
|
||||
if ( qsscanf(raw, "%d.%d%n", &major, &minor, &nchars_read) != 2 )
|
||||
return false;
|
||||
p += nchars_read;
|
||||
|
||||
if ( p[0] == '.' && qisdigit(p[1]) )
|
||||
{
|
||||
if ( qsscanf(p, ".%d%n", &revision, &nchars_read) != 1 )
|
||||
return false;
|
||||
p += nchars_read;
|
||||
}
|
||||
|
||||
*out = pylib_version_t(major, minor, revision, p, raw);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef __UNIX__
|
||||
//-------------------------------------------------------------------------
|
||||
static bool extract_version_from_str(
|
||||
pylib_version_t *out,
|
||||
const char *p,
|
||||
const char *stem,
|
||||
const char *end_delimiter)
|
||||
{
|
||||
const size_t stemlen = qstrlen(stem);
|
||||
if ( !strneq(p, stem, stemlen) )
|
||||
return false;
|
||||
p += stemlen;
|
||||
const char *p2 = qstrstr(p, end_delimiter);
|
||||
if ( p2 == nullptr )
|
||||
p2 = tail(p);
|
||||
size_t nbytes = p2 - p;
|
||||
char raw[MAXSTR];
|
||||
if ( nbytes > sizeof(raw) - 1 )
|
||||
nbytes = sizeof(raw) - 1;
|
||||
memmove(raw, p, nbytes);
|
||||
raw[nbytes] = '\0';
|
||||
parse_python_version_str(out, raw);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static void set_preferred_pylib_version(pylib_entries_t *result)
|
||||
{
|
||||
// If python(3)-config exists, use it to determine the preferred version
|
||||
if ( args.ignore_python_config )
|
||||
return;
|
||||
|
||||
const char *config_util = args.major_version == 3 ? "python3-config" : "python-config";
|
||||
qstring cmd;
|
||||
cmd.sprnt("%s --libs", config_util);
|
||||
|
||||
qstring error;
|
||||
qstring verbuf;
|
||||
FILE *fp = popen(cmd.c_str(), "r");
|
||||
if ( fp != nullptr )
|
||||
{
|
||||
char outbuf[MAXSTR];
|
||||
/*ssize_t nread =*/ qfread(fp, outbuf, sizeof(outbuf));
|
||||
int rc = pclose(fp);
|
||||
if ( rc == 0 )
|
||||
{
|
||||
pylib_version_t version;
|
||||
const char *p = qstrstr(outbuf, "-lpython");
|
||||
if ( p != nullptr
|
||||
&& extract_version_from_str(&version, p, "-lpython", " ") )
|
||||
{
|
||||
#ifdef __MAC__
|
||||
// python3-config output on OSX will contain modifiers, i.e. "-lpython3.7m", but when detecting pylibs on OSX
|
||||
// we extract the python version from the LC_ID_DYLIB load command, which does not contain modifiers.
|
||||
// it seems safe to ignore the modifiers on OSX, they appear to be only used for compatibility purposes.
|
||||
version.modifiers.clear();
|
||||
#endif
|
||||
out_verb("Preferred version, as reported by \"%s\": %s\n", cmd.c_str(), version.str(&verbuf));
|
||||
pylib_entry_t *e = result->get_entry_for_version(version);
|
||||
if ( e != nullptr )
|
||||
e->preferred = true;
|
||||
else
|
||||
error.sprnt("\"%s\" reports preferred "
|
||||
"version \"%s\", but no corresponding library file "
|
||||
"was found.\n", cmd.c_str(), version.str(&verbuf));
|
||||
}
|
||||
else
|
||||
{
|
||||
error.sprnt("Error parsing \"%s\" output", cmd.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error.sprnt("Error calling \"%s\"", cmd.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error.sprnt("\"%s\" is not available", config_util);
|
||||
}
|
||||
|
||||
if ( !error.empty() )
|
||||
out_verb("%s. Cannot determine preferred version this way.\n",
|
||||
error.c_str());
|
||||
}
|
||||
#endif // __UNIX__
|
||||
|
||||
#if defined(__LINUX__) || defined(__MAC__)
|
||||
# ifdef __LINUX__
|
||||
# define SOSFX "so"
|
||||
# else
|
||||
# define SOSFX "dylib"
|
||||
# endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
struct file_visitor_t
|
||||
{
|
||||
virtual int visit_file(const char *path) = 0;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static int visit_files(
|
||||
file_visitor_t &dv,
|
||||
const char *dir,
|
||||
const char *pattern,
|
||||
int attr = 0)
|
||||
{
|
||||
char path[QMAXPATH];
|
||||
qmakepath(path, sizeof(path), dir, pattern, nullptr);
|
||||
|
||||
qffblk64_t fb;
|
||||
for ( int code = qfindfirst(path, &fb, attr);
|
||||
code == 0;
|
||||
code = qfindnext(&fb) )
|
||||
{
|
||||
qmakepath(path, sizeof(path), dir, fb.ff_name, nullptr);
|
||||
int ret = dv.visit_file(path);
|
||||
if ( ret != 0 )
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//lint -esym(528, for_all_plugin_files) not referenced
|
||||
static int for_all_plugin_files(file_visitor_t &dv, qstring *errbuf)
|
||||
{
|
||||
char path[QMAXPATH];
|
||||
char found = 0;
|
||||
for ( int is_ea64 = 0; is_ea64 < 2; ++is_ea64 )
|
||||
{
|
||||
// Only patch for actually available IDAs (e.g. don't fail in std edition)
|
||||
qmakepath(path, sizeof(path), idadir(""), is_ea64 ? "ida64" : "ida", nullptr);
|
||||
if ( qfileexist(path) )
|
||||
{
|
||||
found++;
|
||||
char fname[QMAXPATH];
|
||||
qsnprintf(fname, sizeof(fname), "idapython%d%s.%s", args.major_version, is_ea64 ? "_64" : "", SOSFX);
|
||||
qmakepath(path, sizeof(path), idadir(""), "plugins", fname, nullptr);
|
||||
int ret = dv.visit_file(path);
|
||||
if ( ret != 0 )
|
||||
return ret;
|
||||
|
||||
qmakepath(path, sizeof(path), idadir(""), "python", args.major_version == 3 ? "3" : "2", is_ea64 ? "ida_64" : "ida_32", nullptr);
|
||||
ret = visit_files(dv, path, "_ida_*.so");
|
||||
if ( ret != 0 )
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if ( !found )
|
||||
{
|
||||
errbuf->sprnt("Nothing to patch in %s", idadir(""));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __LINUX__
|
||||
# include "idapyswitch_linux.cpp"
|
||||
#else
|
||||
# ifdef __NT__
|
||||
# include "idapyswitch_win.cpp"
|
||||
# else
|
||||
# include "idapyswitch_mac.cpp"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static void set_verbose(const char *, void *) { args.verbose = true; }
|
||||
static void set_auto_apply(const char *, void *) { args.auto_apply = true; }
|
||||
static void set_dry_run(const char *, void *) { args.dry_run = true; }
|
||||
static void set_force_path(const char *arg, void *) { args.force_path = arg; }
|
||||
#ifdef __UNIX__
|
||||
static void set_ignore_python_config(const char *, void *) { args.ignore_python_config = true; }
|
||||
#endif
|
||||
#ifdef __LINUX__
|
||||
static void set_split_debug_expand_libpython3_dtneeded_room(const char *arg, void *)
|
||||
{
|
||||
args.split_debug_expand_libpython3_dtneeded_room = arg;
|
||||
}
|
||||
#endif
|
||||
#ifdef __MAC__
|
||||
static void set_use_python2(const char *, void *) { args.major_version = 2; }
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const cliopt_t _opts[] =
|
||||
{
|
||||
{ 'v', "verbose", "Verbose mode", set_verbose, 0 },
|
||||
{ 'a', "auto-apply", "Run non-interactively; automatically apply the preferred version (if found)", set_auto_apply, 0 },
|
||||
{ 'r', "dry-run", "Only report what would happen; don't do it", set_dry_run, 0 },
|
||||
{ 's', "force-path",
|
||||
#ifdef __LINUX__
|
||||
"Have IDAPython use the specified \"/path/to/libpython[...]so\" shared object",
|
||||
#else
|
||||
# ifdef __NT__
|
||||
"Have IDAPython use the specified \"\\path\\to\\python3.dll\" DLL",
|
||||
# else
|
||||
"Have IDAPython use the specified \"/path/to/libpython[...]dylib\" dylib",
|
||||
# endif
|
||||
#endif
|
||||
set_force_path,
|
||||
1
|
||||
},
|
||||
|
||||
#ifdef __UNIX__
|
||||
{ 'k', "ignore-python-config", "Do not use python-config to find out the preferred version number", set_ignore_python_config, 0 },
|
||||
#endif
|
||||
#ifdef __LINUX__
|
||||
{ 'x', "split-debug-and-expand-libpython3-dtneeded-room", "Expand the DT_NEEDED room to N bytes, using the local `patchelf` (needed at build-time only)", set_split_debug_expand_libpython3_dtneeded_room, 1 },
|
||||
#endif
|
||||
#ifdef __MAC__
|
||||
{ 't', "use-python2", "Search for alternate Python2 installations, and patch the Python2 version of idapython", set_use_python2, 0 },
|
||||
#endif
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static const char usage_epilog[] =
|
||||
"Switch between available installations of Python3\n"
|
||||
"\n"
|
||||
"Because Python3 does not systematically install a single,\n"
|
||||
"always-available \"python3.dll\", \"libpython3.so\" or \"python3.dylib\",\n"
|
||||
"but rather allows for multiple versions of Python3 to be\n"
|
||||
"installed in parallel on a given system, many tools\n"
|
||||
"provide a way to switch between those versions.\n"
|
||||
"\n"
|
||||
"IDA is no exception, and this tool is one such Python3 'switcher'.\n"
|
||||
"\n"
|
||||
"It can be run in 3 ways:\n"
|
||||
"\n"
|
||||
" 1) The default, interactive way\n"
|
||||
" -------------------------------\n"
|
||||
" > $ idapyswitch\n"
|
||||
" will look on the filesystem for available Python3 installations,\n"
|
||||
" present the user with a list of found versions (sorted according\n"
|
||||
" to preferability), and let the user pick which one IDA should use.\n"
|
||||
"\n"
|
||||
" 2) The 'automatic' way\n"
|
||||
" ----------------------\n"
|
||||
" > $ idapyswitch --auto-apply\n"
|
||||
" will look on the filesystem for available Python3 installations,\n"
|
||||
" and automatically pick the one it deemed the most preferable.\n"
|
||||
"\n"
|
||||
" 3) The 'manual' way\n"
|
||||
" -------------------\n"
|
||||
#ifdef __NT__
|
||||
" > $ idapyswitch --force-path C:\\Python37\\python3.dll\n"
|
||||
#else
|
||||
# ifdef __LINUX__
|
||||
" > $ idapyswitch --force-path /path/to/libpython3.7dm.so.1.2\n"
|
||||
# else
|
||||
" > $ idapyswitch --force-path /path/to/Python.framework/Versions/3.7/Python\n"
|
||||
# endif
|
||||
#endif
|
||||
" will pick the path that the user provided.\n"
|
||||
"\n"
|
||||
"Once a version is picked, this tool will do the following:\n"
|
||||
"\n"
|
||||
#ifdef __NT__
|
||||
" * place the path to the directory containing python3.dll\n"
|
||||
" into the registry. IDA will pick it up at launch-time,\n"
|
||||
" and add it to the list of paths that have to be looked\n"
|
||||
" into by the DLL loader.\n"
|
||||
#else
|
||||
# ifdef __LINUX__
|
||||
" * patch 'idapython.so' and 'idapython64.so' so that they\n"
|
||||
" have a DT_NEEDED corresponding to the DT_SONAME of the\n"
|
||||
" library that was selected.\n"
|
||||
# else
|
||||
" * patch 'idapython.dylib' and 'idapython64.dylib' so that\n"
|
||||
" they refer to the right Python3 dylib.\n"
|
||||
# endif
|
||||
#endif
|
||||
;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
cliopts_t opts(out);
|
||||
opts.epilog = usage_epilog;
|
||||
opts.add(_opts, qnumber(_opts));
|
||||
opts.apply(argc, argv);
|
||||
|
||||
qstring errbuf;
|
||||
|
||||
#ifdef __LINUX__
|
||||
if ( !args.split_debug_expand_libpython3_dtneeded_room.empty() )
|
||||
{
|
||||
if ( !split_debug_expand_libpython3_dtneeded_room(
|
||||
args.split_debug_expand_libpython3_dtneeded_room.c_str(),
|
||||
&errbuf) )
|
||||
{
|
||||
error(EXIT_CODE_SPLIT_DEBUG_EXPAND_DT_NEEDED_ROOM_FAILED,
|
||||
"Cannot split debug/expand DT_NEEDED room: %s\n",
|
||||
errbuf.c_str());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
pyver_tool_t tool;
|
||||
|
||||
if ( !args.force_path.empty() )
|
||||
{
|
||||
const char *path = args.force_path.c_str();
|
||||
pylib_version_t dummy_version;
|
||||
pylib_entry_t entry(dummy_version);
|
||||
if ( !tool.path_to_pylib_entry(&entry, path, &errbuf) )
|
||||
{
|
||||
error(EXIT_CODE_FORCE_PATH_FAILED,
|
||||
"Cannot determine python library version for \"%s\": %s\n",
|
||||
path, errbuf.c_str());
|
||||
}
|
||||
if ( !tool.apply_version(entry, &errbuf) )
|
||||
{
|
||||
qstring buf;
|
||||
error(EXIT_CODE_FORCE_PATH_FAILED,
|
||||
"Applying \"%s\" (extracted from path \"%s\") failed: %s\n",
|
||||
entry.str(&buf), path, errbuf.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pylib_entries_t entries;
|
||||
tool.find_python_libs(&entries);
|
||||
|
||||
const size_t nentries = entries.entries.size();
|
||||
if ( nentries > 0 )
|
||||
{
|
||||
qstring buf;
|
||||
const pylib_entry_t *preferred = nullptr;
|
||||
if ( args.auto_apply )
|
||||
{
|
||||
preferred = &entries.entries[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
out("The following Python installations were found:\n");
|
||||
for ( size_t i = 0; i < nentries; ++i )
|
||||
{
|
||||
out_ident_inc_t iinc;
|
||||
const pylib_entry_t &e = entries.entries[i];
|
||||
out("#%" FMT_Z ": %s (%s)\n",
|
||||
i,
|
||||
e.version.str(&buf),
|
||||
!e.paths.empty() ? e.paths[0].c_str() : "<unavailable path>");
|
||||
}
|
||||
|
||||
size_t picked = size_t(-1);
|
||||
while ( picked >= nentries )
|
||||
{
|
||||
out("Please pick a number between 0 and %" FMT_Z " (default: 0)\n", nentries-1);
|
||||
char numbuf[MAXSTR];
|
||||
qfgets(numbuf, sizeof(numbuf), stdin);
|
||||
qstring qnumbuf(numbuf);
|
||||
qnumbuf.rtrim('\n');
|
||||
if ( qnumbuf.empty() )
|
||||
picked = 0;
|
||||
else
|
||||
picked = qatoll(qnumbuf.c_str());
|
||||
}
|
||||
preferred = &entries.entries[picked];
|
||||
}
|
||||
|
||||
out("Applying version %s\n", preferred->version.str(&buf));
|
||||
if ( !tool.apply_version(*preferred, &errbuf) )
|
||||
{
|
||||
error(EXIT_CODE_APPLY_FAILED,
|
||||
"Apply failed: %s\n",
|
||||
errbuf.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error(EXIT_CODE_NO_INSTALLS,
|
||||
"No Python installations were found\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,477 +0,0 @@
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool extract_version_from_libpython_filename(
|
||||
pylib_version_t *out,
|
||||
const char *p)
|
||||
{
|
||||
return extract_version_from_str(out, p, "libpython", ".so");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void pyver_tool_t::do_find_python_libs(pylib_entries_t *result) const
|
||||
{
|
||||
//
|
||||
// Find all libpython3*so* present on disk
|
||||
//
|
||||
static const char lib_pattern[] = "libpython3*.so*";
|
||||
static const char *dirs[] =
|
||||
{
|
||||
"/usr/lib/x86_64-linux-gnu", // Debian/Ubuntu
|
||||
"/usr/lib64", // RedHat - FHS
|
||||
#ifdef _DEBUG
|
||||
"/opt/test-python-libs"
|
||||
#endif
|
||||
};
|
||||
|
||||
qstring verbuf;
|
||||
|
||||
for ( size_t i = 0; i < qnumber(dirs); ++i )
|
||||
{
|
||||
const char *d = dirs[i];
|
||||
out_verb("Searching for \"%s\" in \"%s\"\n", lib_pattern, d);
|
||||
{
|
||||
out_ident_inc_t iinc;
|
||||
char path[QMAXPATH];
|
||||
qmakepath(path, sizeof(path), d, lib_pattern, nullptr);
|
||||
qffblk64_t fb;
|
||||
for ( int code = qfindfirst(path, &fb, 0);
|
||||
code == 0;
|
||||
code = qfindnext(&fb) )
|
||||
{
|
||||
pylib_version_t version;
|
||||
if ( extract_version_from_libpython_filename(&version, fb.ff_name) )
|
||||
{
|
||||
qmakepath(path, sizeof(path), d, fb.ff_name, nullptr);
|
||||
out_verb("Found: \"%s\" (version: %s)\n", path, version.str(&verbuf));
|
||||
result->add_entry(version, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
bool pyver_tool_t::do_path_to_pylib_entry(
|
||||
pylib_entry_t *entry,
|
||||
const char *path,
|
||||
qstring *errbuf) const
|
||||
{
|
||||
const char *fname = qbasename(path);
|
||||
const bool ok = fname != nullptr && qfileexist(path);
|
||||
if ( ok )
|
||||
{
|
||||
extract_version_from_libpython_filename(&entry->version, fname);
|
||||
entry->paths.push_back(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
errbuf->sprnt("Couldn't parse file name \"%s\"", fname);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
#include "../../ldr/elf/reader.cpp"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool read_ident_and_header_and_get_dyninfo(
|
||||
dynamic_info_t *out_dyninfo,
|
||||
reader_t::dyninfo_tags_t *out_dyninfo_tags,
|
||||
reader_t &reader,
|
||||
qstring *errbuf)
|
||||
{
|
||||
if ( !reader.read_ident() )
|
||||
{
|
||||
*errbuf = "Couldn't parse ELF file ident";
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !reader.read_header() )
|
||||
{
|
||||
*errbuf = "Couldn't parse ELF file header";
|
||||
return false;
|
||||
}
|
||||
|
||||
dynamic_linking_tables_t dlt;
|
||||
if ( reader.read_section_headers()
|
||||
&& reader.sections.has_valid_dynamic_linking_tables_info() )
|
||||
{
|
||||
dlt = reader.sections.get_dynamic_linking_tables_info();
|
||||
}
|
||||
else if ( reader.read_program_headers()
|
||||
&& reader.pheaders.has_valid_dynamic_linking_tables_info() )
|
||||
{
|
||||
dlt = reader.pheaders.get_dynamic_linking_tables_info();
|
||||
}
|
||||
return dlt.is_valid()
|
||||
&& reader.read_dynamic_info_tags(out_dyninfo_tags, dlt)
|
||||
&& reader.parse_dynamic_info(out_dyninfo, *out_dyninfo_tags);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool find_libpython_dt_needed_info(
|
||||
char out_dt_needed_buf[MAXSTR],
|
||||
qoff64_t *out_dt_needed_off,
|
||||
const reader_t::dyninfo_tags_t &dyninfo_tags,
|
||||
reader_t &reader,
|
||||
const char *path,
|
||||
qstring *errbuf)
|
||||
{
|
||||
static const char needed_stem[] = "libpython";
|
||||
for ( const auto &dyn : dyninfo_tags )
|
||||
{
|
||||
if ( dyn.d_tag == DT_NEEDED )
|
||||
{
|
||||
const qoff64_t off = reader.dyn_strtab.offset + dyn.d_un;
|
||||
input_status_t save_excursion(reader);
|
||||
if ( save_excursion.seek(off) == -1 )
|
||||
{
|
||||
errbuf->sprnt("Couldn't seek to offset %" FMT_64 "u in \"%s\"", off, path);
|
||||
return false;
|
||||
}
|
||||
out_dt_needed_buf[0] = '\0';
|
||||
qlread(reader.get_linput(), out_dt_needed_buf, MAXSTR);
|
||||
out_verb("DT_NEEDED at offset %" FMT_64 "u is: \"%s\"\n", off, out_dt_needed_buf);
|
||||
if ( strneq(out_dt_needed_buf, needed_stem, sizeof(needed_stem)-1) )
|
||||
{
|
||||
*out_dt_needed_off = off;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
errbuf->sprnt("No DT_NEEDED starting with \"%s\" found", needed_stem);
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool patch_dt_needed(
|
||||
const char *path,
|
||||
const qstring &_replacement,
|
||||
qstring *errbuf)
|
||||
{
|
||||
out_verb("Setting relevant DT_NEEDED of \"%s\" to \"%s\"\n", path, _replacement.c_str());
|
||||
out_ident_inc_t iinc;
|
||||
linput_t *linput = open_linput(path, /*remote=*/ false);
|
||||
if ( linput == nullptr )
|
||||
{
|
||||
errbuf->sprnt("File not found: %s", path);
|
||||
return false;
|
||||
}
|
||||
linput_janitor_t lj(linput);
|
||||
reader_t reader(linput);
|
||||
dynamic_info_t dyninfo;
|
||||
reader_t::dyninfo_tags_t dyninfo_tags;
|
||||
if ( !read_ident_and_header_and_get_dyninfo(
|
||||
&dyninfo,
|
||||
&dyninfo_tags,
|
||||
reader,
|
||||
errbuf) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char dt_needed[MAXSTR];
|
||||
qoff64_t dt_needed_off;
|
||||
if ( !find_libpython_dt_needed_info(
|
||||
dt_needed,
|
||||
&dt_needed_off,
|
||||
dyninfo_tags,
|
||||
reader,
|
||||
path,
|
||||
errbuf) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
out_verb("Found DT_NEEDED; currently: \"%s\"\n", dt_needed);
|
||||
|
||||
// count the maximum number of bytes we can store in there
|
||||
size_t room = 0;
|
||||
{
|
||||
input_status_t save_excursion(reader);
|
||||
if ( save_excursion.seek(dt_needed_off) == -1 )
|
||||
{
|
||||
errbuf->sprnt("Couldn't seek to offset %" FMT_64 "u in \"%s\"", dt_needed_off, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// find the end of the current DT_NEEDED
|
||||
uint8 byte;
|
||||
const int64 filesz = qlsize(reader.get_linput());
|
||||
while ( qltell(reader.get_linput()) < filesz && reader.read_byte(&byte) == 0 )
|
||||
if ( byte == 0 )
|
||||
break;
|
||||
|
||||
// then find the beginning of the next string, or the end of file
|
||||
while ( qltell(reader.get_linput()) < filesz && reader.read_byte(&byte) == 0 )
|
||||
if ( byte != 0 )
|
||||
break;
|
||||
room = qltell(reader.get_linput()) - dt_needed_off - 1;
|
||||
}
|
||||
|
||||
bytevec_t replacement;
|
||||
replacement.append(_replacement.c_str(), _replacement.length() + 1);
|
||||
size_t nbytes = replacement.size();
|
||||
out_verb("We have room for %" FMT_Z " bytes, and need to write %" FMT_Z "\n",
|
||||
room, nbytes);
|
||||
|
||||
// and patch
|
||||
if ( room >= nbytes )
|
||||
{
|
||||
if ( room > nbytes )
|
||||
{
|
||||
out_verb("Expanding replacement with %" FMT_Z " '\\0' bytes, to "
|
||||
"override possible previous soname that could derail "
|
||||
"later computation of available room.\n", room - nbytes);
|
||||
replacement.resize(room, 0);
|
||||
nbytes = replacement.size();
|
||||
}
|
||||
|
||||
FILE *fp = openM(path);
|
||||
if ( fp != nullptr )
|
||||
{
|
||||
file_janitor_t fpj(fp);
|
||||
if ( qfseek(fp, dt_needed_off, SEEK_SET) == 0 )
|
||||
{
|
||||
if ( !args.dry_run )
|
||||
{
|
||||
// we want to write the zero as well!
|
||||
if ( qfwrite(fp, replacement.begin(), nbytes) == nbytes )
|
||||
{
|
||||
out_verb("File \"%s\" successfully patched\n", path);
|
||||
}
|
||||
else
|
||||
{
|
||||
errbuf->sprnt("Couldn't write %" FMT_Z " bytes to \"%s\"", nbytes, path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out("Would write %" FMT_Z " bytes (\"%s\") to file\n",
|
||||
nbytes, replacement.begin());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errbuf->sprnt("Cannot seek to position %" FMT_64 "u in \"%s\"", dt_needed_off, path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errbuf->sprnt("Couldn't open \"%s\" for writing", path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errbuf->sprnt("Replacement \"%s\" has a length of %" FMT_Z
|
||||
" bytes, but there is only room for %" FMT_Z ""
|
||||
" bytes in the file. Cannot proceed.\n",
|
||||
replacement.begin(), nbytes, room);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
bool pyver_tool_t::do_apply_version(
|
||||
const pylib_entry_t &entry,
|
||||
qstring *errbuf) const
|
||||
{
|
||||
qstring soname;
|
||||
for ( const auto &path : entry.paths )
|
||||
{
|
||||
out_verb("Trying to find out DT_SONAME from file \"%s\"\n", path.c_str());
|
||||
linput_t *linput = open_linput(path.c_str(), /*remote=*/ false);
|
||||
linput_janitor_t lj(linput);
|
||||
reader_t reader(linput);
|
||||
dynamic_info_t dyninfo;
|
||||
reader_t::dyninfo_tags_t dyninfo_tags;
|
||||
qstring nonfatal_errbuf;
|
||||
if ( read_ident_and_header_and_get_dyninfo(
|
||||
&dyninfo,
|
||||
&dyninfo_tags,
|
||||
reader,
|
||||
&nonfatal_errbuf) )
|
||||
{
|
||||
for ( const auto &dyn : dyninfo_tags )
|
||||
{
|
||||
if ( dyn.d_tag == DT_SONAME )
|
||||
{
|
||||
soname = dyninfo.d_un_str(reader, dyn.d_tag, dyn.d_un);
|
||||
out_verb("Found DT_SONAME: \"%s\"\n", soname.c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out_verb("%s: %s", path.c_str(), nonfatal_errbuf.c_str());
|
||||
}
|
||||
if ( !soname.empty() )
|
||||
break;
|
||||
}
|
||||
if ( soname.empty() )
|
||||
{
|
||||
*errbuf = "No SONAME found";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now, do patch
|
||||
struct ida_local patcher_t : public file_visitor_t
|
||||
{
|
||||
const qstring &lsoname;
|
||||
qstring *lerrbuf;
|
||||
|
||||
patcher_t(const qstring &_soname, qstring *_errbuf)
|
||||
: lsoname(_soname), lerrbuf(_errbuf) {}
|
||||
|
||||
virtual int visit_file(const char *path) override
|
||||
{
|
||||
return patch_dt_needed(path, lsoname, lerrbuf) ? 0 : -1;
|
||||
}
|
||||
};
|
||||
patcher_t patcher(soname, errbuf);
|
||||
return for_all_plugin_files(patcher, patcher.lerrbuf) == 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static int run_command(const char *_cmd, qstring *errbuf)
|
||||
{
|
||||
qstring cmd(_cmd);
|
||||
if ( args.dry_run )
|
||||
cmd.insert("echo ");
|
||||
int rc = -1;
|
||||
out_verb("Running: \"%s\"\n", cmd.c_str());
|
||||
FILE *fp = popen(cmd.c_str(), "r");
|
||||
if ( fp != nullptr )
|
||||
{
|
||||
char outbuf[MAXSTR];
|
||||
/*ssize_t nread =*/ qfread(fp, outbuf, sizeof(outbuf));
|
||||
rc = pclose(fp);
|
||||
if ( rc != 0 )
|
||||
errbuf->sprnt("Error calling \"%s\"; output is: %s", cmd.c_str(), outbuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
errbuf->sprnt("Command \"%s\" couldn't be run", cmd.c_str());
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
#define SLOT_SIZE 64
|
||||
static bool split_debug_expand_libpython3_dtneeded_room(
|
||||
const char *path,
|
||||
qstring *errbuf)
|
||||
{
|
||||
qstring cmdline;
|
||||
char dt_needed[MAXSTR];
|
||||
{
|
||||
qstring debug_path(path);
|
||||
debug_path.append(".debug");
|
||||
|
||||
char path_dir[QMAXPATH];
|
||||
if ( !qdirname(path_dir, sizeof(path_dir), path) )
|
||||
{
|
||||
errbuf->sprnt("Cannot obtain directory name for path \"%s\"", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
char cwd[QMAXPATH];
|
||||
qgetcwd(cwd, sizeof(cwd));
|
||||
|
||||
if ( qchdir(path_dir) == 0 )
|
||||
{
|
||||
out_verb("Changed directory to: \"%s\"\n", path_dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
errbuf->sprnt("Cannot chdir to \"%s\"", path_dir);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
cmdline.sprnt("objcopy --only-keep-debug %s %s", qbasename(path), qbasename(debug_path.c_str()));
|
||||
if ( run_command(cmdline.c_str(), errbuf) != 0 )
|
||||
return false;
|
||||
|
||||
cmdline.sprnt("strip -s -x %s", qbasename(path));
|
||||
if ( run_command(cmdline.c_str(), errbuf) != 0 )
|
||||
return false;
|
||||
|
||||
cmdline.sprnt("objcopy --add-gnu-debuglink=%s %s", qbasename(debug_path.c_str()), qbasename(path));
|
||||
if ( run_command(cmdline.c_str(), errbuf) != 0 )
|
||||
return false;
|
||||
|
||||
if ( qchdir(cwd) == 0 )
|
||||
{
|
||||
out_verb("Back to directory: \"%s\"\n", cwd);
|
||||
}
|
||||
else
|
||||
{
|
||||
errbuf->sprnt("Cannot chdir back to \"%s\"", cwd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
linput_t *linput = open_linput(path, /*remote=*/ false);
|
||||
if ( linput == nullptr )
|
||||
{
|
||||
errbuf->sprnt("File not found: %s", path);
|
||||
return false;
|
||||
}
|
||||
linput_janitor_t lj(linput);
|
||||
reader_t reader(linput);
|
||||
dynamic_info_t dyninfo;
|
||||
reader_t::dyninfo_tags_t dyninfo_tags;
|
||||
if ( !read_ident_and_header_and_get_dyninfo(
|
||||
&dyninfo,
|
||||
&dyninfo_tags,
|
||||
reader,
|
||||
errbuf) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qoff64_t dt_needed_off;
|
||||
if ( !find_libpython_dt_needed_info(
|
||||
dt_needed,
|
||||
&dt_needed_off,
|
||||
dyninfo_tags,
|
||||
reader,
|
||||
path,
|
||||
errbuf) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
qstring replacement(dt_needed);
|
||||
replacement.resize(SLOT_SIZE, '_');
|
||||
|
||||
// call patchelf to replace the DT_NEEDED with a padded one
|
||||
out_verb("Found DT_NEEDED: \"%s\"; replacing with \"%s\"\n",
|
||||
dt_needed, replacement.c_str());
|
||||
|
||||
cmdline.sprnt("patchelf --replace-needed %s %s %s",
|
||||
dt_needed,
|
||||
replacement.c_str(),
|
||||
path);
|
||||
if ( run_command(cmdline.c_str(), errbuf) == 0 )
|
||||
{
|
||||
out_verb("\"%s\" command successful. Restoring the "
|
||||
"original DT_NEEDED of \"%s\"\n",
|
||||
cmdline.c_str(), dt_needed);
|
||||
if ( !patch_dt_needed(path, dt_needed, errbuf) )
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,522 +0,0 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define BUILD_IDAPYSWITCH
|
||||
#include "../../ldr/ar/ar.hpp"
|
||||
#include "../../ldr/ar/aixar.hpp"
|
||||
#include "../../ldr/ar/arcmn.cpp" // for is_ar_file
|
||||
#include "../../ldr/mach-o/common.cpp"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static void get_python_version(pylib_version_t *out, uint32 mask)
|
||||
{
|
||||
out->revision = uint8(mask);
|
||||
out->minor = uint8(mask >> 8);
|
||||
out->major = uint8(mask >> 16);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static uint32 get_python_version_mask(const pylib_version_t &version)
|
||||
{
|
||||
return version.revision
|
||||
| version.minor << 8
|
||||
| version.major << 16;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool get_pylib_entry_for_macho(
|
||||
pylib_entry_t *entry,
|
||||
const char *path,
|
||||
qstring *errbuf)
|
||||
{
|
||||
linput_t *li = open_linput(path, false);
|
||||
if ( li == nullptr )
|
||||
{
|
||||
errbuf->sprnt("Failed to open file: %s", winerr(errno));
|
||||
return false;
|
||||
}
|
||||
linput_janitor_t lij(li);
|
||||
|
||||
macho_file_t mfile(li);
|
||||
if ( !mfile.parse_header() )
|
||||
{
|
||||
errbuf->sprnt("Failed to parse Mach-O header");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t n = 0;
|
||||
size_t nfat = mfile.get_fat_subfiles();
|
||||
if ( nfat == 0 )
|
||||
{
|
||||
if ( mfile.get_subfile_type(0) != macho_file_t::SUBFILE_MACH_64 )
|
||||
{
|
||||
errbuf->sprnt("File is not 64-bit Mach-O");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool found_x64 = false;
|
||||
for ( size_t i = 0; i < nfat; i++ )
|
||||
{
|
||||
if ( mfile.get_subfile_type(i) == macho_file_t::SUBFILE_MACH_64 )
|
||||
{
|
||||
found_x64 = true;
|
||||
n = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found_x64 )
|
||||
{
|
||||
errbuf->sprnt("No 64-bit arch found in FAT header");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !mfile.set_subfile(n) )
|
||||
{
|
||||
errbuf->sprnt("Failed to parse load commands");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ida_local lcid_finder_t : public macho_lc_visitor_t
|
||||
{
|
||||
pylib_entry_t *entry;
|
||||
lcid_finder_t(pylib_entry_t *_entry) : entry(_entry) {}
|
||||
virtual int visit_dylib(
|
||||
const struct dylib_command *dl,
|
||||
const char *begin,
|
||||
const char *end) override
|
||||
{
|
||||
if ( dl->cmd == LC_ID_DYLIB )
|
||||
{
|
||||
get_python_version(&entry->version, dl->dylib.current_version);
|
||||
get_python_version(&entry->compatibility_version, dl->dylib.compatibility_version);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
lcid_finder_t finder(entry);
|
||||
if ( !mfile.visit_load_commands(finder) )
|
||||
{
|
||||
errbuf->sprnt("failed to determine libpython version: LC_ID_DYLIB not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( entry->version.major != args.major_version )
|
||||
{
|
||||
qstring verbuf;
|
||||
errbuf->sprnt("unsupported python version %s", entry->version.str(&verbuf));
|
||||
return false;
|
||||
}
|
||||
|
||||
entry->paths.push_back(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static int extract_pylib_bin(pylib_entries_t *result, const char *version_dir)
|
||||
{
|
||||
struct ida_local pylib_finder_t : public file_visitor_t
|
||||
{
|
||||
pylib_entries_t *result;
|
||||
pylib_finder_t(pylib_entries_t *_result) : result(_result) {}
|
||||
virtual int visit_file(const char *_binpath) override
|
||||
{
|
||||
// macOS is absurdly dependent on symlinks. remove them to limit noise.
|
||||
char buf[PATH_MAX];
|
||||
const char *binpath = realpath(_binpath, buf);
|
||||
if ( binpath == nullptr )
|
||||
{
|
||||
out_verb("Skipping %s: realpath() failed: %s\n", _binpath, winerr(errno));
|
||||
return 0;
|
||||
}
|
||||
if ( result->path_history.find(binpath) != result->path_history.end() )
|
||||
{
|
||||
out_verb("Skipping %s: duplicate of %s\n", _binpath, binpath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
result->path_history.push_back(binpath);
|
||||
|
||||
qstring errbuf;
|
||||
pylib_version_t dummy;
|
||||
pylib_entry_t entry(dummy);
|
||||
|
||||
if ( !get_pylib_entry_for_macho(&entry, binpath, &errbuf) )
|
||||
{
|
||||
out_verb("Skipping %s: %s\n", binpath, errbuf.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
qstring verbuf;
|
||||
out_verb("Found: \"%s\" (version: %s)\n", binpath, entry.version.str(&verbuf));
|
||||
result->entries.add_unique(entry);
|
||||
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
// the name of the Framework binary can vary. just be safe and examine all files.
|
||||
pylib_finder_t f(result);
|
||||
return visit_files(f, version_dir, "*");
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static void extract_pylib_versions(pylib_entries_t *result, const char *framework)
|
||||
{
|
||||
struct ida_local version_visitor_t : public file_visitor_t
|
||||
{
|
||||
pylib_entries_t *result;
|
||||
version_visitor_t(pylib_entries_t *_result) : result(_result) {}
|
||||
virtual int visit_file(const char *version_dir) override
|
||||
{
|
||||
extract_pylib_bin(result, version_dir);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// examine all Python versions in the Framework
|
||||
version_visitor_t v(result);
|
||||
char versions[QMAXPATH];
|
||||
qmakepath(versions, sizeof(versions), framework, "Versions", nullptr);
|
||||
visit_files(v, versions, "*", FA_DIREC);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void pyver_tool_t::do_find_python_libs(pylib_entries_t *result) const
|
||||
{
|
||||
// find all instances of Python.framework on the system
|
||||
static const char *system_fwks[] =
|
||||
{
|
||||
"/Library/Frameworks",
|
||||
"/System/Library/Frameworks",
|
||||
"/Library/Developer/CommandLineTools/Library/Frameworks",
|
||||
"/Applications/Xcode.app/Contents/Developer/Library/Frameworks",
|
||||
"/opt/local/Library/Frameworks",
|
||||
};
|
||||
|
||||
qstrvec_t framework_dirs;
|
||||
for ( size_t i = 0; i < qnumber(system_fwks); i++ )
|
||||
framework_dirs.push_back(system_fwks[i]);
|
||||
|
||||
struct ida_local homebrew_handler_t : public file_visitor_t
|
||||
{
|
||||
qstrvec_t *framework_dirs;
|
||||
homebrew_handler_t(qstrvec_t *_framework_dirs) : framework_dirs(_framework_dirs) {}
|
||||
virtual int visit_file(const char *path) override
|
||||
{
|
||||
framework_dirs->push_back(qstring(path) + "/Frameworks");
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// homebrew keeps python installations in /usr/local/opt/python@X.X/Frameworks
|
||||
homebrew_handler_t hh(&framework_dirs);
|
||||
visit_files(hh, "/usr/local/opt", "python*", FA_DIREC);
|
||||
|
||||
struct ida_local python_framework_finder_t : public file_visitor_t
|
||||
{
|
||||
pylib_entries_t *result;
|
||||
python_framework_finder_t(pylib_entries_t *_result) : result(_result) {}
|
||||
virtual int visit_file(const char *framework) override
|
||||
{
|
||||
extract_pylib_versions(result, framework);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// check for a PythonX.framework in each framework dir
|
||||
python_framework_finder_t pff(result);
|
||||
for ( size_t i = 0, n = framework_dirs.size(); i < n; i++ )
|
||||
visit_files(pff, framework_dirs[i].c_str(), "Python*.framework", FA_DIREC);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
bool pyver_tool_t::do_path_to_pylib_entry(
|
||||
pylib_entry_t *entry,
|
||||
const char *path,
|
||||
qstring *errbuf) const
|
||||
{
|
||||
return get_pylib_entry_for_macho(entry, path, errbuf);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// misc information required to patch the load command for libpython
|
||||
struct python_lc_info_t
|
||||
{
|
||||
uint32 off; // offset of libpython load command
|
||||
uint32 size; // size of libpython load command
|
||||
qstring path; // libpython path
|
||||
pylib_version_t version; // libpython version
|
||||
bytevec_t header; // header data: mach_header + all load commands
|
||||
uint32 headerpadsz; // size of header, including all padded bytes
|
||||
python_lc_info_t(void) : off(0), size(0), headerpadsz(UINT_MAX) {}
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool get_python_lc_info(python_lc_info_t *plc, const char *path, qstring *errbuf)
|
||||
{
|
||||
linput_t *li = open_linput(path, false);
|
||||
if ( li == nullptr )
|
||||
{
|
||||
errbuf->sprnt("Failed to open file: %s", winerr(errno));
|
||||
return false;
|
||||
}
|
||||
linput_janitor_t lij(li);
|
||||
|
||||
// here we are assuming the target binary was built by us, which means it is a non-fat,
|
||||
// 64-bit Mach-O file that links against a PythonX.X framework, and has its header padded
|
||||
// so that we can patch the load commands without issue.
|
||||
macho_file_t mfile(li);
|
||||
if ( !mfile.parse_header() )
|
||||
{
|
||||
errbuf->sprnt("Failed to parse Mach-O header");
|
||||
return false;
|
||||
}
|
||||
if ( mfile.get_fat_subfiles() > 0 || mfile.get_subfile_type(0) != macho_file_t::SUBFILE_MACH_64 )
|
||||
{
|
||||
errbuf->sprnt("Unexpected filetype (expected 64-bit Mach-O)");
|
||||
return false;
|
||||
}
|
||||
if ( !mfile.set_subfile(0) )
|
||||
{
|
||||
errbuf->sprnt("Failed to parse load commands");
|
||||
return false;
|
||||
}
|
||||
|
||||
const secvec_t §s = mfile.get_sections();
|
||||
|
||||
// find the section with the smallest fileoff. it will tell us the size of the padded header.
|
||||
for ( size_t i = 0, nsects = sects.size(); i < nsects; i++ )
|
||||
{
|
||||
const section_64 &s = sects[i];
|
||||
|
||||
if ( s.size != 0
|
||||
&& (s.flags & S_ZEROFILL) == 0
|
||||
&& (s.flags & S_THREAD_LOCAL_ZEROFILL) == 0
|
||||
&& s.offset < plc->headerpadsz )
|
||||
{
|
||||
plc->headerpadsz = s.offset;
|
||||
}
|
||||
}
|
||||
|
||||
if ( plc->headerpadsz == UINT_MAX )
|
||||
{
|
||||
errbuf->sprnt("Failed to determine padded size of the Mach-O header");
|
||||
return false;
|
||||
}
|
||||
|
||||
// extract the libpython load command
|
||||
struct ida_local python_lc_finder_t : public macho_lc_visitor_t
|
||||
{
|
||||
python_lc_info_t *plc;
|
||||
python_lc_finder_t(python_lc_info_t *_plc) : plc(_plc)
|
||||
{
|
||||
plc->off = sizeof(mach_header_64);
|
||||
}
|
||||
virtual int visit_any_load_command(
|
||||
const struct load_command *lc,
|
||||
const char *begin,
|
||||
const char *end) override
|
||||
{
|
||||
if ( lc->cmd == LC_LOAD_DYLIB )
|
||||
{
|
||||
const struct dylib_command *dl = (const struct dylib_command *)begin;
|
||||
const char *p = begin + dl->dylib.name.offset;
|
||||
if ( p < end )
|
||||
{
|
||||
qstring _path = qstring(p, end-p);
|
||||
const char *basename = qbasename(_path.c_str());
|
||||
if ( strneq(basename, "Python", 6) || strneq(basename, "libpython", 9) )
|
||||
{
|
||||
plc->path = _path;
|
||||
plc->size = dl->cmdsize;
|
||||
get_python_version(&plc->version, dl->dylib.current_version);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// not libpython, keep looking
|
||||
plc->off += lc->cmdsize;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
python_lc_finder_t finder(plc);
|
||||
if ( !mfile.visit_load_commands(finder) )
|
||||
{
|
||||
errbuf->sprnt("No libpython dependency found");
|
||||
return false;
|
||||
}
|
||||
|
||||
mfile.get_mach_header_data(&plc->header);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
typedef janitor_t<int> fd_janitor_t;
|
||||
template <> inline fd_janitor_t::~janitor_t()
|
||||
{
|
||||
qclose(resource);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool do_patch(
|
||||
void *map,
|
||||
const pylib_entry_t &entry,
|
||||
const python_lc_info_t &plc,
|
||||
qstring *errbuf)
|
||||
{
|
||||
const char *path = entry.paths[0].c_str();
|
||||
size_t path_len = entry.paths[0].length();
|
||||
|
||||
// validate the size of the new load command
|
||||
mach_header_64 *mheader = (mach_header_64 *)map;
|
||||
uint32 sizeofcmds = mheader->sizeofcmds;
|
||||
uint32 newcmdsize = align_up(sizeof(dylib_command) + path_len + 1, 8);
|
||||
|
||||
int32 diff = newcmdsize - plc.size;
|
||||
sizeofcmds += diff;
|
||||
|
||||
if ( sizeofcmds + sizeof(mach_header_64) > plc.headerpadsz )
|
||||
{
|
||||
errbuf->sprnt("Updated load commands do not fit in the Mach-O header");
|
||||
return false;
|
||||
}
|
||||
|
||||
// patch the mach header
|
||||
mheader->sizeofcmds = sizeofcmds;
|
||||
|
||||
// patch the libpython load command
|
||||
dylib_command *cmd = (dylib_command *)((uchar *)map + plc.off);
|
||||
cmd->cmdsize = newcmdsize;
|
||||
cmd->dylib.current_version = get_python_version_mask(entry.version);
|
||||
cmd->dylib.compatibility_version = get_python_version_mask(entry.compatibility_version);
|
||||
|
||||
// write the new libpython path
|
||||
uchar *ptr = (uchar *)cmd + sizeof(dylib_command);
|
||||
memcpy(ptr, path, path_len);
|
||||
ptr += path_len;
|
||||
size_t npad = newcmdsize - (sizeof(dylib_command) + path_len);
|
||||
memset(ptr, 0, npad);
|
||||
ptr += npad;
|
||||
|
||||
// copy the original load commands after libpython
|
||||
const uchar *org = plc.header.begin() + plc.off + plc.size;
|
||||
const uchar *end = plc.header.end();
|
||||
size_t norg = end - org;
|
||||
memcpy(ptr, org, norg);
|
||||
ptr += norg;
|
||||
|
||||
// if the new sizeofcmds is smaller, fill excess space with 0s
|
||||
if ( diff < 0 )
|
||||
memset(ptr, 0, -diff);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool patch_python_dylib_cmd(
|
||||
const char *path,
|
||||
const pylib_entry_t &entry,
|
||||
qstring *errbuf)
|
||||
{
|
||||
if ( args.dry_run )
|
||||
{
|
||||
out("Would patch: %s\n", path);
|
||||
return true;
|
||||
}
|
||||
|
||||
out_verb("Patching: %s\n", path);
|
||||
|
||||
python_lc_info_t plc;
|
||||
if ( !get_python_lc_info(&plc, path, errbuf) )
|
||||
return false;
|
||||
|
||||
// be careful that we're not patching the wrong build of idapython
|
||||
if ( entry.version.major != plc.version.major )
|
||||
{
|
||||
errbuf->sprnt("idapython binary %s was not built against python %d", path, entry.version.major);
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = qopen(path, O_RDWR);
|
||||
if ( fd < 0 )
|
||||
{
|
||||
errbuf->sprnt("Failed to open file: %s", winerr(errno));
|
||||
return false;
|
||||
}
|
||||
fd_janitor_t fdj(fd);
|
||||
|
||||
qstatbuf sbuf;
|
||||
memset(&sbuf, 0, sizeof(sbuf));
|
||||
if ( qfstat(fd, &sbuf) != 0 )
|
||||
{
|
||||
errbuf->sprnt("fstat() failed: %s", winerr(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
void *map = mmap(0, sbuf.qst_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if ( map == MAP_FAILED )
|
||||
{
|
||||
errbuf->sprnt("mmap() failed: %s", winerr(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = do_patch(map, entry, plc, errbuf);
|
||||
|
||||
munmap(map, sbuf.qst_size);
|
||||
return ok;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
bool pyver_tool_t::do_apply_version(
|
||||
const pylib_entry_t &entry,
|
||||
qstring *errbuf) const
|
||||
{
|
||||
#ifdef __APPLE_SILICON__
|
||||
// modify the libpython symlink in idabin so that it points to the given libpython path
|
||||
qstring link_name;
|
||||
link_name.sprnt("libpython%d.link.dylib", entry.version.major);
|
||||
|
||||
char link_path[QMAXPATH];
|
||||
qmakepath(link_path, sizeof(link_path), idadir(""), link_name.c_str(), nullptr);
|
||||
if ( qfileexist(link_path) )
|
||||
{
|
||||
out_verb("Removing existing \"%s\"\n", link_path);
|
||||
int rc = qunlink(link_path);
|
||||
if ( rc != 0 )
|
||||
{
|
||||
errbuf->sprnt("Unlinking \"%s\" failed: %s", link_path, winerr(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char *target = entry.paths[0].c_str();
|
||||
out_verb("Linking \"%s\" -> \"%s\"\n", link_path, target);
|
||||
int rc = symlink(target, link_path);
|
||||
if ( rc != 0 )
|
||||
{
|
||||
errbuf->sprnt("Linking to \"%s\" failed: %s", target, winerr(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
// patch the libpython load commands in all idapython modules
|
||||
struct ida_local patcher_t : public file_visitor_t
|
||||
{
|
||||
const pylib_entry_t &entry;
|
||||
qstring *lerrbuf;
|
||||
patcher_t(const pylib_entry_t &_entry, qstring *_errbuf) : entry(_entry), lerrbuf(_errbuf) {}
|
||||
virtual int visit_file(const char *path) override
|
||||
{
|
||||
return patch_python_dylib_cmd(path, entry, lerrbuf) ? 0 : -1;
|
||||
}
|
||||
};
|
||||
patcher_t patcher(entry, errbuf);
|
||||
return for_all_plugin_files(patcher, patcher.lerrbuf) == 0;
|
||||
#endif
|
||||
}
|
||||
@@ -1,672 +0,0 @@
|
||||
|
||||
#define PYTHON3_DLL "python3.dll"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
#define IDA_HKEY HKEY_CURRENT_USER
|
||||
#define IDA_ADDLIB_SUBKEY L"Software\\Hex-Rays\\IDA"
|
||||
#define IDA_ADDLIB_VALUE L"Python3TargetDLL"
|
||||
|
||||
#define PYTHON_INSTALLS_KEY L"Software\\Python"
|
||||
#define PYTHON_INSTALL_PATH_SUBKEY L"InstallPath"
|
||||
#define PYTHON_DISPLAY_NAME_SUBKEY L"DisplayName"
|
||||
#define PYTHON_SYSVER_SUBKEY L"SysVersion"
|
||||
#define PYTHON_SYSARCH_SUBKEY L"SysArchitecture"
|
||||
#define PYTHON_INSTALL_PATH_DEFAULT_VALUE L""
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool open_ida_addlib_subkey(HKEY *out, REGSAM samDesired)
|
||||
{
|
||||
DWORD err = RegOpenKeyExW(IDA_HKEY, IDA_ADDLIB_SUBKEY, 0, samDesired, out);
|
||||
if ( err == ERROR_SUCCESS )
|
||||
return true;
|
||||
if ( samDesired == KEY_READ )
|
||||
return false;
|
||||
// opening for write failed; create the subkeys
|
||||
err = RegCreateKeyExW(IDA_HKEY, IDA_ADDLIB_SUBKEY, 0, NULL, REG_OPTION_NON_VOLATILE, samDesired, NULL, out, NULL);
|
||||
return err == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool read_string(
|
||||
qstring *out,
|
||||
HKEY key,
|
||||
const wchar16_t *value,
|
||||
qstring *errbuf=nullptr)
|
||||
{
|
||||
DWORD type;
|
||||
DWORD size;
|
||||
if ( RegQueryValueExW(key, value, nullptr, &type, nullptr, &size) == ERROR_SUCCESS && type == REG_SZ )
|
||||
{
|
||||
bytevec_t buf;
|
||||
buf.resize(size);
|
||||
if ( RegQueryValueExW(key, value, nullptr, &type, buf.begin(), &size) == ERROR_SUCCESS && type == REG_SZ )
|
||||
return utf16_utf8(out, (wchar16_t *) buf.begin(), buf.size() / sizeof(wchar16_t));
|
||||
}
|
||||
if ( errbuf != nullptr )
|
||||
errbuf->sprnt("Couldn't query value \"%ls\"\n", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool write_string(
|
||||
HKEY key,
|
||||
const wchar16_t *value,
|
||||
const char *str,
|
||||
qstring *errbuf=nullptr)
|
||||
{
|
||||
qwstring wstr;
|
||||
bool ok = utf8_utf16(&wstr, str)
|
||||
&& RegSetValueExW(key, value, 0, REG_SZ,
|
||||
(LPBYTE) wstr.c_str(),
|
||||
wstr.length() * sizeof(wstr[0])) == ERROR_SUCCESS;
|
||||
if ( !ok )
|
||||
errbuf->sprnt("Couldn't write string data \"%s\" to value \"%ls\"", str, value);
|
||||
return ok;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool extract_version_from_path(
|
||||
pylib_version_t *out,
|
||||
const char *_path)
|
||||
{
|
||||
qstring qpath(_path);
|
||||
qpath.rtrim('\\');
|
||||
const char *path = qpath.c_str();
|
||||
const char *p = qbasename(path);
|
||||
if ( p == nullptr )
|
||||
return false;
|
||||
if ( !strnieq(p, "Python", 6) )
|
||||
return false;
|
||||
p += 6;
|
||||
if ( !qisdigit(p[0]) || !qisdigit(p[1]) )
|
||||
return false;
|
||||
out->raw = p;
|
||||
int major = p[0] - '0';
|
||||
int minor = p[1] - '0';
|
||||
int revision = 0;
|
||||
p += 2;
|
||||
if ( qisdigit(p[0]) )
|
||||
{
|
||||
revision = p[0] - '0';
|
||||
++p;
|
||||
}
|
||||
out->major = major;
|
||||
out->minor = minor;
|
||||
out->revision = revision;
|
||||
out->modifiers = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#pragma comment(lib, "version.lib")
|
||||
//-------------------------------------------------------------------------
|
||||
DWORD GetFileVersionNumber(const char *filename, DWORD *pdwMSVer, DWORD *pdwLSVer)
|
||||
{
|
||||
DWORD dwResult = NOERROR;
|
||||
unsigned uiSize;
|
||||
DWORD dwVerInfoSize;
|
||||
DWORD dwHandle;
|
||||
PBYTE prgbVersionInfo = NULL;
|
||||
VS_FIXEDFILEINFO *lpVSFixedFileInfo = NULL;
|
||||
|
||||
DWORD dwMSVer = 0xffffffff;
|
||||
DWORD dwLSVer = 0xffffffff;
|
||||
|
||||
qwstring wfilename;
|
||||
if ( !utf8_utf16(&wfilename, filename) )
|
||||
{
|
||||
dwResult = ERROR_INVALID_PARAMETER;
|
||||
goto Finish;
|
||||
}
|
||||
|
||||
dwVerInfoSize = GetFileVersionInfoSizeW(wfilename.c_str(), &dwHandle);
|
||||
if ( dwVerInfoSize != 0 )
|
||||
{
|
||||
prgbVersionInfo = (PBYTE)qalloc(dwVerInfoSize);
|
||||
if ( prgbVersionInfo == NULL )
|
||||
{
|
||||
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
||||
goto Finish;
|
||||
}
|
||||
|
||||
// Read version stamping info
|
||||
if ( GetFileVersionInfoW(wfilename.c_str(), dwHandle, dwVerInfoSize, prgbVersionInfo) )
|
||||
{
|
||||
// get the value for VS_FIXEDFILEINFO
|
||||
if ( VerQueryValueW(prgbVersionInfo, L"\\", (LPVOID*)&lpVSFixedFileInfo, &uiSize) && (uiSize != 0) )
|
||||
{
|
||||
dwMSVer = lpVSFixedFileInfo->dwFileVersionMS;
|
||||
dwLSVer = lpVSFixedFileInfo->dwFileVersionLS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dwResult = GetLastError();
|
||||
goto Finish;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dwResult = GetLastError();
|
||||
}
|
||||
|
||||
out_verb("%s is version %d.%d.%d.%d\n", filename, HIWORD(dwMSVer), LOWORD(dwMSVer), HIWORD(dwLSVer), LOWORD(dwLSVer));
|
||||
|
||||
Finish:
|
||||
if ( prgbVersionInfo != NULL )
|
||||
qfree(prgbVersionInfo);
|
||||
if ( pdwMSVer != NULL )
|
||||
*pdwMSVer = dwMSVer;
|
||||
if ( pdwLSVer != NULL )
|
||||
*pdwLSVer = dwLSVer;
|
||||
|
||||
return dwResult;
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
static bool extract_version_from_dll(
|
||||
pylib_version_t *out,
|
||||
const char *fname)
|
||||
{
|
||||
DWORD dwMSVer, dwLSVer;
|
||||
DWORD res = GetFileVersionNumber(fname, &dwMSVer, &dwLSVer);
|
||||
if ( res == NOERROR )
|
||||
{
|
||||
out->raw.sprnt("%d.%d.%d.%d", HIWORD(dwMSVer), LOWORD(dwMSVer), HIWORD(dwLSVer), LOWORD(dwLSVer));
|
||||
// Python DLL versions look like 3.7.4150.1013 -> 3.7.4
|
||||
out->major = HIWORD(dwMSVer);
|
||||
out->minor = LOWORD(dwMSVer);
|
||||
out->revision = HIWORD(dwLSVer) / 1000;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_verb("error getting version of \"%s\": %s\n", fname, winerr(res));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool is_python3Y_dll_file_name(const char *fname)
|
||||
{
|
||||
return fname != nullptr
|
||||
&& strnieq(fname, "python3", 7)
|
||||
&& qisdigit(fname[7])
|
||||
&& strieq(get_file_ext(fname), "dll");
|
||||
}
|
||||
|
||||
#include <exehdr.h>
|
||||
|
||||
#include "../../ldr/pe/pe.h"
|
||||
#include "../../ldr/pe/common.cpp"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool check_dll_x86_64(const char *path)
|
||||
{
|
||||
linput_t *linput = open_linput(path, /*remote=*/ false);
|
||||
if ( linput == nullptr )
|
||||
return false;
|
||||
linput_janitor_t lj(linput);
|
||||
pe_loader_t pl;
|
||||
return pl.read_header(linput, /*silent=*/ true)
|
||||
&& pl.pe.machine == PECPU_AMD64;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static bool probe_python_install_dir_from_dll_path(
|
||||
qstrvec_t *out_paths,
|
||||
pylib_version_t *out_version,
|
||||
const char *path,
|
||||
qstring *errbuf)
|
||||
{
|
||||
char dir[QMAXPATH];
|
||||
if ( !qdirname(dir, sizeof(dir), path) )
|
||||
{
|
||||
errbuf->sprnt("Couldn't retrieve directory name from \"%s\"", path);
|
||||
return false;
|
||||
}
|
||||
extract_version_from_path(out_version, dir); // not fatal if this fails
|
||||
|
||||
qstring found_python3_dll;
|
||||
qstring found_python3Y_dll;
|
||||
static const char dll_pattern[] = "python3*.dll";
|
||||
|
||||
char pattern_path[QMAXPATH];
|
||||
qmakepath(pattern_path, sizeof(pattern_path), dir, dll_pattern, nullptr);
|
||||
qstring verbuf;
|
||||
qffblk64_t fb;
|
||||
for ( int code = qfindfirst(pattern_path, &fb, 0);
|
||||
code == 0;
|
||||
code = qfindnext(&fb) )
|
||||
{
|
||||
const char *ext = get_file_ext(fb.ff_name);
|
||||
if ( ext != nullptr && strieq(ext, "dll") )
|
||||
{
|
||||
char dll_path[QMAXPATH];
|
||||
qmakepath(dll_path, sizeof(dll_path), dir, fb.ff_name, nullptr);
|
||||
pylib_version_t tmp;
|
||||
if ( extract_version_from_dll(&tmp, path) )
|
||||
*out_version = tmp;
|
||||
out_verb("Found: \"%s\" (version: %s)\n", dll_path, out_version->str(&verbuf));
|
||||
out_paths->push_back(dll_path);
|
||||
|
||||
if ( found_python3_dll.empty() && strieq(fb.ff_name, PYTHON3_DLL) )
|
||||
found_python3_dll = dll_path;
|
||||
if ( found_python3Y_dll.empty() && is_python3Y_dll_file_name(fb.ff_name) )
|
||||
found_python3Y_dll = dll_path;
|
||||
}
|
||||
}
|
||||
|
||||
if ( found_python3_dll.empty() )
|
||||
{
|
||||
errbuf->sprnt("No \"" PYTHON3_DLL "\" file found in directory \"%s\"", dir);
|
||||
return false;
|
||||
}
|
||||
if ( found_python3Y_dll.empty() )
|
||||
{
|
||||
errbuf->sprnt("No \"python3[0-9].dll\" file found in directory \"%s\"", dir);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !check_dll_x86_64(found_python3_dll.c_str())
|
||||
|| !check_dll_x86_64(found_python3Y_dll.c_str()) )
|
||||
{
|
||||
errbuf->sprnt("DLLs in directory \"%s\" do not have the x86_64 architecture", dir);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool has_appx_path(qstrvec_t paths)
|
||||
{
|
||||
static qstring appx_path;
|
||||
if ( appx_path.empty() )
|
||||
{
|
||||
HKEY hkey;
|
||||
bool ok = false;
|
||||
if ( RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Appx", 0, KEY_READ, &hkey) == ERROR_SUCCESS )
|
||||
{
|
||||
ok = read_string(&appx_path, hkey, L"PackageRoot");
|
||||
RegCloseKey(hkey);
|
||||
}
|
||||
if ( !ok )
|
||||
{
|
||||
// no Appx support, set to dummy value which doesn't occur in paths
|
||||
appx_path = "<none>";
|
||||
}
|
||||
}
|
||||
for ( const qstring &path : paths )
|
||||
if ( path.find(appx_path) != qstring::npos )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// ignore known bad Pythons:
|
||||
// 3.8.0 release (https://bugs.python.org/issue37633)
|
||||
// Anaconda 2019.10, 2020.02 and 2020.11 (https://github.com/ContinuumIO/anaconda-issues/issues/11374)
|
||||
// AppStore Python on Windows 10 (dll can't be loaded from outside of Appx package)
|
||||
static bool bad_entry(const pylib_entry_t &e)
|
||||
{
|
||||
if ( e.version.major == 3
|
||||
&& e.version.minor == 8
|
||||
&& e.version.revision == 0 )
|
||||
{
|
||||
out("Ignoring unusable Python 3.8.0 \"%s\"\n", !e.paths.empty() ? e.paths[0].c_str() : "?");
|
||||
return true;
|
||||
}
|
||||
if ( e.display_name == "Anaconda 2019.10"
|
||||
|| e.display_name == "Anaconda 2020.02"
|
||||
|| e.display_name == "Anaconda 2020.11" )
|
||||
{
|
||||
out("Ignoring unusable %s \"%s\"\n", e.display_name.c_str(), !e.paths.empty() ? e.paths[0].c_str() : "?");
|
||||
return true;
|
||||
}
|
||||
if ( has_appx_path(e.paths) )
|
||||
{
|
||||
out("Ignoring unusable AppStore Python \"%s\"\n", !e.paths.empty() ? e.paths[0].c_str() : "?");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
static void remove_bad_entries(pylib_entries_t *result)
|
||||
{
|
||||
auto p = result->entries.begin();
|
||||
while ( p != result->entries.end() )
|
||||
{
|
||||
if ( bad_entry(*p) )
|
||||
p = result->entries.erase(p);
|
||||
else
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// given a key to a path like HKLM\SOFTWARE\Python\PythonCore,
|
||||
// - enumerate subkeys and their check InstallPath subkey, using the default value as the directory to the installation
|
||||
// e.g.
|
||||
// HKLM\SOFTWARE\Python\PythonCore\3.6\InstallPath -> (Default) = C:\Python36\
|
||||
// - check for python3.dll and python3Y.dll in it and add them to 'result'
|
||||
static void enum_python_key(pylib_entries_t *result, const HKEY hkey, qstring *_errbuf, qstring *_verbuf)
|
||||
{
|
||||
qstring &errbuf = *_errbuf;
|
||||
qstring &verbuf = *_verbuf;
|
||||
int index = 0;
|
||||
WCHAR subkey[MAXSTR];
|
||||
qstring displayname;
|
||||
if ( read_string(&displayname, hkey, PYTHON_DISPLAY_NAME_SUBKEY) )
|
||||
{
|
||||
out("Checking installs from \"%s\"\n", displayname.c_str());
|
||||
}
|
||||
while ( true )
|
||||
{
|
||||
DWORD subkey_sz = qnumber(subkey);
|
||||
if ( RegEnumKeyExW(hkey, index++, subkey, &subkey_sz,
|
||||
nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS )
|
||||
{
|
||||
// no more installs
|
||||
break;
|
||||
}
|
||||
HKEY ihkey;
|
||||
if ( RegOpenKeyExW(hkey, subkey, 0, KEY_READ, &ihkey) == ERROR_SUCCESS )
|
||||
{
|
||||
out_verb("Opened \"%ls\"\n", subkey);
|
||||
// opened an install. get its version from SysVersion value
|
||||
qstring sysver;
|
||||
pylib_version_t version;
|
||||
bool ok = read_string(&sysver, ihkey, PYTHON_SYSVER_SUBKEY);
|
||||
ok = ok && parse_python_version_str(&version, sysver.c_str()) && version.major >= 3;
|
||||
qstring arch;
|
||||
if ( read_string(&arch, ihkey, PYTHON_SYSARCH_SUBKEY) && arch != "64bit" )
|
||||
ok = false;
|
||||
if ( ok )
|
||||
{
|
||||
if ( read_string(&displayname, ihkey, PYTHON_DISPLAY_NAME_SUBKEY) )
|
||||
{
|
||||
out("Checking \"%s\" (%s)\n", displayname.c_str(), sysver.c_str());
|
||||
}
|
||||
HKEY vhkey;
|
||||
if ( RegOpenKeyExW(ihkey, PYTHON_INSTALL_PATH_SUBKEY, 0, KEY_READ, &vhkey) == ERROR_SUCCESS )
|
||||
{
|
||||
qstring install_path;
|
||||
if ( read_string(&install_path, vhkey, PYTHON_INSTALL_PATH_DEFAULT_VALUE, &errbuf) )
|
||||
{
|
||||
char probe[QMAXPATH];
|
||||
qmakepath(probe, sizeof(probe), install_path.c_str(), PYTHON3_DLL, nullptr);
|
||||
pylib_version_t unused;
|
||||
qstrvec_t paths;
|
||||
if ( probe_python_install_dir_from_dll_path(
|
||||
&paths,
|
||||
&version,
|
||||
probe,
|
||||
&errbuf) )
|
||||
{
|
||||
out("Found: \"%s\" (version: %s)\n", install_path.c_str(), version.str(&verbuf));
|
||||
pylib_entry_t &e = result->add_entry(version, paths);
|
||||
e.display_name = displayname;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_verb("Ignoring directory \"%s\": %s\n", install_path.c_str(), errbuf.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out_verb("Couldn't query \"%s\"'s value \"%ls\": %s\n",
|
||||
subkey,
|
||||
PYTHON_INSTALL_PATH_DEFAULT_VALUE,
|
||||
errbuf.c_str());
|
||||
}
|
||||
RegCloseKey(vhkey);
|
||||
}
|
||||
else
|
||||
{
|
||||
out("Couldn't open \"%s\"\n", subkey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out_verb("Not a 64-bit Python 3.x or no version info, skipping\n");
|
||||
}
|
||||
|
||||
RegCloseKey(ihkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
void pyver_tool_t::do_find_python_libs(pylib_entries_t *result) const
|
||||
{
|
||||
qstring errbuf;
|
||||
qstring verbuf;
|
||||
|
||||
//
|
||||
// Enumerate:
|
||||
// * HKEY_LOCAL_MACHINE\Software\Python\PythonCore\* versions
|
||||
// * HKEY_CURRENT_USER\Software\Python\PythonCore\* versions
|
||||
//
|
||||
static const HKEY top_keys[] =
|
||||
{
|
||||
HKEY_LOCAL_MACHINE,
|
||||
HKEY_CURRENT_USER,
|
||||
};
|
||||
|
||||
static const char * knames[] =
|
||||
{
|
||||
"HLKM",
|
||||
"HKCU",
|
||||
};
|
||||
for ( size_t i = 0; i < qnumber(top_keys); ++i )
|
||||
{
|
||||
HKEY hkey_python;
|
||||
out_verb("Searching for subkeys of \"%s\\%ls\"\n", knames[i], PYTHON_INSTALLS_KEY);
|
||||
if ( RegOpenKeyExW(top_keys[i], PYTHON_INSTALLS_KEY, 0, KEY_READ, &hkey_python) == ERROR_SUCCESS )
|
||||
{
|
||||
WCHAR subkey[MAXSTR];
|
||||
int index = 0;
|
||||
while ( true )
|
||||
{
|
||||
DWORD subkey_sz = qnumber(subkey);
|
||||
if ( RegEnumKeyExW(hkey_python, index++, subkey, &subkey_sz,
|
||||
nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS )
|
||||
{
|
||||
break;
|
||||
}
|
||||
HKEY hkey;
|
||||
if ( RegOpenKeyExW(hkey_python, subkey, 0, KEY_READ, &hkey) == ERROR_SUCCESS )
|
||||
{
|
||||
out_verb("Found \"%s\\%ls\\%ls\"\n", knames[i], PYTHON_INSTALLS_KEY, subkey);
|
||||
enum_python_key(result, hkey, &errbuf, &verbuf);
|
||||
RegCloseKey(hkey);
|
||||
}
|
||||
}
|
||||
RegCloseKey(hkey_python);
|
||||
}
|
||||
}
|
||||
|
||||
remove_bad_entries(result);
|
||||
|
||||
//
|
||||
// See if we already have one registered for IDA
|
||||
//
|
||||
{
|
||||
HKEY idahkey;
|
||||
if ( open_ida_addlib_subkey(&idahkey, KEY_READ) )
|
||||
{
|
||||
qstring existing;
|
||||
if ( read_string(&existing, idahkey, IDA_ADDLIB_VALUE) )
|
||||
{
|
||||
out_verb("Previously-used DLL: \"%s\"\n", existing.c_str());
|
||||
pylib_version_t version;
|
||||
qstrvec_t paths;
|
||||
if ( probe_python_install_dir_from_dll_path(
|
||||
&paths,
|
||||
&version,
|
||||
existing.c_str(),
|
||||
&errbuf) )
|
||||
{
|
||||
out("IDA previously used: \"%s\" (guessed version: %s). "
|
||||
"Making this the preferred version.\n",
|
||||
existing.c_str(), version.str(&verbuf));
|
||||
// do we have it in the list?
|
||||
bool found = false;
|
||||
for ( pylib_entry_t &e : result->entries )
|
||||
{
|
||||
if ( e.paths.has(existing) )
|
||||
{
|
||||
found = true;
|
||||
e.preferred = true;
|
||||
}
|
||||
}
|
||||
if ( !found )
|
||||
{
|
||||
// add a new one
|
||||
pylib_entry_t e(version);
|
||||
e.paths.swap(paths);
|
||||
e.preferred = true;
|
||||
result->entries.push_back(e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out_verb("Ignoring directory \"%s\": %s\n",
|
||||
existing.c_str(), errbuf.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out("\"%ls\" exists, but no \"%ls\" value found\n",
|
||||
IDA_ADDLIB_SUBKEY, IDA_ADDLIB_VALUE);
|
||||
}
|
||||
RegCloseKey(idahkey);
|
||||
}
|
||||
else
|
||||
{
|
||||
out("No \"%ls\" key found\n", IDA_ADDLIB_SUBKEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
bool pyver_tool_t::do_path_to_pylib_entry(
|
||||
pylib_entry_t *entry,
|
||||
const char *path,
|
||||
qstring *errbuf) const
|
||||
{
|
||||
return probe_python_install_dir_from_dll_path(&entry->paths, &entry->version, path, errbuf);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
bool pyver_tool_t::do_apply_version(
|
||||
const pylib_entry_t &entry,
|
||||
qstring *errbuf) const
|
||||
{
|
||||
HKEY idahkey;
|
||||
if ( !open_ida_addlib_subkey(&idahkey, KEY_WRITE) )
|
||||
{
|
||||
errbuf->sprnt("Couldn't open \"%ls\" key for writing\n", IDA_ADDLIB_SUBKEY);
|
||||
return false;
|
||||
}
|
||||
|
||||
qstring replacement;
|
||||
bool ok = false;
|
||||
for ( const auto &path : entry.paths )
|
||||
{
|
||||
const char *candidate = qbasename(path.c_str());
|
||||
if ( is_python3Y_dll_file_name(candidate) )
|
||||
{
|
||||
replacement = candidate;
|
||||
ok = write_string(idahkey, IDA_ADDLIB_VALUE, path.c_str(), errbuf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
RegCloseKey(idahkey);
|
||||
if ( !ok )
|
||||
{
|
||||
errbuf->sprnt("Couldn't find a suitable python3Y.dll file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now, let's handle sip.pyd
|
||||
out_verb("Handling sip" PY_MODULE_EXT "\n");
|
||||
char path[QMAXPATH];
|
||||
qmakepath(path, sizeof(path), idadir(""), "python", "3", "PyQt5", "sip" PY_MODULE_EXT, nullptr);
|
||||
linput_t *linput = open_linput(path, /*remote=*/ false);
|
||||
if ( linput == nullptr )
|
||||
{
|
||||
errbuf->sprnt("File not found: %s", path);
|
||||
return false;
|
||||
}
|
||||
linput_janitor_t lj(linput);
|
||||
pe_loader_t pl;
|
||||
if ( !pl.read_header(linput, /*silent=*/ true)
|
||||
|| pl.process_sections(linput) != 0 )
|
||||
{
|
||||
errbuf->sprnt("%s: couldn't read header, or process sections", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
for ( int ni = 0; ; ++ni )
|
||||
{
|
||||
peimpdir_t tmp;
|
||||
off_t off = pl.pe.impdir.rva + ni*sizeof(peimpdir_t);
|
||||
out_verb("Reading import table at %u (0x%x)\n", uint32(off), uint32(off));
|
||||
if ( !pl.vmread(linput, off, &tmp, sizeof(tmp)) )
|
||||
{
|
||||
errbuf->sprnt("%s: failed reading import table", path);
|
||||
}
|
||||
if ( tmp.dllname == 0 || tmp.looktab == 0 )
|
||||
break;
|
||||
|
||||
char dll[MAXSTR];
|
||||
bool ok = true;
|
||||
pl.asciiz(linput, tmp.dllname, dll, sizeof(dll), &ok);
|
||||
if ( !ok )
|
||||
break;
|
||||
out_verb("Import table entry #%d; dll name: \"%s\"\n", ni, dll);
|
||||
if ( is_python3Y_dll_file_name(dll) )
|
||||
{
|
||||
out_verb("Found python3Y.dll: \"%s\" at offset %u (0x%x)\n",
|
||||
dll, tmp.dllname, tmp.dllname);
|
||||
FILE *fp = openM(path);
|
||||
if ( fp != nullptr )
|
||||
{
|
||||
file_janitor_t fpj(fp);
|
||||
if ( qfseek(fp, pl.map_ea(tmp.dllname), SEEK_SET) == 0 )
|
||||
{
|
||||
const size_t nbytes = replacement.size(); // we want to write the zero as well!
|
||||
if ( !args.dry_run )
|
||||
{
|
||||
if ( qfwrite(fp, replacement.c_str(), nbytes) == nbytes )
|
||||
{
|
||||
out_verb("File \"%s\" successfully patched (with \"%s\")\n", path, replacement.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
errbuf->sprnt("Couldn't write %" FMT_Z " bytes to \"%s\"", nbytes, path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out("Would write %" FMT_Z " bytes (\"%s\") to file\n",
|
||||
nbytes, replacement.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errbuf->sprnt("Cannot seek to position %u in \"%s\"", tmp.dllname, path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errbuf->sprnt("Couldn't open \"%s\" for writing", path);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -28,20 +28,19 @@ include ../../allmake.mak
|
||||
all: configs modules pyfiles deployed_modules idapython_modules api_contents pydoc_injections pyqt sip bins examples_index # public_tree test_idc docs
|
||||
|
||||
ifeq ($(OUT_OF_TREE_BUILD),)
|
||||
IDAPYSWITCH:=$(R)idapyswitch$(B)
|
||||
IDAPYSWITCH_DEP:=$(IDAPYSWITCH)
|
||||
IDAPYSWITCH_PATH:=$(IDAPYSWITCH)
|
||||
BINS += $(IDAPYSWITCH)
|
||||
else
|
||||
# when out-of-tree (i.e., from github), we only build idapyswitch64,
|
||||
# and rely on it even for the __EA32__ build
|
||||
ifdef __EA64__
|
||||
BINS += $(IDAPYSWITCH)
|
||||
ifdef __NT__
|
||||
IDAPYSWITCH_PATH:=$(IDA_INSTALL)/idapyswitch.exe
|
||||
else
|
||||
IDAPYSWITCH_64_HACK := 64
|
||||
IDAPYSWITCH_PATH:=$(IDA_INSTALL)/idapyswitch
|
||||
endif
|
||||
endif
|
||||
|
||||
IDAPYSWITCH:=$(R)idapyswitch$(IDAPYSWITCH_64_HACK)$(B)
|
||||
|
||||
BINS += $(IDAPYSWITCH)
|
||||
BINS += $(IDAPYSWITCH_DEP)
|
||||
bins: $(BINS)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
@@ -116,12 +115,12 @@ else
|
||||
# build-time, let's use our tool (which will in turn use patchelf)
|
||||
# to expand the DT_NEEDED 'slot' size.
|
||||
ifeq ($(PYTHON_VERSION_MAJOR),3)
|
||||
IDAPYSWITCH_MODULE_DEP := $(IDAPYSWITCH)
|
||||
IDAPYSWITCH_MODULE_DEP := $(IDAPYSWITCH_DEP)
|
||||
ifndef __CODE_CHECKER__
|
||||
# this is for idapython[64].so
|
||||
POSTACTION=$(Q)$(IDAPYSWITCH) --split-debug-and-expand-libpython3-dtneeded-room $(MODULE)
|
||||
POSTACTION=$(Q)$(IDAPYSWITCH_PATH) --split-debug-and-expand-libpython3-dtneeded-room $(MODULE)
|
||||
# and this for _ida_*.so
|
||||
POSTACTION_IDA_X_SO=$(Q)$(IDAPYSWITCH) --split-debug-and-expand-libpython3-dtneeded-room
|
||||
POSTACTION_IDA_X_SO=$(Q)$(IDAPYSWITCH_PATH) --split-debug-and-expand-libpython3-dtneeded-room
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -964,9 +963,9 @@ $(R)idapyswitch$(B): $(call dumb_target, pro, $(IDAPYSWITCH_OBJS))
|
||||
ifdef __APPLE_SILICON__
|
||||
tbd: $(TBD_MODULE_DEP)
|
||||
# copy the tbd library to idabin, and instruct idapyswitch to create the symlink to libpython
|
||||
$(TBD_MODULE_DEP): $(TBD_FILE) $(IDAPYSWITCH)
|
||||
$(TBD_MODULE_DEP): $(TBD_FILE) $(IDAPYSWITCH_DEP)
|
||||
$(Q)$(CP) $< $@
|
||||
cd $(R) && $(IDAPYSWITCH) $(TBD_IDAPYSWITCH_ARGS) --force-path $(shell $(PYTHON)-config --prefix)/Python
|
||||
cd $(R) && $(IDAPYSWITCH_PATH) $(TBD_IDAPYSWITCH_ARGS) --force-path $(shell $(PYTHON)-config --prefix)/Python
|
||||
else
|
||||
tbd: ;
|
||||
endif
|
||||
|
||||
Binary file not shown.
@@ -15057,7 +15057,7 @@ class abstract_graph_t(ida_gdl.gdl_graph_t)
|
||||
| create_tree_layout(self, *args) -> 'bool'
|
||||
| create_tree_layout(self) -> bool
|
||||
|
|
||||
| get_edge(self, *args) -> 'edge_info_t'
|
||||
| get_edge(self, *args) -> 'edge_info_t *'
|
||||
| get_edge(self, e) -> edge_info_t
|
||||
| @param e (C++: edge_t)
|
||||
|
|
||||
@@ -16146,7 +16146,7 @@ class mutable_graph_t(abstract_graph_t)
|
||||
| create_tree_layout(self, *args) -> 'bool'
|
||||
| create_tree_layout(self) -> bool
|
||||
|
|
||||
| get_edge(self, *args) -> 'edge_info_t'
|
||||
| get_edge(self, *args) -> 'edge_info_t *'
|
||||
| get_edge(self, e) -> edge_info_t
|
||||
| @param e (C++: edge_t)
|
||||
|
|
||||
|
||||
+5
-5
@@ -31,9 +31,9 @@
|
||||
%ignore abstract_graph_t::create_orthogonal_layout;
|
||||
%ignore abstract_graph_t::clone;
|
||||
%ignore abstract_graph_t::nrect;
|
||||
%rename (nrect) my_nrect;
|
||||
%rename (nrect) novirt_nrect;
|
||||
%ignore abstract_graph_t::get_edge;
|
||||
%rename (get_edge) my_get_edge;
|
||||
%rename (get_edge) novirt_get_edge;
|
||||
|
||||
%ignore edge_info_t::add_layout_point;
|
||||
%ignore edge_infos_wrapper_t::edge_infos_wrapper_t;
|
||||
@@ -69,11 +69,11 @@ public:
|
||||
|
||||
%extend abstract_graph_t {
|
||||
public:
|
||||
virtual edge_info_t my_get_edge(edge_t e)
|
||||
edge_info_t *novirt_get_edge(edge_t e)
|
||||
{
|
||||
return *($self->get_edge(e));
|
||||
return $self->get_edge(e);
|
||||
}
|
||||
virtual rect_t my_nrect(int n)
|
||||
rect_t novirt_nrect(int n)
|
||||
{
|
||||
return $self->nrect(n);
|
||||
}
|
||||
|
||||
+4
-4
@@ -398,10 +398,10 @@ SWIG_DECLARE_PY_CLINKED_OBJECT(textctrl_info_t)
|
||||
%newobject place_t::as_structplace_t;
|
||||
%newobject place_t::as_simpleline_place_t;
|
||||
%extend place_t {
|
||||
static idaplace_t *as_idaplace_t(place_t *p) { return (idaplace_t *) p->clone(); }
|
||||
static enumplace_t *as_enumplace_t(place_t *p) { return (enumplace_t *) p->clone(); }
|
||||
static structplace_t *as_structplace_t(place_t *p) { return (structplace_t *) p->clone(); }
|
||||
static simpleline_place_t *as_simpleline_place_t(place_t *p) { return (simpleline_place_t *) p->clone(); }
|
||||
static idaplace_t *as_idaplace_t(place_t *p) { return p != nullptr ? (idaplace_t *) p->clone() : nullptr; }
|
||||
static enumplace_t *as_enumplace_t(place_t *p) { return p != nullptr ? (enumplace_t *) p->clone() : nullptr; }
|
||||
static structplace_t *as_structplace_t(place_t *p) { return p != nullptr ? (structplace_t *) p->clone() : nullptr; }
|
||||
static simpleline_place_t *as_simpleline_place_t(place_t *p) { return p != nullptr ? (simpleline_place_t *) p->clone() : nullptr; }
|
||||
|
||||
PyObject *py_generate(void *ud, int maxsize)
|
||||
{
|
||||
|
||||
@@ -2,228 +2,75 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>IDAPython examples</title>
|
||||
<script type="text/javascript">
|
||||
collapse_normal = "data:image/png;base64,"
|
||||
+"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgI"
|
||||
+"fAhkiAAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAE9JREFUSIntz8EJwFAIA9AY"
|
||||
+"upiTtX8wwcm01/YW+JcWfEeJGIExxv+ZEoqIS8mRTHfP5+yQWpidSq6qAOB1"
|
||||
+"gMriDumD7l5KjmRutRljfNQNRgQNjM3h6lA=";
|
||||
collapse_hover = "data:image/png;base64,"
|
||||
+"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgI"
|
||||
+"fAhkiAAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAbVJREFUSInVlT1v01AUhp/3"
|
||||
+"ioUoEiOIAl2QWDpCB4qIbKfZGPgB/AXE1ERiyYSo+BcsLAxVw4avrEgoUkBM"
|
||||
+"UKEKsUbKxpjB9WGgRWnspLcuS9/x6Ph5fK4/Dlz2KLRxMBg0ms1mx8wi4BbQ"
|
||||
+"AKbAAbAfx/FhLcFoNLo6m81eAF3g2opWD3TjOP4aLPDer0naA+6fdSPHOZL0"
|
||||
+"Moqi3TMFWZbdMLMxcCcQ/i+SeicSV9XQ7/edmb2vAwcws1dpmm7Dkgm8988k"
|
||||
+"va0Dn8uhpI3KCSTtXBAOcK8oiiclQZZld4GN/yBA0tMri0Uz26yo7S7WlgAf"
|
||||
+"AVtzpc0qwU3p9KNJkqQXIvDe9yXNC9ZKR+ScsxBYYIrSBEVRTBYnyLKsG0Iz"
|
||||
+"s62F0qQkAD5XXPg6RFCRcemIkiT5ZWbfagJPRdJe5XfgnAt6a1bFzH4AHyoF"
|
||||
+"w+HwHfDpAvwj59zzKIrypT+7NE2vO+fGwHoNwU4cx29gyc8OoN1uT/M8fyjp"
|
||||
+"yznAuaTeCXylAKDT6UyAx/xdNr9X9Ur6KOnB/C6Ac6zM4822LSkys9uSGkVR"
|
||||
+"TJ1z351z+61W62co63LlD5ogjVIofsWl";
|
||||
expand_normal = "data:image/png;base64,"
|
||||
+"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgI"
|
||||
+"fAhkiAAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAKpJREFUSIntk9ERgjAMhr/0"
|
||||
+"HMANdARGIF3EUdRNXIQrbsAKbsAExBfg0FMsVR686/eUNvkvaZJCJvMtslQQ"
|
||||
+"QtgCRX9sVbWZi98kFFWYWejtK1DOBbuEBItYPUHUDKqqOo0CkT1wADCzG3AZ"
|
||||
+"fM65WlXrqTZqBiJyfHO/A0Zf13UADwlWb1HUC8zsPNifWvSsTfkH5XRNvffl"
|
||||
+"XPz/r2nKT25ERHu7/WUxmcxr7pBbLDAu4/t2";
|
||||
expand_hover = "data:image/png;base64,"
|
||||
+"iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgI"
|
||||
+"fAhkiAAAAAlwSFlzAAAWJQAAFiUBSVIk8AAAAgxJREFUSInVlbFrFFEQxn/z"
|
||||
+"2MbjQOQKLUwCYiUiiJgm4c59exErwVLwP7AREc/OrcSQUvAfsEgjiLnydnm5"
|
||||
+"5iBCKo0gokWQwHEHsbC44m7H5k73cruXjdjkg4XHzDfzzZt9bx6cdkhRYrPZ"
|
||||
+"LJXL5duq6gMXgRLQBfaALWvtl38S6HQ6ZwaDwSOgAZydQ42BhrV2t7BAu91e"
|
||||
+"GA6H70Xk+nGFjDFU1cdBELw6VsA5d0FVd4DFgsn/QESe+b6/DmCyCGEYGlV9"
|
||||
+"m5P8EIjG326GH1V9EUXRGuTswDn3QFXf5BS4ba31x7xbqupyeJ/7/f61zB2o"
|
||||
+"aiMn6CS4UqlU7s4IOOcuA1f/gwAics87alTV5Qzbeiro+2Q9Go32RSTtWwVW"
|
||||
+"UqHLM/8gjuMnIrKRtllrC13IOI5DEXmeMv2aaZExRoskK4hkpkVJkhyITBfs"
|
||||
+"nGuk/PtBEGwCtFqtRc/z7k98qrrCNA5mBIAPRw2q+nKyFpFtYBPA87xLaV9W"
|
||||
+"rpkWBUHwTVU/zgkqDBF5l3kPRGReVUWx1+v1mpmnIwxDU61W28BqhvuQvyPi"
|
||||
+"HHAjgzMSkTu+70e5xy+KovPGmB1g6YSVAzy11m5AzrADqNfr3fHFyRxoORgC"
|
||||
+"DyfJ5woA+L7/YyzSAH7O44pIS0RuWmtfT9mLljZ+2dZExFfVBREpJUnSNcZ8"
|
||||
+"MsZs1Wq1r0VznS78BtXqtYTFW0oe";
|
||||
|
||||
var name_expanded = null;
|
||||
|
||||
function is_expanded()
|
||||
{
|
||||
return name_expanded != null;
|
||||
}
|
||||
|
||||
function set_expanded(name)
|
||||
{
|
||||
name_expanded = name;
|
||||
}
|
||||
|
||||
function init()
|
||||
{
|
||||
set_all_images("c_expand_gadget", expand_normal);
|
||||
}
|
||||
|
||||
function image_with_hover(name)
|
||||
{
|
||||
set_image(name, name == name_expanded ? collapse_hover : expand_hover);
|
||||
}
|
||||
|
||||
function image_without_hover(name)
|
||||
{
|
||||
set_image(name, name == name_expanded ? collapse_normal : expand_normal);
|
||||
}
|
||||
|
||||
function expand_click(name)
|
||||
{
|
||||
if ( is_expanded() && name_expanded != name )
|
||||
{
|
||||
// something else is expanded - close it
|
||||
|
||||
expand_toggle(name_expanded);
|
||||
}
|
||||
|
||||
expand_toggle(name, true);
|
||||
}
|
||||
|
||||
function expand_toggle(name, cursor_is_there=false)
|
||||
{
|
||||
set_expanded(is_expanded() ? null : name);
|
||||
|
||||
if ( cursor_is_there )
|
||||
image_with_hover(name);
|
||||
else
|
||||
image_without_hover(name);
|
||||
|
||||
// actual expansion / collapse
|
||||
|
||||
var div = document.getElementById('DIV_' + name);
|
||||
div.style.display = is_expanded() ? 'initial' : 'none';
|
||||
|
||||
// scroll a little for divs at the bottom of the page
|
||||
|
||||
if ( is_expanded() )
|
||||
{
|
||||
var margin = 100;
|
||||
var rect = div.getBoundingClientRect();
|
||||
|
||||
var delta = rect.top + margin - window.innerHeight;
|
||||
//console.log('rect.top = ' + rect.top);
|
||||
//console.log('window.innerHeight = ' + window.innerHeight);
|
||||
//console.log('delta = ' + delta);
|
||||
if ( delta > 0 )
|
||||
window.scrollBy(0, delta);
|
||||
}
|
||||
}
|
||||
|
||||
function on_see_also(see_also)
|
||||
{
|
||||
if ( is_expanded() )
|
||||
{
|
||||
// likely it is, since I clicked on "see also";
|
||||
// close it
|
||||
|
||||
expand_toggle(name_expanded);
|
||||
}
|
||||
|
||||
expand_toggle(see_also);
|
||||
|
||||
document.getElementById('IMG_' + see_also).scrollIntoView();
|
||||
return true;
|
||||
}
|
||||
|
||||
function set_image(name, source)
|
||||
{
|
||||
document.getElementById('IMG_' + name).src = source;
|
||||
}
|
||||
|
||||
function set_all_images(classname, source)
|
||||
{
|
||||
var all = document.getElementsByClassName(classname);
|
||||
for ( var i in all )
|
||||
all[i].src = source;
|
||||
}
|
||||
|
||||
function gotoMD()
|
||||
{
|
||||
location.href = "https://github.com/idapython/src/blob/master/examples/index.md";
|
||||
}
|
||||
</script>
|
||||
<link rel="stylesheet" href="index.css">
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
</head>
|
||||
|
||||
<body onload="init();">
|
||||
<body>
|
||||
<div style="margin:20px 20px 50px 20px">
|
||||
<span style="border:solid black 1px;padding:10px;font-size:small;cursor:pointer" onclick="gotoMD();">
|
||||
Switch to MarkDown</span>
|
||||
<a href="http://htmlpreview.github.io/?https://github.com/idapython/src/blob/master/examples/index.html">View on GitHub</a>
|
||||
</div>
|
||||
<div class="toplevel-actions" onclick="handle_toplevel_action()">
|
||||
<a href="#" class="exp-col-all expand-all">Expand all</a>
|
||||
<a href="#" class="exp-col-all collapse-all">collapse all</a>
|
||||
</div>
|
||||
|
||||
<h1>IDAPython examples:</h1>
|
||||
<!--gen:group:category-->
|
||||
<h2>Category: <!--gen:category--></h2>
|
||||
<div class="c_list">
|
||||
<div class="c_list" onclick="handle_click()">
|
||||
<!--gen:block-->
|
||||
<a name="<!--gen:name-->"/>
|
||||
<div>
|
||||
<img id="IMG_<!--gen:name-->" class="c_expand_gadget"
|
||||
onmouseover="image_with_hover('<!--gen:name-->');"
|
||||
onmouseout="image_without_hover('<!--gen:name-->');"
|
||||
onclick="expand_click('<!--gen:name-->');"/>
|
||||
<!--gen:name-->: <i><!--gen:summary--></i>
|
||||
</div>
|
||||
<div id="DIV_<!--gen:name-->" style="display:none">
|
||||
<div class="example-entry collapsed-entry" name="<!--gen:name-->">
|
||||
<div>
|
||||
<span class="exp-col expander">▹</span>
|
||||
<span class="exp-col collapser" style="display:none">▿</span>
|
||||
<a href="<!--gen:path-->"><!--gen:name--></a>: <i><!--gen:summary--></i>
|
||||
</div>
|
||||
<div class="details" id="DIV_<!--gen:name-->">
|
||||
|
||||
<hr/>
|
||||
|
||||
<h2><!--gen:name--></h2>
|
||||
|
||||
<h3>Category</h3>
|
||||
<indent><!--gen:category--></indent>
|
||||
|
||||
<h3>Summary</h3>
|
||||
<indent><!--gen:summary--></indent>
|
||||
|
||||
<h3>Source code</h3>
|
||||
<indent><a target="_blank"
|
||||
href="https://github.com/idapython/src/blob/master/examples/<!--gen:path-->">
|
||||
Jump to GitHub</a></indent>
|
||||
|
||||
<h3>Description</h3>
|
||||
<indent>
|
||||
<pre>
|
||||
<!--gen:description-->
|
||||
</pre>
|
||||
</indent>
|
||||
|
||||
<!-- "Keywords" heading produced only if there is data for it -->
|
||||
|
||||
<!--gen:block-->
|
||||
<!--gen:first-->
|
||||
<h3>Keywords</h3>
|
||||
<!--gen:end-->
|
||||
<span><!--gen:keywords--></span>
|
||||
<!--gen:end-->
|
||||
|
||||
<h3>Uses</h3>
|
||||
<ul>
|
||||
<ul>
|
||||
<li>Category: <!--gen:category--></li>
|
||||
<li>Summary: <!--gen:summary--></li>
|
||||
<li>View on <a href="https://github.com/idapython/src/blob/master/examples/<!--gen:path-->">GitHub</a></li>
|
||||
<!-- only if there is data for it -->
|
||||
<li>Keywords:
|
||||
<!--gen:block-->
|
||||
<li><!--gen:uses--></li>
|
||||
<span>
|
||||
<!--gen:first-->
|
||||
<!--gen:end-->
|
||||
<!--gen:keywords-->
|
||||
</span>
|
||||
<!--gen:end-->
|
||||
</ul>
|
||||
|
||||
<!-- "See also" heading produced only if there is data for it -->
|
||||
|
||||
<!--gen:block-->
|
||||
<!--gen:first-->
|
||||
<h3>See also</h3>
|
||||
<ul>
|
||||
<!--gen:end-->
|
||||
<li><a href="#<!--gen:see_also-->"
|
||||
onclick="on_see_also('<!--gen:see_also-->');">
|
||||
<!--gen:see_also--></a></li>
|
||||
<!--gen:last-->
|
||||
</ul>
|
||||
<!--gen:end-->
|
||||
<!--gen:end-->
|
||||
|
||||
<hr/>
|
||||
<li>APIs used
|
||||
<ul>
|
||||
<!--gen:block-->
|
||||
<li><!--gen:uses--></li>
|
||||
<!--gen:end-->
|
||||
</ul>
|
||||
</li>
|
||||
<li>Summary: <!--gen:summary--></li>
|
||||
|
||||
<!-- only if there is data for it -->
|
||||
<!--gen:block-->
|
||||
<!--gen:first-->
|
||||
<li>See also:
|
||||
<ul>
|
||||
<!--gen:end-->
|
||||
<li><a href="#<!--gen:see_also-->"
|
||||
onclick="on_see_also('<!--gen:see_also-->');">
|
||||
<!--gen:see_also--></a></li>
|
||||
<!--gen:last-->
|
||||
</ul>
|
||||
<!--gen:end-->
|
||||
</li>
|
||||
<!--gen:end-->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end block (per example in this category) -->
|
||||
<!--gen:end-->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[Switch to HTML](http://htmlpreview.github.io/?https://github.com/idapython/src/blob/master/examples/index.html)
|
||||
[HTML version](http://htmlpreview.github.io/?https://github.com/idapython/src/blob/master/examples/index.html)
|
||||
|
||||
# IDAPython examples
|
||||
|
||||
|
||||
Reference in New Issue
Block a user