IDAPython 1.4.1:

- added AUTHORS.txt and changed the banner
- IDAPython_ExecFile() will print script execution errors to the log window too
- added 'ph' into idaapi. It is a replacement for idaapi.cvar.ph
- added runscript to init.py for backward compatibility
- added cli_t support
This commit is contained in:
elias.bachaalany 2010-07-16 12:07:49 +00:00
parent 8495e5205b
commit 6b0dfd84c0
10 changed files with 966 additions and 32 deletions

13
AUTHORS.txt Normal file
View File

@ -0,0 +1,13 @@
The IDAPython Team:
* Gergely Erdelyi - http://d-dome.net/idapython/
Original IDAPython author.
* Hex-Rays - http://www.hex-rays.com/
Hex-Rays joined the project in September 2009 and started contributing.
* Ero Carrera - http://dkbza.org/
Project contributor

View File

@ -36,7 +36,7 @@ else:
# IDAPython version
VERSION_MAJOR = 1
VERSION_MINOR = 4
VERSION_PATCH = 0
VERSION_PATCH = 1
# Determine Python version
PYTHON_MAJOR_VERSION = int(platform.python_version()[0])
@ -66,12 +66,15 @@ BINDIST_MANIFEST = [
"README.txt",
"COPYING.txt",
"CHANGES.txt",
"AUTHORS.txt",
"STATUS.txt",
"docs/notes.txt",
"examples/chooser.py",
"examples/colours.py",
"examples/debughook.py",
"examples/ex_cli.py",
"examples/ex1.idc",
"examples/ex_custdata.py",
"examples/ex1_idaapi.py",
"examples/ex1_idautils.py",
"examples/hotkey.py",

100
examples/ex_cli.py Normal file
View File

@ -0,0 +1,100 @@
# -----------------------------------------------------------------------
# This is an example illustrating how to implement a CLI
# (c) Hex-Rays
#
from idaapi import NW_OPENIDB, NW_CLOSEIDB, NW_TERMIDA, NW_REMOVE, COLSTR, cli_t
#<pycode(ex_cli_ex1)>
class mycli_t(cli_t):
flags = 0
sname = "pycli"
lname = "Python CLI"
hint = "pycli hint"
def OnExecuteLine(self, line):
"""
The user pressed Enter. The CLI is free to execute the line immediately or ask for more lines.
This callback is mandatory.
@param line: typed line(s)
@return Boolean: True-executed line, False-ask for more lines
"""
print "OnExecute:", line
return True
def OnKeydown(self, line, x, sellen, vkey, shift):
"""
A keyboard key has been pressed
This is a generic callback and the CLI is free to do whatever it wants.
This callback is optional.
@param line: current input line
@param x: current x coordinate of the cursor
@param sellen: current selection length (usually 0)
@param vkey: virtual key code. if the key has been handled, it should be returned as zero
@param shift: shift state
@return:
None - Nothing was changed
tuple(line, x, sellen, vkey): if either of the input line or the x coordinate or the selection length has been modified.
It is possible to return a tuple with None elements to preserve old values. Example: tuple(new_line, None, None, None) or tuple(new_line)
"""
print "Onkeydown: line=%s x=%d sellen=%d vkey=%d shift=%d" % (line, x, sellen, vkey, shift)
return None
def OnCompleteLine(self, prefix, n, line, prefix_start):
"""
The user pressed Tab. Find a completion number N for prefix PREFIX
This callback is optional.
@param prefix: Line prefix at x (string)
@param n: completion number (int)
@param line: the current line (string)
@param prefix_start: the index where PREFIX starts in LINE (int)
@return: None if no completion could be generated otherwise a String with the completion suggestion
"""
print "OnCompleteLine: prefix=%s n=%d line=%s prefix_start=%d" % (prefix, n, line, prefix_start)
return None
#</pycode(ex_cli_ex1)>
# -----------------------------------------------------------------------
def nw_handler(code, old=0):
if code == NW_OPENIDB:
print "nw_handler(): installing CLI"
mycli.register()
elif code == NW_CLOSEIDB:
print "nw_handler(): removing CLI"
mycli.unregister()
elif code == NW_TERMIDA:
print "nw_handler(): uninstalled nw handler"
idaapi.notify_when(NW_TERMIDA | NW_OPENIDB | NW_CLOSEIDB | NW_REMOVE, nw_handler)
# -----------------------------------------------------------------------
# Already installed?
try:
mycli
# remove previous CLI
mycli.unregister()
del mycli
# remove previous handler
nw_handler(NW_TERMIDA)
except:
pass
finally:
mycli = mycli_t()
# register CLI
if mycli.register():
print "CLI installed"
# install new handler
idaapi.notify_when(NW_TERMIDA | NW_OPENIDB | NW_CLOSEIDB, nw_handler)
else:
del mycli
print "Failed to install CLI"

216
examples/ex_custdata.py Normal file
View File

@ -0,0 +1,216 @@
# -----------------------------------------------------------------------
# This is an example illustrating how to use custom data types in Python
# (c) Hex-Rays
#
from idaapi import data_type_t, data_format_t, NW_OPENIDB, NW_CLOSEIDB, NW_TERMIDA, NW_REMOVE, COLSTR
import struct
import ctypes
import platform
#<pycode(ex_custdata)>
# -----------------------------------------------------------------------
class pascal_data_type(data_type_t):
def __init__(self):
data_type_t.__init__(self, name="py_pascal_string",
value_size = 2, menu_name = "Pascal string",
asm_keyword = "pstr")
def calc_item_size(self, ea, maxsize):
# Custom data types may be used in structure definitions. If this case
# ea is a member id. Check for this situation and return 1
if _idaapi.is_member_id(ea):
return 1
# get the length byte
n = _idaapi.get_byte(ea)
# string too big?
if n > maxsize:
return 0
# ok, accept the string
return n + 1
class pascal_data_format(data_format_t):
FORMAT_NAME = "py_pascal_string_pstr"
def __init__(self):
data_format_t.__init__(self, name=pascal_data_format.FORMAT_NAME)
def printf(self, value, current_ea, operand_num, dtid):
# Take the length byte
n = ord(value[0])
o = ['"']
for ch in value[1:]:
b = ord(ch)
if b < 0x20 or b > 128:
o.append(r'\x%02x' % ord(ch))
else:
o.append(ch)
o.append('"')
return "".join(o)
# -----------------------------------------------------------------------
class simplevm_data_type(data_type_t):
ASM_KEYWORD = "svm_emit"
def __init__(self):
data_type_t.__init__(self,
name="py_simple_vm",
value_size = 1,
menu_name = "SimpleVM",
asm_keyword = simplevm_data_type.ASM_KEYWORD)
def calc_item_size(self, ea, maxsize):
if _idaapi.is_member_id(ea):
return 1
# get the opcode and see if it has an imm
n = 5 if (_idaapi.get_byte(ea) & 3) == 0 else 1
# string too big?
if n > maxsize:
return 0
# ok, accept
return n
class simplevm_data_format(data_format_t):
def __init__(self):
data_format_t.__init__(self,
name="py_simple_vm_format",
menu_name = "SimpleVM")
# Some tables for the disassembler
INST = {1: 'add', 2: 'mul', 3: 'sub', 4: 'xor', 5: 'mov'}
REGS = {1: 'r1', 2: 'r2', 3: 'r3'}
def disasm(self, inst):
"""A simple local disassembler. In reality one can use a full-blown disassembler to render the text"""
opbyte = ord(inst[0])
op = opbyte >> 4
if not (1<=op<=5):
return None
r1 = (opbyte & 0xf) >> 2
r2 = opbyte & 3
sz = 0
if r2 == 0:
if len(inst) != 5:
return None
imm = struct.unpack_from('L', inst, 1)[0]
sz = 5
else:
imm = None
sz = 1
text = "%s %s, %s" % (
COLSTR(simplevm_data_format.INST[op], idaapi.SCOLOR_INSN),
COLSTR(simplevm_data_format.REGS[r1], idaapi.SCOLOR_REG),
COLSTR("0x%08X" % imm, idaapi.SCOLOR_NUMBER) if imm is not None else COLSTR(simplevm_data_format.REGS[r2], idaapi.SCOLOR_REG))
return (sz, text)
def printf(self, value, current_ea, operand_num, dtid):
r = self.disasm(value)
if not r:
return None
if dtid == 0:
return "%s(%s)" % (simplevm_data_type.ASM_KEYWORD, r[1])
return r[1]
# -----------------------------------------------------------------------
# This format will display DWORD values as MAKE_DWORD(0xHI, 0xLO)
class makedword_data_format(data_format_t):
def __init__(self):
data_format_t.__init__(self,
name="py_makedword",
value_size = 4,
menu_name = "Make DWORD")
def printf(self, value, current_ea, operand_num, dtid):
if len(value) != 4: return None
w1 = struct.unpack_from("H", value, 0)[0]
w2 = struct.unpack_from("H", value, 2)[0]
return "MAKE_DWORD(0x%04X, 0x%04X)" % (w2, w1)
# -----------------------------------------------------------------------
# This format will try to load a resource string given a number
# So instead of displaying:
# push 66h
# call message_box_from_rsrc_string
# It can be rendered as;
# push RSRC("The message")
# call message_box_from_rsrc_string
#
# The get_rsrc_string() is not optimal since it loads/unloads the
# DLL each time for a new string. It can be improved in many ways.
class rsrc_string_format(data_format_t):
def __init__(self):
data_format_t.__init__(self,
name="py_w32rsrcstring",
value_size = 1,
menu_name = "Resource string")
self.cache_node = idaapi.netnode("$ py_w32rsrcstring", 0, 1)
def get_rsrc_string(self, fn, id):
"""
Simple method that loads the input file as a DLL with LOAD_LIBRARY_AS_DATAFILE flag.
It then tries to LoadString()
"""
k32 = ctypes.windll.kernel32
u32 = ctypes.windll.user32
hinst = k32.LoadLibraryExA(fn, 0, 0x2)
if hinst == 0:
return ""
buf = ctypes.create_string_buffer(1024)
r = u32.LoadStringA(hinst, id, buf, 1024-1)
k32.FreeLibrary(hinst)
return buf.value if r else ""
def printf(self, value, current_ea, operand_num, dtid):
# Is it already cached?
val = self.cache_node.supval(current_ea)
# Not cached?
if val == None:
# Retrieve it
num = idaapi.struct_unpack(value)
val = self.get_rsrc_string(idaapi.get_input_file_path(), num)
# Cache it
self.cache_node.supset(current_ea, val)
# Failed to retrieve?
if val == "" or val == "\x00":
return None
# Return the format
return "RSRC_STR(\"%s\")" % COLSTR(val, idaapi.SCOLOR_IMPNAME)
# -----------------------------------------------------------------------
# Table of formats and types to be registered/unregistered
# If a tuple has one element then it is the format to be registered with dtid=0
# If the tuple has more than one element, the tuple[0] is the data type and tuple[1:] are the data formats
new_formats = [
(pascal_data_type(), pascal_data_format()),
(simplevm_data_type(), simplevm_data_format()),
(makedword_data_format(),),
(simplevm_data_format(),)
]
if platform.system() == 'Windows':
new_formats.append((rsrc_string_format(),))
#</pycode(ex_custdata)>
# -----------------------------------------------------------------------
def nw_handler(code, old=0):
# delete notifications
if code == NW_OPENIDB:
idaapi.register_data_types_and_formats(new_formats)
elif code == NW_CLOSEIDB:
idaapi.unregister_data_types_and_formats(new_formats)
elif code == NW_TERMIDA:
idaapi.notify_when(NW_TERMIDA | NW_OPENIDB | NW_CLOSEIDB | NW_REMOVE, nw_handler)
# -----------------------------------------------------------------------
# Check if already installed
if idaapi.find_custom_data_type(pascal_data_format.FORMAT_NAME) == -1:
if not idaapi.register_data_types_and_formats(new_formats):
print "Failed to register types!"
else:
idaapi.notify_when(NW_TERMIDA | NW_OPENIDB | NW_CLOSEIDB, nw_handler)
print "Formats installed!"
else:
print "Formats already installed!"

View File

@ -39,16 +39,27 @@ class IDAPythonStdOut:
def isatty(self):
return False
# -----------------------------------------------------------------------
def runscript(script):
"""
Executes a script.
This function is present for backward compatiblity. Please use idaapi.IDAPython_ExecScript() instead
@param script: script path
@return: Error string or None on success
"""
import idaapi
return idaapi.IDAPython_ExecScript(script, globals())
# -----------------------------------------------------------------------
def print_banner():
banner = [
"Python interpreter version %d.%d.%d %s (serial %d)" % sys.version_info,
"Copyright (c) 1990-2010 Python Software Foundation - http://www.python.org/",
"",
"IDAPython" + (" 64-bit" if __EA64__ else "") + " version %d.%d.%d %s (serial %d)" % IDAPYTHON_VERSION,
"Copyright (c) 2004-2010 Gergely Erdelyi - http://code.google.com/p/idapython/"
"Python %d.%d.%d %s (serial %d) (c) 1990-2010 Python Software Foundation" % sys.version_info,
"IDAPython" + (" 64-bit" if __EA64__ else "") + " v%d.%d.%d %s (serial %d) (c) The IDAPython Team <idapython@googlegroups.com>" % IDAPYTHON_VERSION
]
sepline = '-' * max([len(s) for s in banner])
sepline = '-' * (max([len(s) for s in banner])+1)
print sepline
print "\n".join(banner)

View File

@ -726,7 +726,7 @@ void pyg_select_node(PyObject *self, int nid)
}
//</code(py_graph)>
%}
#endif
#endif // __NT__
#ifdef __NT__
%inline %{
@ -739,9 +739,8 @@ void pyg_select_node(PyObject *self, int nid);
bool pyg_show(PyObject *self);
//</inline(py_graph)>
%}
#endif
#endif // __NT__
#ifdef __NT__
%pythoncode %{
#<pycode(py_graph)>
class GraphViewer:
@ -907,4 +906,4 @@ class GraphViewer:
#</pydoc>
#</pycode(py_graph)>
%}
#endif

View File

@ -1919,6 +1919,7 @@ def IDAPython_ExecScript(script, g):
execfile(script, g)
except Exception, e:
PY_COMPILE_ERR = str(e) + "\n" + traceback.format_exc()
print PY_COMPILE_ERR
finally:
# Restore the globals to the state before the script was run
g['__file__'] = old__file__
@ -2132,7 +2133,5 @@ static bool notify_when(int when, PyObject *py_callable)
%include "typeinf.i"
%include "ua.i"
%include "xref.i"
#ifdef __NT__
%include "graph.i"
#endif
%include "graph.i"
%include "fpro.i"

View File

@ -143,21 +143,6 @@ static PyObject *AssembleLine(ea_t ea, ea_t cs, ea_t ip, bool use32, const char
Py_RETURN_NONE;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_tbyte_size():
"""
Returns the 'ph.tbyte_size' field as defined in he processor module
"""
pass
#</pydoc>
*/
static size_t ph_get_tbyte_size()
{
return ph.tbyte_size;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
@ -173,6 +158,216 @@ static size_t ph_get_id()
return ph.id;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_version():
"""
Returns the 'ph.version'
"""
pass
#</pydoc>
*/
static size_t ph_get_version()
{
return ph.version;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_flag():
"""
Returns the 'ph.flag'
"""
pass
#</pydoc>
*/
static size_t ph_get_flag()
{
return ph.flag;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_cnbits():
"""
Returns the 'ph.cnbits'
"""
pass
#</pydoc>
*/
static size_t ph_get_cnbits()
{
return ph.cnbits;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_dnbits():
"""
Returns the 'ph.dnbits'
"""
pass
#</pydoc>
*/
static size_t ph_get_dnbits()
{
return ph.dnbits;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_regFirstSreg():
"""
Returns the 'ph.regFirstSreg'
"""
pass
#</pydoc>
*/
static size_t ph_get_regFirstSreg()
{
return ph.regFirstSreg;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_regLastSreg():
"""
Returns the 'ph.regLastSreg'
"""
pass
#</pydoc>
*/
static size_t ph_get_regLastSreg()
{
return ph.regLastSreg;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_segreg_size():
"""
Returns the 'ph.segreg_size'
"""
pass
#</pydoc>
*/
static size_t ph_get_segreg_size()
{
return ph.segreg_size;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_regCodeSreg():
"""
Returns the 'ph.regCodeSreg'
"""
pass
#</pydoc>
*/
static size_t ph_get_regCodeSreg()
{
return ph.regCodeSreg;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_regDataSreg():
"""
Returns the 'ph.regDataSreg'
"""
pass
#</pydoc>
*/
static size_t ph_get_regDataSreg()
{
return ph.regDataSreg;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_high_fixup_bits():
"""
Returns the 'ph.high_fixup_bits'
"""
pass
#</pydoc>
*/
static size_t ph_get_high_fixup_bits()
{
return ph.high_fixup_bits;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_icode_return():
"""
Returns the 'ph.icode_return'
"""
pass
#</pydoc>
*/
static size_t ph_get_icode_return()
{
return ph.icode_return;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_instruc_start():
"""
Returns the 'ph.instruc_start'
"""
pass
#</pydoc>
*/
static size_t ph_get_instruc_start()
{
return ph.instruc_start;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_instruc_end():
"""
Returns the 'ph.instruc_end'
"""
pass
#</pydoc>
*/
static size_t ph_get_instruc_end()
{
return ph.instruc_end;
}
//-------------------------------------------------------------------------
/*
#<pydoc>
def ph_get_tbyte_size():
"""
Returns the 'ph.tbyte_size' field as defined in he processor module
"""
pass
#</pydoc>
*/
static size_t ph_get_tbyte_size()
{
return ph.tbyte_size;
}
//-------------------------------------------------------------------------
/*
#<pydoc>

View File

@ -22,8 +22,12 @@
%ignore skipSpaces;
%ignore stristr;
// Ignore the cli_t class
// CLI
%ignore cli_t;
%ignore install_command_interpreter;
%rename (install_command_interpreter) py_install_command_interpreter;
%ignore remove_command_interpreter;
%rename (remove_command_interpreter) py_remove_command_interpreter;
%include "typemaps.i"
@ -755,6 +759,262 @@ PyObject *choose2_find(const char *title)
#ifdef __NT__
%{
//<code(py_cli)>
//--------------------------------------------------------------------------
#define MAX_PY_CLI 12
// Callbacks table
// This structure was devised because the cli callbacks have no user-data parameter
struct py_cli_cbs_t
{
bool (idaapi *execute_line)(const char *line);
bool (idaapi *complete_line)(
qstring *completion,
const char *prefix,
int n,
const char *line,
int x);
bool (idaapi *keydown)(
qstring *line,
int *p_x,
int *p_sellen,
uint16 *vk_key,
int shift);
};
// CLI Python wrapper class
class py_cli_t
{
private:
//--------------------------------------------------------------------------
cli_t cli;
PyObject *self;
qstring cli_sname, cli_lname, cli_hint;
//--------------------------------------------------------------------------
static py_cli_t *py_clis[MAX_PY_CLI];
static const py_cli_cbs_t py_cli_cbs[MAX_PY_CLI];
//--------------------------------------------------------------------------
#define IMPL_PY_CLI_CB(CBN) \
static bool idaapi s_keydown##CBN(qstring *line, int *p_x, int *p_sellen, uint16 *vk_key, int shift) \
{ \
return py_clis[CBN]->on_keydown(line, p_x, p_sellen, vk_key, shift); \
} \
static bool idaapi s_execute_line##CBN(const char *line) \
{ \
return py_clis[CBN]->on_execute_line(line); \
} \
static bool idaapi s_complete_line##CBN(qstring *completion, const char *prefix, int n, const char *line, int x) \
{ \
return py_clis[CBN]->on_complete_line(completion, prefix, n, line, x); \
}
IMPL_PY_CLI_CB(0); IMPL_PY_CLI_CB(1); IMPL_PY_CLI_CB(2); IMPL_PY_CLI_CB(3);
IMPL_PY_CLI_CB(4); IMPL_PY_CLI_CB(5); IMPL_PY_CLI_CB(6); IMPL_PY_CLI_CB(7);
IMPL_PY_CLI_CB(8); IMPL_PY_CLI_CB(9); IMPL_PY_CLI_CB(10); IMPL_PY_CLI_CB(11);
#undef IMPL_PY_CLI_CB
//--------------------------------------------------------------------------
// callback: the user pressed Enter
// CLI is free to execute the line immediately or ask for more lines
// Returns: true-executed line, false-ask for more lines
bool on_execute_line(const char *line)
{
PyObject *result = PyObject_CallMethod(self, (char *)S_ON_EXECUTE_LINE, "s", line);
bool ok = result != NULL && PyObject_IsTrue(result);
PyW_ShowErr(S_ON_EXECUTE_LINE);
Py_XDECREF(result);
return ok;
}
//--------------------------------------------------------------------------
// callback: a keyboard key has been pressed
// This is a generic callback and the CLI is free to do whatever
// it wants.
// line - current input line (in/out argument)
// p_x - pointer to current x coordinate of the cursor (in/out)
// p_sellen - pointer to current selection length (usually 0)
// p_vk_key - pointer to virtual key code (in/out)
// if the key has been handled, it should be reset to 0 by CLI
// shift - shift state
// Returns: true-modified input line or x coordinate or selection length
// This callback is optional
bool on_keydown(
qstring *line,
int *p_x,
int *p_sellen,
uint16 *vk_key,
int shift)
{
PyObject *result = PyObject_CallMethod(
self,
(char *)S_ON_KEYDOWN,
"siiHi",
line->c_str(),
*p_x,
*p_sellen,
*vk_key,
shift);
bool ok = result != NULL && PyTuple_Check(result);
PyW_ShowErr(S_ON_KEYDOWN);
if ( ok )
{
Py_ssize_t sz = PyTuple_Size(result);
PyObject *item;
if ( sz > 0 && (item = PyTuple_GetItem(result, 0)) != NULL && PyString_Check(item) )
*line = PyString_AsString(item);
if ( sz > 1 && (item = PyTuple_GetItem(result, 1)) != NULL && PyInt_Check(item) )
*p_x = PyInt_AsLong(item);
if ( sz > 2 && (item = PyTuple_GetItem(result, 2)) != NULL && PyInt_Check(item) )
*p_sellen = PyInt_AsLong(item);
if ( sz > 3 && (item = PyTuple_GetItem(result, 3)) != NULL && PyInt_Check(item) )
*vk_key = PyInt_AsLong(item) & 0xffff;
}
Py_XDECREF(result);
return ok;
}
// callback: the user pressed Tab
// Find a completion number N for prefix PREFIX
// LINE is given as context information. X is the index where PREFIX starts in LINE
// New prefix should be stored in PREFIX.
// Returns: true if generated a new completion
// This callback is optional
bool on_complete_line(
qstring *completion,
const char *prefix,
int n,
const char *line,
int x)
{
PyObject *result = PyObject_CallMethod(self, (char *)S_ON_COMPLETE_LINE, "sisi", prefix, n, line, x);
bool ok = result != NULL && PyString_Check(result);
PyW_ShowErr(S_ON_COMPLETE_LINE);
if ( ok )
*completion = PyString_AsString(result);
Py_XDECREF(result);
return ok;
}
// Private ctor (use bind())
py_cli_t()
{
}
public:
//---------------------------------------------------------------------------
static int bind(PyObject *py_obj)
{
int cli_idx;
// Find an empty slot
for ( cli_idx = 0; cli_idx < MAX_PY_CLI; ++cli_idx )
{
if ( py_clis[cli_idx] == NULL )
break;
}
py_cli_t *py_cli = NULL;
do
{
// No free slots?
if ( cli_idx >= MAX_PY_CLI )
break;
// Create a new instance
py_cli = new py_cli_t();
PyObject *attr;
// Start populating the 'cli' member
py_cli->cli.size = sizeof(cli_t);
// Store 'flags'
if ( (attr = PyW_TryGetAttrString(py_obj, S_FLAGS)) == NULL )
{
py_cli->cli.flags = 0;
}
else
{
py_cli->cli.flags = PyLong_AsLong(attr);
Py_DECREF(attr);
}
// Store 'sname'
if ( !PyW_GetStringAttr(py_obj, "sname", &py_cli->cli_sname) )
break;
py_cli->cli.sname = py_cli->cli_sname.c_str();
// Store 'lname'
if ( !PyW_GetStringAttr(py_obj, "lname", &py_cli->cli_lname) )
break;
py_cli->cli.lname = py_cli->cli_lname.c_str();
// Store 'hint'
if ( !PyW_GetStringAttr(py_obj, "hint", &py_cli->cli_hint) )
break;
py_cli->cli.hint = py_cli->cli_hint.c_str();
// Store callbacks
if ( !PyObject_HasAttrString(py_obj, S_ON_EXECUTE_LINE) )
break;
py_cli->cli.execute_line = py_cli_cbs[cli_idx].execute_line;
py_cli->cli.complete_line = PyObject_HasAttrString(py_obj, S_ON_COMPLETE_LINE) ? py_cli_cbs[cli_idx].complete_line : NULL;
py_cli->cli.keydown = PyObject_HasAttrString(py_obj, S_ON_KEYDOWN) ? py_cli_cbs[cli_idx].keydown : NULL;
// install CLI
install_command_interpreter(&py_cli->cli);
// Take reference to this object
py_cli->self = py_obj;
Py_INCREF(py_obj);
// Save the instance
py_clis[cli_idx] = py_cli;
return cli_idx;
} while (false);
delete py_cli;
return -1;
}
//---------------------------------------------------------------------------
static void unbind(int cli_idx)
{
// Out of bounds or not set?
if ( cli_idx < 0 || cli_idx >= MAX_PY_CLI || py_clis[cli_idx] == NULL )
return;
py_cli_t *py_cli = py_clis[cli_idx];
remove_command_interpreter(&py_cli->cli);
Py_DECREF(py_cli->self);
delete py_cli;
py_clis[cli_idx] = NULL;
return;
}
};
py_cli_t *py_cli_t::py_clis[MAX_PY_CLI] = {NULL};
#define DECL_PY_CLI_CB(CBN) { s_execute_line##CBN, s_complete_line##CBN, s_keydown##CBN }
const py_cli_cbs_t py_cli_t::py_cli_cbs[MAX_PY_CLI] =
{
DECL_PY_CLI_CB(0), DECL_PY_CLI_CB(1), DECL_PY_CLI_CB(2), DECL_PY_CLI_CB(3),
DECL_PY_CLI_CB(4), DECL_PY_CLI_CB(5), DECL_PY_CLI_CB(6), DECL_PY_CLI_CB(7),
DECL_PY_CLI_CB(8), DECL_PY_CLI_CB(9), DECL_PY_CLI_CB(10), DECL_PY_CLI_CB(11)
};
#undef DECL_PY_CLI_CB
//</code(py_cli)>
//<code(py_custviewer)>
//---------------------------------------------------------------------------
// Base class for all custviewer place_t providers
@ -1619,6 +1879,18 @@ public:
#ifdef __NT__
%inline %{
//<inline(py_cli)>
static int py_install_command_interpreter(PyObject *py_obj)
{
return py_cli_t::bind(py_obj);
}
static void py_remove_command_interpreter(int cli_idx)
{
py_cli_t::unbind(cli_idx);
}
//</inline(py_cli)>
//<inline(py_custviewer)>
//
// Pywraps Simple Custom Viewer functions
@ -2348,8 +2620,112 @@ class Choose2(object):
#</pycode(py_kernwin)>
%}
#ifdef __NT__
%pythoncode %{
#<pycode(py_cli)>
class cli_t(pyidc_opaque_object_t):
"""
cli_t wrapper class.
This class allows you to implement your own command line interface handlers.
"""
def __init__(self):
self.__cli_idx = -1
self.__clink__ = None
def register(self, flags = 0, sname = None, lname = None, hint = None):
"""
Registers the CLI.
@param flags: Feature bits. No bits are defined yet, must be 0
@param sname: Short name (displayed on the button)
@param lname: Long name (displayed in the menu)
@param hint: Hint for the input line
@return Boolean: True-Success, False-Failed
"""
# Already registered?
if self.__cli_idx >= 0:
return True
if sname is not None: self.sname = sname
if lname is not None: self.lname = lname
if hint is not None: self.hint = hint
# Register
self.__cli_idx = _idaapi.install_command_interpreter(self)
return False if self.__cli_idx < 0 else True
def unregister(self):
"""
Unregisters the CLI (if it was registered)
"""
if self.__cli_idx < 0:
return False
_idaapi.remove_command_interpreter(self.__cli_idx)
self.__cli_idx = -1
return True
def __del__(self):
self.unregister()
#
# Implement these methods in the subclass:
#
#<pydoc>
# def OnExecuteLine(self, line):
# """
# The user pressed Enter. The CLI is free to execute the line immediately or ask for more lines.
#
# This callback is mandatory.
#
# @param line: typed line(s)
# @return Boolean: True-executed line, False-ask for more lines
# """
# return True
#
# def OnKeydown(self, line, x, sellen, vkey, shift):
# """
# A keyboard key has been pressed
# This is a generic callback and the CLI is free to do whatever it wants.
#
# This callback is optional.
#
# @param line: current input line
# @param x: current x coordinate of the cursor
# @param sellen: current selection length (usually 0)
# @param vkey: virtual key code. if the key has been handled, it should be returned as zero
# @param shift: shift state
#
# @return:
# None - Nothing was changed
# tuple(line, x, sellen, vkey): if either of the input line or the x coordinate or the selection length has been modified.
# It is possible to return a tuple with None elements to preserve old values. Example: tuple(new_line, None, None, None) or tuple(new_line)
# """
# return None
#
# def OnCompleteLine(self, prefix, n, line, prefix_start):
# """
# The user pressed Tab. Find a completion number N for prefix PREFIX
#
# This callback is optional.
#
# @param prefix: Line prefix at x (string)
# @param n: completion number (int)
# @param line: the current line (string)
# @param prefix_start: the index where PREFIX starts in LINE (int)
#
# @return: None if no completion could be generated otherwise a String with the completion suggestion
# """
# return None
#</pydoc>
#</pycode(py_cli)>
#<pycode(py_custviewer)>
class simplecustviewer_t(object):
"""The base class for implementing simple custom viewers"""
@ -2586,4 +2962,3 @@ class simplecustviewer_t(object):
#</pydoc>
#</pycode(py_custviewer)>
%}
#endif

View File

@ -1347,5 +1347,28 @@ class processor_t(pyidc_opaque_object_t):
"""This function returns cmd.auxpref value"""
return self.cmd.auxpref
# ----------------------------------------------------------------------
class __ph(object):
id = property(lambda self: ph_get_id())
cnbits = property(lambda self: ph_get_cnbits())
dnbits = property(lambda self: ph_get_dnbits())
flag = property(lambda self: ph_get_flag())
high_fixup_bits = property(lambda self: ph_get_high_fixup_bits())
icode_return = property(lambda self: ph_get_icode_return())
instruc = property(lambda self: ph_get_instruc())
instruc_end = property(lambda self: ph_get_instruc_end())
instruc_start = property(lambda self: ph_get_instruc_start())
regCodeSreg = property(lambda self: ph_get_regCodeSreg())
regDataSreg = property(lambda self: ph_get_regDataSreg())
regFirstSreg = property(lambda self: ph_get_regFirstSreg())
regLastSreg = property(lambda self: ph_get_regLastSreg())
regnames = property(lambda self: ph_get_regnames())
segreg_size = property(lambda self: ph_get_segreg_size())
tbyte_size = property(lambda self: ph_get_tbyte_size())
version = property(lambda self: ph_get_version())
ph = __ph()
#</pycode(py_ua)>
%}